| author | |
| committer | |
| log | 0e3930bb5a92a60de6faad4c348f062a7a420eca |
| tree | 7a750af5bb8907f281945e794c7bdce3bd8f561e |
| parent | 5622f9382c4f54d6d2cc267590dda5352718ef60 |
| parent | 96fe49ef620cfd8423e878262420f58d5983d58c |
| signature |
x/os, x/net: layout tcp, ipv4/ipv6, and socket abstractions13 files changed, 1163 insertions(+), 115 deletions(-)
lib/std/builtin.zig+1-1| ... | @@ -165,7 +165,7 @@ pub const CallingConvention = enum { | ... | @@ -165,7 +165,7 @@ pub const CallingConvention = enum { |
| 165 | APCS, | 165 | APCS, |
| 166 | AAPCS, | 166 | AAPCS, |
| 167 | AAPCSVFP, | 167 | AAPCSVFP, |
| 168 | SysV | 168 | SysV, |
| 169 | }; | 169 | }; |
| 170 | 170 | ||
| 171 | /// This data structure is used by the Zig language code generation and | 171 | /// This data structure is used by the Zig language code generation and |
lib/std/compress/deflate.zig+2-4| ... | @@ -662,14 +662,12 @@ test "lengths overflow" { | ... | @@ -662,14 +662,12 @@ test "lengths overflow" { |
| 662 | // malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316) | 662 | // malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316) |
| 663 | // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6 | 663 | // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6 |
| 664 | // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11 | 664 | // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11 |
| 665 | const stream = [_]u8{ | 665 | const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 }; |
| 666 | 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 | ||
| 667 | }; | ||
| 668 | 666 | ||
| 669 | const reader = std.io.fixedBufferStream(&stream).reader(); | 667 | const reader = std.io.fixedBufferStream(&stream).reader(); |
| 670 | var window: [0x8000]u8 = undefined; | 668 | var window: [0x8000]u8 = undefined; |
| 671 | var inflate = inflateStream(reader, &window); | 669 | var inflate = inflateStream(reader, &window); |
| 672 | 670 | ||
| 673 | var buf: [1]u8 = undefined; | 671 | var buf: [1]u8 = undefined; |
| 674 | std.testing.expectError(error.InvalidLength, inflate.read(&buf)); | 672 | std.testing.expectError(error.InvalidLength, inflate.read(&buf)); |
| 675 | } | 673 | } |
lib/std/os/bits/darwin.zig+7| ... | @@ -832,6 +832,13 @@ pub const SO_RCVTIMEO = 0x1006; | ... | @@ -832,6 +832,13 @@ pub const SO_RCVTIMEO = 0x1006; |
| 832 | pub const SO_ERROR = 0x1007; | 832 | pub const SO_ERROR = 0x1007; |
| 833 | pub const SO_TYPE = 0x1008; | 833 | pub const SO_TYPE = 0x1008; |
| 834 | 834 | ||
| 835 | pub const SO_NREAD = 0x1020; | ||
| 836 | pub const SO_NKE = 0x1021; | ||
| 837 | pub const SO_NOSIGPIPE = 0x1022; | ||
| 838 | pub const SO_NOADDRERR = 0x1023; | ||
| 839 | pub const SO_NWRITE = 0x1024; | ||
| 840 | pub const SO_REUSESHAREUID = 0x1025; | ||
| 841 | |||
| 835 | fn wstatus(x: u32) u32 { | 842 | fn wstatus(x: u32) u32 { |
| 836 | return x & 0o177; | 843 | return x & 0o177; |
| 837 | } | 844 | } |
lib/std/os/bits/linux.zig+35-4| ... | @@ -32,6 +32,7 @@ pub usingnamespace @import("linux/prctl.zig"); | ... | @@ -32,6 +32,7 @@ pub usingnamespace @import("linux/prctl.zig"); |
| 32 | pub usingnamespace @import("linux/securebits.zig"); | 32 | pub usingnamespace @import("linux/securebits.zig"); |
| 33 | 33 | ||
| 34 | const is_mips = builtin.arch.isMIPS(); | 34 | const is_mips = builtin.arch.isMIPS(); |
| 35 | const is_ppc = builtin.arch.isPPC(); | ||
| 35 | const is_ppc64 = builtin.arch.isPPC64(); | 36 | const is_ppc64 = builtin.arch.isPPC64(); |
| 36 | const is_sparc = builtin.arch.isSPARC(); | 37 | const is_sparc = builtin.arch.isSPARC(); |
| 37 | 38 | ||
| ... | @@ -458,7 +459,39 @@ pub const AF_QIPCRTR = PF_QIPCRTR; | ... | @@ -458,7 +459,39 @@ pub const AF_QIPCRTR = PF_QIPCRTR; |
| 458 | pub const AF_SMC = PF_SMC; | 459 | pub const AF_SMC = PF_SMC; |
| 459 | pub const AF_MAX = PF_MAX; | 460 | pub const AF_MAX = PF_MAX; |
| 460 | 461 | ||
| 461 | pub usingnamespace if (!is_mips) | 462 | pub usingnamespace if (is_mips) |
| 463 | struct {} | ||
| 464 | else if (is_ppc or is_ppc64) | ||
| 465 | struct { | ||
| 466 | pub const SO_DEBUG = 1; | ||
| 467 | pub const SO_REUSEADDR = 2; | ||
| 468 | pub const SO_TYPE = 3; | ||
| 469 | pub const SO_ERROR = 4; | ||
| 470 | pub const SO_DONTROUTE = 5; | ||
| 471 | pub const SO_BROADCAST = 6; | ||
| 472 | pub const SO_SNDBUF = 7; | ||
| 473 | pub const SO_RCVBUF = 8; | ||
| 474 | pub const SO_KEEPALIVE = 9; | ||
| 475 | pub const SO_OOBINLINE = 10; | ||
| 476 | pub const SO_NO_CHECK = 11; | ||
| 477 | pub const SO_PRIORITY = 12; | ||
| 478 | pub const SO_LINGER = 13; | ||
| 479 | pub const SO_BSDCOMPAT = 14; | ||
| 480 | pub const SO_REUSEPORT = 15; | ||
| 481 | pub const SO_RCVLOWAT = 16; | ||
| 482 | pub const SO_SNDLOWAT = 17; | ||
| 483 | pub const SO_RCVTIMEO = 18; | ||
| 484 | pub const SO_SNDTIMEO = 19; | ||
| 485 | pub const SO_PASSCRED = 20; | ||
| 486 | pub const SO_PEERCRED = 21; | ||
| 487 | pub const SO_ACCEPTCONN = 30; | ||
| 488 | pub const SO_PEERSEC = 31; | ||
| 489 | pub const SO_SNDBUFFORCE = 32; | ||
| 490 | pub const SO_RCVBUFFORCE = 33; | ||
| 491 | pub const SO_PROTOCOL = 38; | ||
| 492 | pub const SO_DOMAIN = 39; | ||
| 493 | } | ||
| 494 | else | ||
| 462 | struct { | 495 | struct { |
| 463 | pub const SO_DEBUG = 1; | 496 | pub const SO_DEBUG = 1; |
| 464 | pub const SO_REUSEADDR = 2; | 497 | pub const SO_REUSEADDR = 2; |
| ... | @@ -487,9 +520,7 @@ pub usingnamespace if (!is_mips) | ... | @@ -487,9 +520,7 @@ pub usingnamespace if (!is_mips) |
| 487 | pub const SO_RCVBUFFORCE = 33; | 520 | pub const SO_RCVBUFFORCE = 33; |
| 488 | pub const SO_PROTOCOL = 38; | 521 | pub const SO_PROTOCOL = 38; |
| 489 | pub const SO_DOMAIN = 39; | 522 | pub const SO_DOMAIN = 39; |
| 490 | } | 523 | }; |
| 491 | else | ||
| 492 | struct {}; | ||
| 493 | 524 | ||
| 494 | pub const SO_SECURITY_AUTHENTICATION = 22; | 525 | pub const SO_SECURITY_AUTHENTICATION = 22; |
| 495 | pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; | 526 | pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; |
lib/std/os/bits/linux/arm64.zig+1| ... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
| 9 | const std = @import("../../../std.zig"); | 9 | const std = @import("../../../std.zig"); |
| 10 | const linux = std.os.linux; | 10 | const linux = std.os.linux; |
| 11 | const socklen_t = linux.socklen_t; | 11 | const socklen_t = linux.socklen_t; |
| 12 | const sockaddr = linux.sockaddr; | ||
| 12 | const iovec = linux.iovec; | 13 | const iovec = linux.iovec; |
| 13 | const iovec_const = linux.iovec_const; | 14 | const iovec_const = linux.iovec_const; |
| 14 | const uid_t = linux.uid_t; | 15 | const uid_t = linux.uid_t; |
lib/std/os/bits/linux/riscv64.zig+5| ... | @@ -376,6 +376,11 @@ pub const timespec = extern struct { | ... | @@ -376,6 +376,11 @@ pub const timespec = extern struct { |
| 376 | tv_nsec: isize, | 376 | tv_nsec: isize, |
| 377 | }; | 377 | }; |
| 378 | 378 | ||
| 379 | pub const timeval = extern struct { | ||
| 380 | tv_sec: time_t, | ||
| 381 | tv_usec: i64, | ||
| 382 | }; | ||
| 383 | |||
| 379 | pub const Flock = extern struct { | 384 | pub const Flock = extern struct { |
| 380 | l_type: i16, | 385 | l_type: i16, |
| 381 | l_whence: i16, | 386 | l_whence: i16, |
lib/std/os/bits/windows.zig+5| ... | @@ -22,6 +22,11 @@ pub const timespec = extern struct { | ... | @@ -22,6 +22,11 @@ pub const timespec = extern struct { |
| 22 | tv_nsec: c_long, | 22 | tv_nsec: c_long, |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | pub const timeval = extern struct { | ||
| 26 | tv_sec: c_long, | ||
| 27 | tv_usec: c_long, | ||
| 28 | }; | ||
| 29 | |||
| 25 | pub const sig_atomic_t = c_int; | 30 | pub const sig_atomic_t = c_int; |
| 26 | 31 | ||
| 27 | /// maximum signal number + 1 | 32 | /// maximum signal number + 1 |
lib/std/x.zig+23-1| ... | @@ -1 +1,23 @@ | ... | @@ -1 +1,23 @@ |
| 1 | pub const os = @import("x/os/os.zig"); | 1 | // SPDX-License-Identifier: MIT |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 7 | const std = @import("std.zig"); | ||
| 8 | |||
| 9 | pub const os = struct { | ||
| 10 | pub const Socket = @import("x/os/Socket.zig"); | ||
| 11 | pub usingnamespace @import("x/os/net.zig"); | ||
| 12 | }; | ||
| 13 | |||
| 14 | pub const net = struct { | ||
| 15 | pub const ip = @import("x/net/ip.zig"); | ||
| 16 | pub const tcp = @import("x/net/tcp.zig"); | ||
| 17 | }; | ||
| 18 | |||
| 19 | test { | ||
| 20 | inline for (.{ os, net }) |module| { | ||
| 21 | std.testing.refAllDecls(module); | ||
| 22 | } | ||
| 23 | } |
lib/std/x/net/ip.zig created+59| ... | @@ -0,0 +1,59 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 7 | const std = @import("../../std.zig"); | ||
| 8 | |||
| 9 | const IPv4 = std.x.os.IPv4; | ||
| 10 | const IPv6 = std.x.os.IPv6; | ||
| 11 | const Socket = std.x.os.Socket; | ||
| 12 | |||
| 13 | /// A generic IP abstraction. | ||
| 14 | const ip = @This(); | ||
| 15 | |||
| 16 | /// A union of all eligible types of IP addresses. | ||
| 17 | pub const Address = union(enum) { | ||
| 18 | ipv4: IPv4.Address, | ||
| 19 | ipv6: IPv6.Address, | ||
| 20 | |||
| 21 | /// Instantiate a new address with a IPv4 host and port. | ||
| 22 | pub fn initIPv4(host: IPv4, port: u16) Address { | ||
| 23 | return .{ .ipv4 = .{ .host = host, .port = port } }; | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Instantiate a new address with a IPv6 host and port. | ||
| 27 | pub fn initIPv6(host: IPv6, port: u16) Address { | ||
| 28 | return .{ .ipv6 = .{ .host = host, .port = port } }; | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Re-interpret a generic socket address into an IP address. | ||
| 32 | pub fn from(address: Socket.Address) ip.Address { | ||
| 33 | return switch (address) { | ||
| 34 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | ||
| 35 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Re-interpret an IP address into a generic socket address. | ||
| 40 | pub fn into(self: ip.Address) Socket.Address { | ||
| 41 | return switch (self) { | ||
| 42 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | ||
| 43 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Implements the `std.fmt.format` API. | ||
| 48 | pub fn format( | ||
| 49 | self: ip.Address, | ||
| 50 | comptime layout: []const u8, | ||
| 51 | opts: fmt.FormatOptions, | ||
| 52 | writer: anytype, | ||
| 53 | ) !void { | ||
| 54 | switch (self) { | ||
| 55 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 56 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 57 | } | ||
| 58 | } | ||
| 59 | }; | ||
lib/std/x/net/tcp.zig created+344| ... | @@ -0,0 +1,344 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 7 | const std = @import("../../std.zig"); | ||
| 8 | |||
| 9 | const os = std.os; | ||
| 10 | const ip = std.x.net.ip; | ||
| 11 | |||
| 12 | const fmt = std.fmt; | ||
| 13 | const mem = std.mem; | ||
| 14 | const builtin = std.builtin; | ||
| 15 | const testing = std.testing; | ||
| 16 | |||
| 17 | const IPv4 = std.x.os.IPv4; | ||
| 18 | const IPv6 = std.x.os.IPv6; | ||
| 19 | const Socket = std.x.os.Socket; | ||
| 20 | |||
| 21 | /// A generic TCP socket abstraction. | ||
| 22 | const tcp = @This(); | ||
| 23 | |||
| 24 | /// A TCP client-address pair. | ||
| 25 | pub const Connection = struct { | ||
| 26 | client: tcp.Client, | ||
| 27 | address: ip.Address, | ||
| 28 | |||
| 29 | /// Enclose a TCP client and address into a client-address pair. | ||
| 30 | pub fn from(conn: Socket.Connection) tcp.Connection { | ||
| 31 | return .{ | ||
| 32 | .client = tcp.Client.from(conn.socket), | ||
| 33 | .address = ip.Address.from(conn.address), | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Unravel a TCP client-address pair into a socket-address pair. | ||
| 38 | pub fn into(self: tcp.Connection) Socket.Connection { | ||
| 39 | return .{ | ||
| 40 | .socket = self.client.socket, | ||
| 41 | .address = self.address.into(), | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Closes the underlying client of the connection. | ||
| 46 | pub fn deinit(self: tcp.Connection) void { | ||
| 47 | self.client.deinit(); | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | /// Possible domains that a TCP client/listener may operate over. | ||
| 52 | pub const Domain = extern enum(u16) { | ||
| 53 | ip = os.AF_INET, | ||
| 54 | ipv6 = os.AF_INET6, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /// A TCP client. | ||
| 58 | pub const Client = struct { | ||
| 59 | socket: Socket, | ||
| 60 | |||
| 61 | /// Opens a new client. | ||
| 62 | pub fn init(domain: tcp.Domain, flags: u32) !Client { | ||
| 63 | return Client{ | ||
| 64 | .socket = try Socket.init( | ||
| 65 | @enumToInt(domain), | ||
| 66 | os.SOCK_STREAM | flags, | ||
| 67 | os.IPPROTO_TCP, | ||
| 68 | ), | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Enclose a TCP client over an existing socket. | ||
| 73 | pub fn from(socket: Socket) Client { | ||
| 74 | return Client{ .socket = socket }; | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Closes the client. | ||
| 78 | pub fn deinit(self: Client) void { | ||
| 79 | self.socket.deinit(); | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Shutdown either the read side, write side, or all sides of the client's underlying socket. | ||
| 83 | pub fn shutdown(self: Client, how: os.ShutdownHow) !void { | ||
| 84 | return self.socket.shutdown(how); | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Have the client attempt to the connect to an address. | ||
| 88 | pub fn connect(self: Client, address: ip.Address) !void { | ||
| 89 | return self.socket.connect(address.into()); | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Read data from the socket into the buffer provided. It returns the | ||
| 93 | /// number of bytes read into the buffer provided. | ||
| 94 | pub fn read(self: Client, buf: []u8) !usize { | ||
| 95 | return self.socket.read(buf); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Read data from the socket into the buffer provided with a set of flags | ||
| 99 | /// specified. It returns the number of bytes read into the buffer provided. | ||
| 100 | pub fn recv(self: Client, buf: []u8, flags: u32) !usize { | ||
| 101 | return self.socket.recv(buf, flags); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Write a buffer of data provided to the socket. It returns the number | ||
| 105 | /// of bytes that are written to the socket. | ||
| 106 | pub fn write(self: Client, buf: []const u8) !usize { | ||
| 107 | return self.socket.write(buf); | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Writes multiple I/O vectors to the socket. It returns the number | ||
| 111 | /// of bytes that are written to the socket. | ||
| 112 | pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { | ||
| 113 | return self.socket.writev(buffers); | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Write a buffer of data provided to the socket with a set of flags specified. | ||
| 117 | /// It returns the number of bytes that are written to the socket. | ||
| 118 | pub fn send(self: Client, buf: []const u8, flags: u32) !usize { | ||
| 119 | return self.socket.send(buf, flags); | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Writes multiple I/O vectors with a prepended message header to the socket | ||
| 123 | /// with a set of flags specified. It returns the number of bytes that are | ||
| 124 | /// written to the socket. | ||
| 125 | pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { | ||
| 126 | return self.socket.sendmsg(msg, flags); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Query and return the latest cached error on the client's underlying socket. | ||
| 130 | pub fn getError(self: Client) !void { | ||
| 131 | return self.socket.getError(); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Query the read buffer size of the client's underlying socket. | ||
| 135 | pub fn getReadBufferSize(self: Client) !u32 { | ||
| 136 | return self.socket.getReadBufferSize(); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Query the write buffer size of the client's underlying socket. | ||
| 140 | pub fn getWriteBufferSize(self: Client) !u32 { | ||
| 141 | return self.socket.getWriteBufferSize(); | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Query the address that the client's socket is locally bounded to. | ||
| 145 | pub fn getLocalAddress(self: Client) !ip.Address { | ||
| 146 | return ip.Address.from(try self.socket.getLocalAddress()); | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if | ||
| 150 | /// the host does not support sockets disabling Nagle's algorithm. | ||
| 151 | pub fn setNoDelay(self: Client, enabled: bool) !void { | ||
| 152 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | ||
| 153 | const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); | ||
| 154 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); | ||
| 155 | } | ||
| 156 | return error.UnsupportedSocketOption; | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Set the write buffer size of the socket. | ||
| 160 | pub fn setWriteBufferSize(self: Client, size: u32) !void { | ||
| 161 | return self.socket.setWriteBufferSize(size); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Set the read buffer size of the socket. | ||
| 165 | pub fn setReadBufferSize(self: Client, size: u32) !void { | ||
| 166 | return self.socket.setReadBufferSize(size); | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Set a timeout on the socket that is to occur if no messages are successfully written | ||
| 170 | /// to its bound destination after a specified number of milliseconds. A subsequent write | ||
| 171 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 172 | pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { | ||
| 173 | return self.socket.setWriteTimeout(milliseconds); | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Set a timeout on the socket that is to occur if no messages are successfully read | ||
| 177 | /// from its bound destination after a specified number of milliseconds. A subsequent | ||
| 178 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | ||
| 179 | /// exceeded. | ||
| 180 | pub fn setReadTimeout(self: Client, milliseconds: usize) !void { | ||
| 181 | return self.socket.setReadTimeout(milliseconds); | ||
| 182 | } | ||
| 183 | }; | ||
| 184 | |||
| 185 | /// A TCP listener. | ||
| 186 | pub const Listener = struct { | ||
| 187 | socket: Socket, | ||
| 188 | |||
| 189 | /// Opens a new listener. | ||
| 190 | pub fn init(domain: tcp.Domain, flags: u32) !Listener { | ||
| 191 | return Listener{ | ||
| 192 | .socket = try Socket.init( | ||
| 193 | @enumToInt(domain), | ||
| 194 | os.SOCK_STREAM | flags, | ||
| 195 | os.IPPROTO_TCP, | ||
| 196 | ), | ||
| 197 | }; | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Closes the listener. | ||
| 201 | pub fn deinit(self: Listener) void { | ||
| 202 | self.socket.deinit(); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Shuts down the underlying listener's socket. The next subsequent call, or | ||
| 206 | /// a current pending call to accept() after shutdown is called will return | ||
| 207 | /// an error. | ||
| 208 | pub fn shutdown(self: Listener) !void { | ||
| 209 | return self.socket.shutdown(.recv); | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Binds the listener's socket to an address. | ||
| 213 | pub fn bind(self: Listener, address: ip.Address) !void { | ||
| 214 | return self.socket.bind(address.into()); | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Start listening for incoming connections. | ||
| 218 | pub fn listen(self: Listener, max_backlog_size: u31) !void { | ||
| 219 | return self.socket.listen(max_backlog_size); | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Accept a pending incoming connection queued to the kernel backlog | ||
| 223 | /// of the listener's socket. | ||
| 224 | pub fn accept(self: Listener, flags: u32) !tcp.Connection { | ||
| 225 | return tcp.Connection.from(try self.socket.accept(flags)); | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Query and return the latest cached error on the listener's underlying socket. | ||
| 229 | pub fn getError(self: Client) !void { | ||
| 230 | return self.socket.getError(); | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Query the address that the listener's socket is locally bounded to. | ||
| 234 | pub fn getLocalAddress(self: Listener) !ip.Address { | ||
| 235 | return ip.Address.from(try self.socket.getLocalAddress()); | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | ||
| 239 | /// the host does not support sockets listening the same address. | ||
| 240 | pub fn setReuseAddress(self: Listener, enabled: bool) !void { | ||
| 241 | return self.socket.setReuseAddress(enabled); | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | ||
| 245 | /// the host does not supports sockets listening on the same port. | ||
| 246 | pub fn setReusePort(self: Listener, enabled: bool) !void { | ||
| 247 | return self.socket.setReusePort(enabled); | ||
| 248 | } | ||
| 249 | |||
| 250 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | ||
| 251 | /// support TCP Fast Open. | ||
| 252 | pub fn setFastOpen(self: Listener, enabled: bool) !void { | ||
| 253 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | ||
| 254 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 255 | } | ||
| 256 | return error.UnsupportedSocketOption; | ||
| 257 | } | ||
| 258 | |||
| 259 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | ||
| 260 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | ||
| 261 | pub fn setQuickACK(self: Listener, enabled: bool) !void { | ||
| 262 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | ||
| 263 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 264 | } | ||
| 265 | return error.UnsupportedSocketOption; | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Set a timeout on the listener that is to occur if no new incoming connections come in | ||
| 269 | /// after a specified number of milliseconds. A subsequent accept call to the listener | ||
| 270 | /// will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 271 | pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { | ||
| 272 | return self.socket.setReadTimeout(milliseconds); | ||
| 273 | } | ||
| 274 | }; | ||
| 275 | |||
| 276 | test "tcp: create client/listener pair" { | ||
| 277 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 278 | |||
| 279 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 280 | defer listener.deinit(); | ||
| 281 | |||
| 282 | try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 283 | try listener.listen(128); | ||
| 284 | |||
| 285 | const binded_address = try listener.getLocalAddress(); | ||
| 286 | |||
| 287 | const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); | ||
| 288 | defer client.deinit(); | ||
| 289 | |||
| 290 | try client.connect(binded_address); | ||
| 291 | |||
| 292 | const conn = try listener.accept(os.SOCK_CLOEXEC); | ||
| 293 | defer conn.deinit(); | ||
| 294 | } | ||
| 295 | |||
| 296 | test "tcp/client: set read timeout of 1 millisecond on blocking client" { | ||
| 297 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 298 | |||
| 299 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 300 | defer listener.deinit(); | ||
| 301 | |||
| 302 | try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 303 | try listener.listen(128); | ||
| 304 | |||
| 305 | const binded_address = try listener.getLocalAddress(); | ||
| 306 | |||
| 307 | const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); | ||
| 308 | defer client.deinit(); | ||
| 309 | |||
| 310 | try client.connect(binded_address); | ||
| 311 | try client.setReadTimeout(1); | ||
| 312 | |||
| 313 | const conn = try listener.accept(os.SOCK_CLOEXEC); | ||
| 314 | defer conn.deinit(); | ||
| 315 | |||
| 316 | var buf: [1]u8 = undefined; | ||
| 317 | testing.expectError(error.WouldBlock, client.read(&buf)); | ||
| 318 | } | ||
| 319 | |||
| 320 | test "tcp/listener: bind to unspecified ipv4 address" { | ||
| 321 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 322 | |||
| 323 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 324 | defer listener.deinit(); | ||
| 325 | |||
| 326 | try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 327 | try listener.listen(128); | ||
| 328 | |||
| 329 | const address = try listener.getLocalAddress(); | ||
| 330 | testing.expect(address == .ipv4); | ||
| 331 | } | ||
| 332 | |||
| 333 | test "tcp/listener: bind to unspecified ipv6 address" { | ||
| 334 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 335 | |||
| 336 | const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC); | ||
| 337 | defer listener.deinit(); | ||
| 338 | |||
| 339 | try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0)); | ||
| 340 | try listener.listen(128); | ||
| 341 | |||
| 342 | const address = try listener.getLocalAddress(); | ||
| 343 | testing.expect(address == .ipv6); | ||
| 344 | } | ||
lib/std/x/os/Socket.zig+114-96| ... | @@ -1,18 +1,109 @@ | ... | @@ -1,18 +1,109 @@ |
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 1 | const std = @import("../../std.zig"); | 7 | const std = @import("../../std.zig"); |
| 8 | const net = @import("net.zig"); | ||
| 2 | 9 | ||
| 3 | const os = std.os; | 10 | const os = std.os; |
| 4 | const mem = std.mem; | 11 | const mem = std.mem; |
| 5 | const net = std.net; | ||
| 6 | const time = std.time; | 12 | const time = std.time; |
| 7 | const builtin = std.builtin; | ||
| 8 | const testing = std.testing; | ||
| 9 | 13 | ||
| 14 | /// A generic socket abstraction. | ||
| 10 | const Socket = @This(); | 15 | const Socket = @This(); |
| 11 | 16 | ||
| 12 | /// A socket-address pair. | 17 | /// A socket-address pair. |
| 13 | pub const Connection = struct { | 18 | pub const Connection = struct { |
| 14 | socket: Socket, | 19 | socket: Socket, |
| 15 | address: net.Address, | 20 | address: Socket.Address, |
| 21 | |||
| 22 | /// Enclose a socket and address into a socket-address pair. | ||
| 23 | pub fn from(socket: Socket, address: Socket.Address) Socket.Connection { | ||
| 24 | return .{ .socket = socket, .address = address }; | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | |||
| 28 | /// A generic socket address abstraction. It is safe to directly access and modify | ||
| 29 | /// the fields of a `Socket.Address`. | ||
| 30 | pub const Address = union(enum) { | ||
| 31 | ipv4: net.IPv4.Address, | ||
| 32 | ipv6: net.IPv6.Address, | ||
| 33 | |||
| 34 | /// Instantiate a new address with a IPv4 host and port. | ||
| 35 | pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address { | ||
| 36 | return .{ .ipv4 = .{ .host = host, .port = port } }; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Instantiate a new address with a IPv6 host and port. | ||
| 40 | pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address { | ||
| 41 | return .{ .ipv6 = .{ .host = host, .port = port } }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Parses a `sockaddr` into a generic socket address. | ||
| 45 | pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address { | ||
| 46 | switch (address.family) { | ||
| 47 | os.AF_INET => { | ||
| 48 | const info = @ptrCast(*const os.sockaddr_in, address); | ||
| 49 | const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) }; | ||
| 50 | const port = mem.bigToNative(u16, info.port); | ||
| 51 | return Socket.Address.initIPv4(host, port); | ||
| 52 | }, | ||
| 53 | os.AF_INET6 => { | ||
| 54 | const info = @ptrCast(*const os.sockaddr_in6, address); | ||
| 55 | const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id }; | ||
| 56 | const port = mem.bigToNative(u16, info.port); | ||
| 57 | return Socket.Address.initIPv6(host, port); | ||
| 58 | }, | ||
| 59 | else => unreachable, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Encodes a generic socket address into an extern union that may be reliably | ||
| 64 | /// casted into a `sockaddr` which may be passed into socket syscalls. | ||
| 65 | pub fn toNative(self: Socket.Address) extern union { | ||
| 66 | ipv4: os.sockaddr_in, | ||
| 67 | ipv6: os.sockaddr_in6, | ||
| 68 | } { | ||
| 69 | return switch (self) { | ||
| 70 | .ipv4 => |address| .{ | ||
| 71 | .ipv4 = .{ | ||
| 72 | .addr = @bitCast(u32, address.host.octets), | ||
| 73 | .port = mem.nativeToBig(u16, address.port), | ||
| 74 | }, | ||
| 75 | }, | ||
| 76 | .ipv6 => |address| .{ | ||
| 77 | .ipv6 = .{ | ||
| 78 | .addr = address.host.octets, | ||
| 79 | .port = mem.nativeToBig(u16, address.port), | ||
| 80 | .scope_id = address.host.scope_id, | ||
| 81 | .flowinfo = 0, | ||
| 82 | }, | ||
| 83 | }, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Returns the number of bytes that make up the `sockaddr` equivalent to the address. | ||
| 88 | pub fn getNativeSize(self: Socket.Address) u32 { | ||
| 89 | return switch (self) { | ||
| 90 | .ipv4 => @sizeOf(os.sockaddr_in), | ||
| 91 | .ipv6 => @sizeOf(os.sockaddr_in6), | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Implements the `std.fmt.format` API. | ||
| 96 | pub fn format( | ||
| 97 | self: Socket.Address, | ||
| 98 | comptime layout: []const u8, | ||
| 99 | opts: fmt.FormatOptions, | ||
| 100 | writer: anytype, | ||
| 101 | ) !void { | ||
| 102 | switch (self) { | ||
| 103 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 104 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 105 | } | ||
| 106 | } | ||
| 16 | }; | 107 | }; |
| 17 | 108 | ||
| 18 | /// The underlying handle of a socket. | 109 | /// The underlying handle of a socket. |
| ... | @@ -23,19 +114,24 @@ pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { | ... | @@ -23,19 +114,24 @@ pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { |
| 23 | return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; | 114 | return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; |
| 24 | } | 115 | } |
| 25 | 116 | ||
| 117 | /// Enclose a socket abstraction over an existing socket file descriptor. | ||
| 118 | pub fn from(fd: os.socket_t) Socket { | ||
| 119 | return Socket{ .fd = fd }; | ||
| 120 | } | ||
| 121 | |||
| 26 | /// Closes the socket. | 122 | /// Closes the socket. |
| 27 | pub fn deinit(self: Socket) void { | 123 | pub fn deinit(self: Socket) void { |
| 28 | os.closeSocket(self.fd); | 124 | os.closeSocket(self.fd); |
| 29 | } | 125 | } |
| 30 | 126 | ||
| 31 | /// Shutdown either the read side, or write side, or the entirety of a socket. | 127 | /// Shutdown either the read side, write side, or all side of the socket. |
| 32 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | 128 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { |
| 33 | return os.shutdown(self.fd, how); | 129 | return os.shutdown(self.fd, how); |
| 34 | } | 130 | } |
| 35 | 131 | ||
| 36 | /// Binds the socket to an address. | 132 | /// Binds the socket to an address. |
| 37 | pub fn bind(self: Socket, address: net.Address) !void { | 133 | pub fn bind(self: Socket, address: Socket.Address) !void { |
| 38 | return os.bind(self.fd, &address.any, address.getOsSockLen()); | 134 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); |
| 39 | } | 135 | } |
| 40 | 136 | ||
| 41 | /// Start listening for incoming connections on the socket. | 137 | /// Start listening for incoming connections on the socket. |
| ... | @@ -44,8 +140,8 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void { | ... | @@ -44,8 +140,8 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void { |
| 44 | } | 140 | } |
| 45 | 141 | ||
| 46 | /// Have the socket attempt to the connect to an address. | 142 | /// Have the socket attempt to the connect to an address. |
| 47 | pub fn connect(self: Socket, address: net.Address) !void { | 143 | pub fn connect(self: Socket, address: Socket.Address) !void { |
| 48 | return os.connect(self.fd, &address.any, address.getOsSockLen()); | 144 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); |
| 49 | } | 145 | } |
| 50 | 146 | ||
| 51 | /// Accept a pending incoming connection queued to the kernel backlog | 147 | /// Accept a pending incoming connection queued to the kernel backlog |
| ... | @@ -54,12 +150,10 @@ pub fn accept(self: Socket, flags: u32) !Socket.Connection { | ... | @@ -54,12 +150,10 @@ pub fn accept(self: Socket, flags: u32) !Socket.Connection { |
| 54 | var address: os.sockaddr = undefined; | 150 | var address: os.sockaddr = undefined; |
| 55 | var address_len: u32 = @sizeOf(os.sockaddr); | 151 | var address_len: u32 = @sizeOf(os.sockaddr); |
| 56 | 152 | ||
| 57 | const fd = try os.accept(self.fd, &address, &address_len, flags); | 153 | const socket = Socket{ .fd = try os.accept(self.fd, &address, &address_len, flags) }; |
| 154 | const socket_address = Socket.Address.fromNative(@alignCast(4, &address)); | ||
| 58 | 155 | ||
| 59 | return Connection{ | 156 | return Socket.Connection.from(socket, socket_address); |
| 60 | .socket = Socket{ .fd = fd }, | ||
| 61 | .address = net.Address.initPosix(@alignCast(4, &address)), | ||
| 62 | }; | ||
| 63 | } | 157 | } |
| 64 | 158 | ||
| 65 | /// Read data from the socket into the buffer provided. It returns the | 159 | /// Read data from the socket into the buffer provided. It returns the |
| ... | @@ -100,11 +194,11 @@ pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { | ... | @@ -100,11 +194,11 @@ pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { |
| 100 | } | 194 | } |
| 101 | 195 | ||
| 102 | /// Query the address that the socket is locally bounded to. | 196 | /// Query the address that the socket is locally bounded to. |
| 103 | pub fn getLocalAddress(self: Socket) !net.Address { | 197 | pub fn getLocalAddress(self: Socket) !Socket.Address { |
| 104 | var address: os.sockaddr = undefined; | 198 | var address: os.sockaddr = undefined; |
| 105 | var address_len: u32 = @sizeOf(os.sockaddr); | 199 | var address_len: u32 = @sizeOf(os.sockaddr); |
| 106 | try os.getsockname(self.fd, &address, &address_len); | 200 | try os.getsockname(self.fd, &address, &address_len); |
| 107 | return net.Address.initPosix(@alignCast(4, &address)); | 201 | return Socket.Address.fromNative(@alignCast(4, &address)); |
| 108 | } | 202 | } |
| 109 | 203 | ||
| 110 | /// Query and return the latest cached error on the socket. | 204 | /// Query and return the latest cached error on the socket. |
| ... | @@ -164,33 +258,6 @@ pub fn setReusePort(self: Socket, enabled: bool) !void { | ... | @@ -164,33 +258,6 @@ pub fn setReusePort(self: Socket, enabled: bool) !void { |
| 164 | return error.UnsupportedSocketOption; | 258 | return error.UnsupportedSocketOption; |
| 165 | } | 259 | } |
| 166 | 260 | ||
| 167 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not support | ||
| 168 | /// sockets disabling Nagle's algorithm. | ||
| 169 | pub fn setNoDelay(self: Socket, enabled: bool) !void { | ||
| 170 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | ||
| 171 | return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_NODELAY, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 172 | } | ||
| 173 | return error.UnsupportedSocketOption; | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | ||
| 177 | /// support TCP Fast Open. | ||
| 178 | pub fn setFastOpen(self: Socket, enabled: bool) !void { | ||
| 179 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | ||
| 180 | return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 181 | } | ||
| 182 | return error.UnsupportedSocketOption; | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | ||
| 186 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | ||
| 187 | pub fn setQuickACK(self: Socket, enabled: bool) !void { | ||
| 188 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | ||
| 189 | return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 190 | } | ||
| 191 | return error.UnsupportedSocketOption; | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Set the write buffer size of the socket. | 261 | /// Set the write buffer size of the socket. |
| 195 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { | 262 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { |
| 196 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size)); | 263 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size)); |
| ... | @@ -206,8 +273,8 @@ pub fn setReadBufferSize(self: Socket, size: u32) !void { | ... | @@ -206,8 +273,8 @@ pub fn setReadBufferSize(self: Socket, size: u32) !void { |
| 206 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | 273 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. |
| 207 | pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { | 274 | pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { |
| 208 | const timeout = os.timeval{ | 275 | const timeout = os.timeval{ |
| 209 | .tv_sec = @intCast(isize, milliseconds / time.ms_per_s), | 276 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), |
| 210 | .tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms), | 277 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), |
| 211 | }; | 278 | }; |
| 212 | 279 | ||
| 213 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout)); | 280 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout)); |
| ... | @@ -219,58 +286,9 @@ pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { | ... | @@ -219,58 +286,9 @@ pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { |
| 219 | /// exceeded. | 286 | /// exceeded. |
| 220 | pub fn setReadTimeout(self: Socket, milliseconds: usize) !void { | 287 | pub fn setReadTimeout(self: Socket, milliseconds: usize) !void { |
| 221 | const timeout = os.timeval{ | 288 | const timeout = os.timeval{ |
| 222 | .tv_sec = @intCast(isize, milliseconds / time.ms_per_s), | 289 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), |
| 223 | .tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms), | 290 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), |
| 224 | }; | 291 | }; |
| 225 | 292 | ||
| 226 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout)); | 293 | return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout)); |
| 227 | } | 294 | } |
| 228 | |||
| 229 | test { | ||
| 230 | testing.refAllDecls(@This()); | ||
| 231 | } | ||
| 232 | |||
| 233 | test "socket/linux: set read timeout of 1 millisecond on blocking socket" { | ||
| 234 | if (builtin.os.tag != .linux) return error.SkipZigTest; | ||
| 235 | |||
| 236 | const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP); | ||
| 237 | defer a.deinit(); | ||
| 238 | |||
| 239 | try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0)); | ||
| 240 | try a.listen(128); | ||
| 241 | |||
| 242 | const binded_address = try a.getLocalAddress(); | ||
| 243 | |||
| 244 | const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP); | ||
| 245 | defer b.deinit(); | ||
| 246 | |||
| 247 | try b.connect(binded_address); | ||
| 248 | try b.setReadTimeout(1); | ||
| 249 | |||
| 250 | const ab = try a.accept(os.SOCK_CLOEXEC); | ||
| 251 | defer ab.socket.deinit(); | ||
| 252 | |||
| 253 | var buf: [1]u8 = undefined; | ||
| 254 | testing.expectError(error.WouldBlock, b.read(&buf)); | ||
| 255 | } | ||
| 256 | |||
| 257 | test "socket/linux: create non-blocking socket pair" { | ||
| 258 | if (builtin.os.tag != .linux) return error.SkipZigTest; | ||
| 259 | |||
| 260 | const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP); | ||
| 261 | defer a.deinit(); | ||
| 262 | |||
| 263 | try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0)); | ||
| 264 | try a.listen(128); | ||
| 265 | |||
| 266 | const binded_address = try a.getLocalAddress(); | ||
| 267 | |||
| 268 | const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP); | ||
| 269 | defer b.deinit(); | ||
| 270 | |||
| 271 | testing.expectError(error.WouldBlock, b.connect(binded_address)); | ||
| 272 | try b.getError(); | ||
| 273 | |||
| 274 | const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 275 | defer ab.socket.deinit(); | ||
| 276 | } |
lib/std/x/os/net.zig created+567| ... | @@ -0,0 +1,567 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 7 | const std = @import("../../std.zig"); | ||
| 8 | |||
| 9 | const os = std.os; | ||
| 10 | const fmt = std.fmt; | ||
| 11 | const mem = std.mem; | ||
| 12 | const math = std.math; | ||
| 13 | const builtin = std.builtin; | ||
| 14 | const testing = std.testing; | ||
| 15 | |||
| 16 | /// Resolves a network interface name into a scope/zone ID. It returns | ||
| 17 | /// an error if either resolution fails, or if the interface name is | ||
| 18 | /// too long. | ||
| 19 | pub fn resolveScopeID(name: []const u8) !u32 { | ||
| 20 | if (comptime @hasDecl(os, "IFNAMESIZE")) { | ||
| 21 | if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong; | ||
| 22 | |||
| 23 | const fd = try os.socket(os.AF_UNIX, os.SOCK_DGRAM, 0); | ||
| 24 | defer os.closeSocket(fd); | ||
| 25 | |||
| 26 | var f: os.ifreq = undefined; | ||
| 27 | mem.copy(u8, &f.ifrn.name, name); | ||
| 28 | f.ifrn.name[name.len] = 0; | ||
| 29 | |||
| 30 | try os.ioctl_SIOCGIFINDEX(fd, &f); | ||
| 31 | |||
| 32 | return @bitCast(u32, f.ifru.ivalue); | ||
| 33 | } | ||
| 34 | return error.Unsupported; | ||
| 35 | } | ||
| 36 | |||
| 37 | /// An IPv4 address comprised of 4 bytes. | ||
| 38 | pub const IPv4 = extern struct { | ||
| 39 | /// A IPv4 host-port pair. | ||
| 40 | pub const Address = extern struct { | ||
| 41 | host: IPv4, | ||
| 42 | port: u16, | ||
| 43 | }; | ||
| 44 | |||
| 45 | /// Octets of a IPv4 address designating the local host. | ||
| 46 | pub const localhost_octets = [_]u8{ 127, 0, 0, 1 }; | ||
| 47 | |||
| 48 | /// The IPv4 address of the local host. | ||
| 49 | pub const localhost: IPv4 = .{ .octets = localhost_octets }; | ||
| 50 | |||
| 51 | /// Octets of an unspecified IPv4 address. | ||
| 52 | pub const unspecified_octets = [_]u8{0} ** 4; | ||
| 53 | |||
| 54 | /// An unspecified IPv4 address. | ||
| 55 | pub const unspecified: IPv4 = .{ .octets = unspecified_octets }; | ||
| 56 | |||
| 57 | /// Octets of a broadcast IPv4 address. | ||
| 58 | pub const broadcast_octets = [_]u8{255} ** 4; | ||
| 59 | |||
| 60 | /// An IPv4 broadcast address. | ||
| 61 | pub const broadcast: IPv4 = .{ .octets = broadcast_octets }; | ||
| 62 | |||
| 63 | /// The prefix octet pattern of a link-local IPv4 address. | ||
| 64 | pub const link_local_prefix = [_]u8{ 169, 254 }; | ||
| 65 | |||
| 66 | /// The prefix octet patterns of IPv4 addresses intended for | ||
| 67 | /// documentation. | ||
| 68 | pub const documentation_prefixes = [_][]const u8{ | ||
| 69 | &[_]u8{ 192, 0, 2 }, | ||
| 70 | &[_]u8{ 198, 51, 100 }, | ||
| 71 | &[_]u8{ 203, 0, 113 }, | ||
| 72 | }; | ||
| 73 | |||
| 74 | octets: [4]u8, | ||
| 75 | |||
| 76 | /// Returns whether or not the two addresses are equal to, less than, or | ||
| 77 | /// greater than each other. | ||
| 78 | pub fn cmp(self: IPv4, other: IPv4) math.Order { | ||
| 79 | return mem.order(u8, &self.octets, &other.octets); | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Returns true if both addresses are semantically equivalent. | ||
| 83 | pub fn eql(self: IPv4, other: IPv4) bool { | ||
| 84 | return mem.eql(u8, &self.octets, &other.octets); | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Returns true if the address is a loopback address. | ||
| 88 | pub fn isLoopback(self: IPv4) bool { | ||
| 89 | return self.octets[0] == 127; | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Returns true if the address is an unspecified IPv4 address. | ||
| 93 | pub fn isUnspecified(self: IPv4) bool { | ||
| 94 | return mem.eql(u8, &self.octets, &unspecified_octets); | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Returns true if the address is a private IPv4 address. | ||
| 98 | pub fn isPrivate(self: IPv4) bool { | ||
| 99 | return self.octets[0] == 10 or | ||
| 100 | (self.octets[0] == 172 and self.octets[1] >= 16 and self.octets[1] <= 31) or | ||
| 101 | (self.octets[0] == 192 and self.octets[1] == 168); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Returns true if the address is a link-local IPv4 address. | ||
| 105 | pub fn isLinkLocal(self: IPv4) bool { | ||
| 106 | return mem.startsWith(u8, &self.octets, &link_local_prefix); | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Returns true if the address is a multicast IPv4 address. | ||
| 110 | pub fn isMulticast(self: IPv4) bool { | ||
| 111 | return self.octets[0] >= 224 and self.octets[0] <= 239; | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Returns true if the address is a IPv4 broadcast address. | ||
| 115 | pub fn isBroadcast(self: IPv4) bool { | ||
| 116 | return mem.eql(u8, &self.octets, &broadcast_octets); | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Returns true if the address is in a range designated for documentation. Refer | ||
| 120 | /// to IETF RFC 5737 for more details. | ||
| 121 | pub fn isDocumentation(self: IPv4) bool { | ||
| 122 | inline for (documentation_prefixes) |prefix| { | ||
| 123 | if (mem.startsWith(u8, &self.octets, prefix)) { | ||
| 124 | return true; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | return false; | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Implements the `std.fmt.format` API. | ||
| 131 | pub fn format( | ||
| 132 | self: IPv4, | ||
| 133 | comptime layout: []const u8, | ||
| 134 | opts: fmt.FormatOptions, | ||
| 135 | writer: anytype, | ||
| 136 | ) !void { | ||
| 137 | if (comptime layout.len != 0 and layout[0] != 's') { | ||
| 138 | @compileError("Unsupported format specifier for IPv4 type '" ++ layout ++ "'."); | ||
| 139 | } | ||
| 140 | |||
| 141 | try fmt.format(writer, "{}.{}.{}.{}", .{ | ||
| 142 | self.octets[0], | ||
| 143 | self.octets[1], | ||
| 144 | self.octets[2], | ||
| 145 | self.octets[3], | ||
| 146 | }); | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Set of possible errors that may encountered when parsing an IPv4 | ||
| 150 | /// address. | ||
| 151 | pub const ParseError = error{ | ||
| 152 | UnexpectedEndOfOctet, | ||
| 153 | TooManyOctets, | ||
| 154 | OctetOverflow, | ||
| 155 | UnexpectedToken, | ||
| 156 | IncompleteAddress, | ||
| 157 | }; | ||
| 158 | |||
| 159 | /// Parses an arbitrary IPv4 address. | ||
| 160 | pub fn parse(buf: []const u8) ParseError!IPv4 { | ||
| 161 | var octets: [4]u8 = undefined; | ||
| 162 | var octet: u8 = 0; | ||
| 163 | |||
| 164 | var index: u8 = 0; | ||
| 165 | var saw_any_digits: bool = false; | ||
| 166 | |||
| 167 | for (buf) |c| { | ||
| 168 | switch (c) { | ||
| 169 | '.' => { | ||
| 170 | if (!saw_any_digits) return error.UnexpectedEndOfOctet; | ||
| 171 | if (index == 3) return error.TooManyOctets; | ||
| 172 | octets[index] = octet; | ||
| 173 | index += 1; | ||
| 174 | octet = 0; | ||
| 175 | saw_any_digits = false; | ||
| 176 | }, | ||
| 177 | '0'...'9' => { | ||
| 178 | saw_any_digits = true; | ||
| 179 | octet = math.mul(u8, octet, 10) catch return error.OctetOverflow; | ||
| 180 | octet = math.add(u8, octet, c - '0') catch return error.OctetOverflow; | ||
| 181 | }, | ||
| 182 | else => return error.UnexpectedToken, | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | if (index == 3 and saw_any_digits) { | ||
| 187 | octets[index] = octet; | ||
| 188 | return IPv4{ .octets = octets }; | ||
| 189 | } | ||
| 190 | |||
| 191 | return error.IncompleteAddress; | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Maps the address to its IPv6 equivalent. In most cases, you would | ||
| 195 | /// want to map the address to its IPv6 equivalent rather than directly | ||
| 196 | /// re-interpreting the address. | ||
| 197 | pub fn mapToIPv6(self: IPv4) IPv6 { | ||
| 198 | var octets: [16]u8 = undefined; | ||
| 199 | mem.copy(u8, octets[0..12], &IPv6.v4_mapped_prefix); | ||
| 200 | mem.copy(u8, octets[12..], &self.octets); | ||
| 201 | return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id }; | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Directly re-interprets the address to its IPv6 equivalent. In most | ||
| 205 | /// cases, you would want to map the address to its IPv6 equivalent rather | ||
| 206 | /// than directly re-interpreting the address. | ||
| 207 | pub fn toIPv6(self: IPv4) IPv6 { | ||
| 208 | var octets: [16]u8 = undefined; | ||
| 209 | mem.set(u8, octets[0..12], 0); | ||
| 210 | mem.copy(u8, octets[12..], &self.octets); | ||
| 211 | return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id }; | ||
| 212 | } | ||
| 213 | }; | ||
| 214 | |||
| 215 | /// An IPv6 address comprised of 16 bytes for an address, and 4 bytes | ||
| 216 | /// for a scope ID; cumulatively summing to 20 bytes in total. | ||
| 217 | pub const IPv6 = extern struct { | ||
| 218 | /// A IPv6 host-port pair. | ||
| 219 | pub const Address = extern struct { | ||
| 220 | host: IPv6, | ||
| 221 | port: u16, | ||
| 222 | }; | ||
| 223 | |||
| 224 | /// Octets of a IPv6 address designating the local host. | ||
| 225 | pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01}; | ||
| 226 | |||
| 227 | /// The IPv6 address of the local host. | ||
| 228 | pub const localhost: IPv6 = .{ | ||
| 229 | .octets = localhost_octets, | ||
| 230 | .scope_id = no_scope_id, | ||
| 231 | }; | ||
| 232 | |||
| 233 | /// Octets of an unspecified IPv6 address. | ||
| 234 | pub const unspecified_octets = [_]u8{0} ** 16; | ||
| 235 | |||
| 236 | /// An unspecified IPv6 address. | ||
| 237 | pub const unspecified: IPv6 = .{ | ||
| 238 | .octets = unspecified_octets, | ||
| 239 | .scope_id = no_scope_id, | ||
| 240 | }; | ||
| 241 | |||
| 242 | /// The prefix of a IPv6 address that is mapped to a IPv4 address. | ||
| 243 | pub const v4_mapped_prefix = [_]u8{0} ** 10 ++ [_]u8{0xFF} ** 2; | ||
| 244 | |||
| 245 | /// A marker value used to designate an IPv6 address with no | ||
| 246 | /// associated scope ID. | ||
| 247 | pub const no_scope_id = math.maxInt(u32); | ||
| 248 | |||
| 249 | octets: [16]u8, | ||
| 250 | scope_id: u32, | ||
| 251 | |||
| 252 | /// Returns whether or not the two addresses are equal to, less than, or | ||
| 253 | /// greater than each other. | ||
| 254 | pub fn cmp(self: IPv6, other: IPv6) math.Order { | ||
| 255 | return switch (mem.order(u8, self.octets, other.octets)) { | ||
| 256 | .eq => math.order(self.scope_id, other.scope_id), | ||
| 257 | else => |order| order, | ||
| 258 | }; | ||
| 259 | } | ||
| 260 | |||
| 261 | /// Returns true if both addresses are semantically equivalent. | ||
| 262 | pub fn eql(self: IPv6, other: IPv6) bool { | ||
| 263 | return self.scope_id == other.scope_id and mem.eql(u8, &self.octets, &other.octets); | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Returns true if the address is an unspecified IPv6 address. | ||
| 267 | pub fn isUnspecified(self: IPv6) bool { | ||
| 268 | return mem.eql(u8, &self.octets, &unspecified_octets); | ||
| 269 | } | ||
| 270 | |||
| 271 | /// Returns true if the address is a loopback address. | ||
| 272 | pub fn isLoopback(self: IPv6) bool { | ||
| 273 | return mem.eql(u8, self.octets[0..3], &[_]u8{ 0, 0, 0 }) and | ||
| 274 | mem.eql(u8, self.octets[12..], &[_]u8{ 0, 0, 0, 1 }); | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Returns true if the address maps to an IPv4 address. | ||
| 278 | pub fn mapsToIPv4(self: IPv6) bool { | ||
| 279 | return mem.startsWith(u8, &self.octets, &v4_mapped_prefix); | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Returns an IPv4 address representative of the address should | ||
| 283 | /// it the address be mapped to an IPv4 address. It returns null | ||
| 284 | /// otherwise. | ||
| 285 | pub fn toIPv4(self: IPv6) ?IPv4 { | ||
| 286 | if (!self.mapsToIPv4()) return null; | ||
| 287 | return IPv4{ .octets = self.octets[12..][0..4].* }; | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Returns true if the address is a multicast IPv6 address. | ||
| 291 | pub fn isMulticast(self: IPv6) bool { | ||
| 292 | return self.octets[0] == 0xFF; | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Returns true if the address is a unicast link local IPv6 address. | ||
| 296 | pub fn isLinkLocal(self: IPv6) bool { | ||
| 297 | return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0x80; | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Returns true if the address is a deprecated unicast site local | ||
| 301 | /// IPv6 address. Refer to IETF RFC 3879 for more details as to | ||
| 302 | /// why they are deprecated. | ||
| 303 | pub fn isSiteLocal(self: IPv6) bool { | ||
| 304 | return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0xC0; | ||
| 305 | } | ||
| 306 | |||
| 307 | /// IPv6 multicast address scopes. | ||
| 308 | pub const Scope = enum(u8) { | ||
| 309 | interface = 1, | ||
| 310 | link = 2, | ||
| 311 | realm = 3, | ||
| 312 | admin = 4, | ||
| 313 | site = 5, | ||
| 314 | organization = 8, | ||
| 315 | global = 14, | ||
| 316 | unknown = 0xFF, | ||
| 317 | }; | ||
| 318 | |||
| 319 | /// Returns the multicast scope of the address. | ||
| 320 | pub fn scope(self: IPv6) Scope { | ||
| 321 | if (!self.isMulticast()) return .unknown; | ||
| 322 | |||
| 323 | return switch (self.octets[0] & 0x0F) { | ||
| 324 | 1 => .interface, | ||
| 325 | 2 => .link, | ||
| 326 | 3 => .realm, | ||
| 327 | 4 => .admin, | ||
| 328 | 5 => .site, | ||
| 329 | 8 => .organization, | ||
| 330 | 14 => .global, | ||
| 331 | else => .unknown, | ||
| 332 | }; | ||
| 333 | } | ||
| 334 | |||
| 335 | /// Implements the `std.fmt.format` API. Specifying 'x' or 's' formats the | ||
| 336 | /// address lower-cased octets, while specifying 'X' or 'S' formats the | ||
| 337 | /// address using upper-cased ASCII octets. | ||
| 338 | /// | ||
| 339 | /// The default specifier is 'x'. | ||
| 340 | pub fn format( | ||
| 341 | self: IPv6, | ||
| 342 | comptime layout: []const u8, | ||
| 343 | opts: fmt.FormatOptions, | ||
| 344 | writer: anytype, | ||
| 345 | ) !void { | ||
| 346 | comptime const specifier = &[_]u8{if (layout.len == 0) 'x' else switch (layout[0]) { | ||
| 347 | 'x', 'X' => |specifier| specifier, | ||
| 348 | 's' => 'x', | ||
| 349 | 'S' => 'X', | ||
| 350 | else => @compileError("Unsupported format specifier for IPv6 type '" ++ layout ++ "'."), | ||
| 351 | }}; | ||
| 352 | |||
| 353 | if (mem.startsWith(u8, &self.octets, &v4_mapped_prefix)) { | ||
| 354 | return fmt.format(writer, "::{" ++ specifier ++ "}{" ++ specifier ++ "}:{}.{}.{}.{}", .{ | ||
| 355 | 0xFF, | ||
| 356 | 0xFF, | ||
| 357 | self.octets[12], | ||
| 358 | self.octets[13], | ||
| 359 | self.octets[14], | ||
| 360 | self.octets[15], | ||
| 361 | }); | ||
| 362 | } | ||
| 363 | |||
| 364 | const zero_span = span: { | ||
| 365 | var i: usize = 0; | ||
| 366 | while (i < self.octets.len) : (i += 2) { | ||
| 367 | if (self.octets[i] == 0 and self.octets[i + 1] == 0) break; | ||
| 368 | } else break :span .{ .from = 0, .to = 0 }; | ||
| 369 | |||
| 370 | const from = i; | ||
| 371 | |||
| 372 | while (i < self.octets.len) : (i += 2) { | ||
| 373 | if (self.octets[i] != 0 or self.octets[i + 1] != 0) break; | ||
| 374 | } | ||
| 375 | |||
| 376 | break :span .{ .from = from, .to = i }; | ||
| 377 | }; | ||
| 378 | |||
| 379 | var i: usize = 0; | ||
| 380 | while (i != 16) : (i += 2) { | ||
| 381 | if (zero_span.from != zero_span.to and i == zero_span.from) { | ||
| 382 | try writer.writeAll("::"); | ||
| 383 | } else if (i >= zero_span.from and i < zero_span.to) {} else { | ||
| 384 | if (i != 0 and i != zero_span.to) try writer.writeAll(":"); | ||
| 385 | |||
| 386 | const val = @as(u16, self.octets[i]) << 8 | self.octets[i + 1]; | ||
| 387 | try fmt.formatIntValue(val, specifier, .{}, writer); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | if (self.scope_id != no_scope_id and self.scope_id != 0) { | ||
| 392 | try fmt.format(writer, "%{d}", .{self.scope_id}); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | /// Set of possible errors that may encountered when parsing an IPv6 | ||
| 397 | /// address. | ||
| 398 | pub const ParseError = error{ | ||
| 399 | MalformedV4Mapping, | ||
| 400 | BadScopeID, | ||
| 401 | } || IPv4.ParseError; | ||
| 402 | |||
| 403 | /// Parses an arbitrary IPv6 address, including link-local addresses. | ||
| 404 | pub fn parse(buf: []const u8) ParseError!IPv6 { | ||
| 405 | if (mem.lastIndexOfScalar(u8, buf, '%')) |index| { | ||
| 406 | const ip_slice = buf[0..index]; | ||
| 407 | const scope_id_slice = buf[index + 1 ..]; | ||
| 408 | |||
| 409 | if (scope_id_slice.len == 0) return error.BadScopeID; | ||
| 410 | |||
| 411 | const scope_id: u32 = switch (scope_id_slice[0]) { | ||
| 412 | '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), | ||
| 413 | else => resolveScopeID(scope_id_slice), | ||
| 414 | } catch return error.BadScopeID; | ||
| 415 | |||
| 416 | return parseWithScopeID(ip_slice, scope_id); | ||
| 417 | } | ||
| 418 | |||
| 419 | return parseWithScopeID(buf, no_scope_id); | ||
| 420 | } | ||
| 421 | |||
| 422 | /// Parses an IPv6 address with a pre-specified scope ID. Presumes | ||
| 423 | /// that the address is not a link-local address. | ||
| 424 | pub fn parseWithScopeID(buf: []const u8, scope_id: u32) ParseError!IPv6 { | ||
| 425 | var octets: [16]u8 = undefined; | ||
| 426 | var octet: u16 = 0; | ||
| 427 | var tail: [16]u8 = undefined; | ||
| 428 | |||
| 429 | var out: []u8 = &octets; | ||
| 430 | var index: u8 = 0; | ||
| 431 | |||
| 432 | var saw_any_digits: bool = false; | ||
| 433 | var abbrv: bool = false; | ||
| 434 | |||
| 435 | for (buf) |c, i| { | ||
| 436 | switch (c) { | ||
| 437 | ':' => { | ||
| 438 | if (!saw_any_digits) { | ||
| 439 | if (abbrv) return error.UnexpectedToken; | ||
| 440 | if (i != 0) abbrv = true; | ||
| 441 | mem.set(u8, out[index..], 0); | ||
| 442 | out = &tail; | ||
| 443 | index = 0; | ||
| 444 | continue; | ||
| 445 | } | ||
| 446 | if (index == 14) return error.TooManyOctets; | ||
| 447 | |||
| 448 | out[index] = @truncate(u8, octet >> 8); | ||
| 449 | index += 1; | ||
| 450 | out[index] = @truncate(u8, octet); | ||
| 451 | index += 1; | ||
| 452 | |||
| 453 | octet = 0; | ||
| 454 | saw_any_digits = false; | ||
| 455 | }, | ||
| 456 | '.' => { | ||
| 457 | if (!abbrv or out[0] != 0xFF and out[1] != 0xFF) { | ||
| 458 | return error.MalformedV4Mapping; | ||
| 459 | } | ||
| 460 | const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1; | ||
| 461 | const v4 = try IPv4.parse(buf[start_index..]); | ||
| 462 | octets[10] = 0xFF; | ||
| 463 | octets[11] = 0xFF; | ||
| 464 | mem.copy(u8, octets[12..], &v4.octets); | ||
| 465 | |||
| 466 | return IPv6{ .octets = octets, .scope_id = scope_id }; | ||
| 467 | }, | ||
| 468 | else => { | ||
| 469 | saw_any_digits = true; | ||
| 470 | const digit = fmt.charToDigit(c, 16) catch return error.UnexpectedToken; | ||
| 471 | octet = math.mul(u16, octet, 16) catch return error.OctetOverflow; | ||
| 472 | octet = math.add(u16, octet, digit) catch return error.OctetOverflow; | ||
| 473 | }, | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | if (!saw_any_digits and !abbrv) { | ||
| 478 | return error.IncompleteAddress; | ||
| 479 | } | ||
| 480 | |||
| 481 | if (index == 14) { | ||
| 482 | out[14] = @truncate(u8, octet >> 8); | ||
| 483 | out[15] = @truncate(u8, octet); | ||
| 484 | } else { | ||
| 485 | out[index] = @truncate(u8, octet >> 8); | ||
| 486 | index += 1; | ||
| 487 | out[index] = @truncate(u8, octet); | ||
| 488 | index += 1; | ||
| 489 | mem.copy(u8, octets[16 - index ..], out[0..index]); | ||
| 490 | } | ||
| 491 | |||
| 492 | return IPv6{ .octets = octets, .scope_id = scope_id }; | ||
| 493 | } | ||
| 494 | }; | ||
| 495 | |||
| 496 | test { | ||
| 497 | testing.refAllDecls(@This()); | ||
| 498 | } | ||
| 499 | |||
| 500 | test "ip: convert to and from ipv6" { | ||
| 501 | try testing.expectFmt("::7f00:1", "{}", .{IPv4.localhost.toIPv6()}); | ||
| 502 | testing.expect(!IPv4.localhost.toIPv6().mapsToIPv4()); | ||
| 503 | |||
| 504 | try testing.expectFmt("::ffff:127.0.0.1", "{}", .{IPv4.localhost.mapToIPv6()}); | ||
| 505 | testing.expect(IPv4.localhost.mapToIPv6().mapsToIPv4()); | ||
| 506 | |||
| 507 | testing.expect(IPv4.localhost.toIPv6().toIPv4() == null); | ||
| 508 | try testing.expectFmt("127.0.0.1", "{}", .{IPv4.localhost.mapToIPv6().toIPv4()}); | ||
| 509 | } | ||
| 510 | |||
| 511 | test "ipv4: parse & format" { | ||
| 512 | const cases = [_][]const u8{ | ||
| 513 | "0.0.0.0", | ||
| 514 | "255.255.255.255", | ||
| 515 | "1.2.3.4", | ||
| 516 | "123.255.0.91", | ||
| 517 | "127.0.0.1", | ||
| 518 | }; | ||
| 519 | |||
| 520 | for (cases) |case| { | ||
| 521 | try testing.expectFmt(case, "{}", .{try IPv4.parse(case)}); | ||
| 522 | } | ||
| 523 | } | ||
| 524 | |||
| 525 | test "ipv6: parse & format" { | ||
| 526 | const inputs = [_][]const u8{ | ||
| 527 | "FF01:0:0:0:0:0:0:FB", | ||
| 528 | "FF01::Fb", | ||
| 529 | "::1", | ||
| 530 | "::", | ||
| 531 | "2001:db8::", | ||
| 532 | "::1234:5678", | ||
| 533 | "2001:db8::1234:5678", | ||
| 534 | "::ffff:123.5.123.5", | ||
| 535 | }; | ||
| 536 | |||
| 537 | const outputs = [_][]const u8{ | ||
| 538 | "ff01::fb", | ||
| 539 | "ff01::fb", | ||
| 540 | "::1", | ||
| 541 | "::", | ||
| 542 | "2001:db8::", | ||
| 543 | "::1234:5678", | ||
| 544 | "2001:db8::1234:5678", | ||
| 545 | "::ffff:123.5.123.5", | ||
| 546 | }; | ||
| 547 | |||
| 548 | for (inputs) |input, i| { | ||
| 549 | try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)}); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | test "ipv6: parse & format addresses with scope ids" { | ||
| 554 | if (!@hasDecl(os, "IFNAMESIZE")) return error.SkipZigTest; | ||
| 555 | |||
| 556 | const inputs = [_][]const u8{ | ||
| 557 | "FF01::FB%lo", | ||
| 558 | }; | ||
| 559 | |||
| 560 | const outputs = [_][]const u8{ | ||
| 561 | "ff01::fb%1", | ||
| 562 | }; | ||
| 563 | |||
| 564 | for (inputs) |input, i| { | ||
| 565 | try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)}); | ||
| 566 | } | ||
| 567 | } | ||
lib/std/x/os/os.zig deleted-9| ... | @@ -1,9 +0,0 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | |||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | pub const Socket = @import("Socket.zig"); | ||
| 6 | |||
| 7 | test { | ||
| 8 | testing.refAllDecls(@This()); | ||
| 9 | } | ||