| author | |
| committer | |
| log | 13068da43e8fbd0ae5d03aff27fb4e8802e1218c |
| tree | a5fd65eb86f80ac4b0d6495ac3b34784b45d0612 |
| parent | 2ab588049e41a96337a4fa8c2d9507320bc4278b |
Address comments from @ifreund and @MasterQ32 to address unsafeness and
ergonomics of the `Address` API.
Rename the `TCP` namespace to `tcp` as it does not contain any
top-level fields.
Fix missing reference to `sockaddr` which was identified by @kprotty in
os/bits/linux/arm64.zig.8 files changed, 534 insertions(+), 435 deletions(-)
lib/std/builtin.zig+1-17| ... | @@ -150,23 +150,7 @@ pub const Mode = enum { | ... | @@ -150,23 +150,7 @@ pub const Mode = enum { |
| 150 | 150 | ||
| 151 | /// This data structure is used by the Zig language code generation and | 151 | /// This data structure is used by the Zig language code generation and |
| 152 | /// therefore must be kept in sync with the compiler implementation. | 152 | /// therefore must be kept in sync with the compiler implementation. |
| 153 | pub const CallingConvention = enum { | 153 | pub const CallingConvention = enum { Unspecified, C, Naked, Async, Inline, Interrupt, Signal, Stdcall, Fastcall, Vectorcall, Thiscall, APCS, AAPCS, AAPCSVFP, SysV }; |
| 154 | Unspecified, | ||
| 155 | C, | ||
| 156 | Naked, | ||
| 157 | Async, | ||
| 158 | Inline, | ||
| 159 | Interrupt, | ||
| 160 | Signal, | ||
| 161 | Stdcall, | ||
| 162 | Fastcall, | ||
| 163 | Vectorcall, | ||
| 164 | Thiscall, | ||
| 165 | APCS, | ||
| 166 | AAPCS, | ||
| 167 | AAPCSVFP, | ||
| 168 | SysV | ||
| 169 | }; | ||
| 170 | 154 | ||
| 171 | /// This data structure is used by the Zig language code generation and | 155 | /// This data structure is used by the Zig language code generation and |
| 172 | /// therefore must be kept in sync with the compiler implementation. | 156 | /// therefore must be kept in sync with the compiler implementation. |
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/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/x.zig+15-1| ... | @@ -1,8 +1,22 @@ | ... | @@ -1,8 +1,22 @@ |
| 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 | |||
| 1 | pub const os = struct { | 9 | pub const os = struct { |
| 2 | pub const Socket = @import("x/os/Socket.zig"); | 10 | pub const Socket = @import("x/os/Socket.zig"); |
| 3 | pub usingnamespace @import("x/os/net.zig"); | 11 | pub usingnamespace @import("x/os/net.zig"); |
| 4 | }; | 12 | }; |
| 5 | 13 | ||
| 6 | pub const net = struct { | 14 | pub const net = struct { |
| 7 | pub const TCP = @import("x/net/TCP.zig"); | 15 | pub const tcp = @import("x/net/tcp.zig"); |
| 8 | }; | 16 | }; |
| 17 | |||
| 18 | test { | ||
| 19 | inline for (.{ os, net }) |module| { | ||
| 20 | std.testing.refAllDecls(module); | ||
| 21 | } | ||
| 22 | } |
lib/std/x/net/TCP.zig deleted-399| ... | @@ -1,399 +0,0 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | |||
| 3 | const os = std.os; | ||
| 4 | const fmt = std.fmt; | ||
| 5 | const mem = std.mem; | ||
| 6 | const testing = std.testing; | ||
| 7 | |||
| 8 | const IPv4 = std.x.os.IPv4; | ||
| 9 | const IPv6 = std.x.os.IPv6; | ||
| 10 | const Socket = std.x.os.Socket; | ||
| 11 | |||
| 12 | /// A generic TCP socket abstraction. | ||
| 13 | const TCP = @This(); | ||
| 14 | |||
| 15 | /// A TCP client-address pair. | ||
| 16 | pub const Connection = struct { | ||
| 17 | client: TCP.Client, | ||
| 18 | address: TCP.Address, | ||
| 19 | |||
| 20 | /// Enclose a TCP client and address into a client-address pair. | ||
| 21 | pub fn from(socket: Socket, address: TCP.Address) Connection { | ||
| 22 | return .{ .client = TCP.Client.from(socket), .address = address }; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Closes the underlying client of the connection. | ||
| 26 | pub fn deinit(self: TCP.Connection) void { | ||
| 27 | self.client.deinit(); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// Possible domains that a TCP client/listener may operate over. | ||
| 32 | pub const Domain = extern enum(u16) { | ||
| 33 | ip = os.AF_INET, | ||
| 34 | ipv6 = os.AF_INET6, | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// A TCP client. | ||
| 38 | pub const Client = struct { | ||
| 39 | socket: Socket, | ||
| 40 | |||
| 41 | /// Opens a new client. | ||
| 42 | pub fn init(domain: TCP.Domain, flags: u32) !Client { | ||
| 43 | return Client{ | ||
| 44 | .socket = try Socket.init( | ||
| 45 | @enumToInt(domain), | ||
| 46 | os.SOCK_STREAM | flags, | ||
| 47 | os.IPPROTO_TCP, | ||
| 48 | ), | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Enclose a TCP client over an existing socket. | ||
| 53 | pub fn from(socket: Socket) Client { | ||
| 54 | return Client{ .socket = socket }; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Closes the client. | ||
| 58 | pub fn deinit(self: Client) void { | ||
| 59 | self.socket.deinit(); | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Shutdown either the read side, write side, or all sides of the client's underlying socket. | ||
| 63 | pub fn shutdown(self: Client, how: os.ShutdownHow) !void { | ||
| 64 | return self.socket.shutdown(how); | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Have the client attempt to the connect to an address. | ||
| 68 | pub fn connect(self: Client, address: TCP.Address) !void { | ||
| 69 | return self.socket.connect(TCP.Address, address); | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Read data from the socket into the buffer provided. It returns the | ||
| 73 | /// number of bytes read into the buffer provided. | ||
| 74 | pub fn read(self: Client, buf: []u8) !usize { | ||
| 75 | return self.socket.read(buf); | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Read data from the socket into the buffer provided with a set of flags | ||
| 79 | /// specified. It returns the number of bytes read into the buffer provided. | ||
| 80 | pub fn recv(self: Client, buf: []u8, flags: u32) !usize { | ||
| 81 | return self.socket.recv(buf, flags); | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Write a buffer of data provided to the socket. It returns the number | ||
| 85 | /// of bytes that are written to the socket. | ||
| 86 | pub fn write(self: Client, buf: []const u8) !usize { | ||
| 87 | return self.socket.write(buf); | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Writes multiple I/O vectors to the socket. It returns the number | ||
| 91 | /// of bytes that are written to the socket. | ||
| 92 | pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { | ||
| 93 | return self.socket.writev(buffers); | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Write a buffer of data provided to the socket with a set of flags specified. | ||
| 97 | /// It returns the number of bytes that are written to the socket. | ||
| 98 | pub fn send(self: Client, buf: []const u8, flags: u32) !usize { | ||
| 99 | return self.socket.send(buf, flags); | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Writes multiple I/O vectors with a prepended message header to the socket | ||
| 103 | /// with a set of flags specified. It returns the number of bytes that are | ||
| 104 | /// written to the socket. | ||
| 105 | pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { | ||
| 106 | return self.socket.sendmsg(msg, flags); | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Query and return the latest cached error on the client's underlying socket. | ||
| 110 | pub fn getError(self: Client) !void { | ||
| 111 | return self.socket.getError(); | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Query the read buffer size of the client's underlying socket. | ||
| 115 | pub fn getReadBufferSize(self: Client) !u32 { | ||
| 116 | return self.socket.getReadBufferSize(); | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Query the write buffer size of the client's underlying socket. | ||
| 120 | pub fn getWriteBufferSize(self: Client) !u32 { | ||
| 121 | return self.socket.getWriteBufferSize(); | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Query the address that the client's socket is locally bounded to. | ||
| 125 | pub fn getLocalAddress(self: Client) !TCP.Address { | ||
| 126 | return self.socket.getLocalAddress(TCP.Address); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if | ||
| 130 | /// the host does not support sockets disabling Nagle's algorithm. | ||
| 131 | pub fn setNoDelay(self: Client, enabled: bool) !void { | ||
| 132 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | ||
| 133 | const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); | ||
| 134 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); | ||
| 135 | } | ||
| 136 | return error.UnsupportedSocketOption; | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Set the write buffer size of the socket. | ||
| 140 | pub fn setWriteBufferSize(self: Client, size: u32) !void { | ||
| 141 | return self.socket.setWriteBufferSize(size); | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Set the read buffer size of the socket. | ||
| 145 | pub fn setReadBufferSize(self: Client, size: u32) !void { | ||
| 146 | return self.socket.setReadBufferSize(size); | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Set a timeout on the socket that is to occur if no messages are successfully written | ||
| 150 | /// to its bound destination after a specified number of milliseconds. A subsequent write | ||
| 151 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 152 | pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { | ||
| 153 | return self.socket.setWriteTimeout(milliseconds); | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Set a timeout on the socket that is to occur if no messages are successfully read | ||
| 157 | /// from its bound destination after a specified number of milliseconds. A subsequent | ||
| 158 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | ||
| 159 | /// exceeded. | ||
| 160 | pub fn setReadTimeout(self: Client, milliseconds: usize) !void { | ||
| 161 | return self.socket.setReadTimeout(milliseconds); | ||
| 162 | } | ||
| 163 | }; | ||
| 164 | |||
| 165 | /// A TCP listener. | ||
| 166 | pub const Listener = struct { | ||
| 167 | socket: Socket, | ||
| 168 | |||
| 169 | /// Opens a new listener. | ||
| 170 | pub fn init(domain: TCP.Domain, flags: u32) !Listener { | ||
| 171 | return Listener{ | ||
| 172 | .socket = try Socket.init( | ||
| 173 | @enumToInt(domain), | ||
| 174 | os.SOCK_STREAM | flags, | ||
| 175 | os.IPPROTO_TCP, | ||
| 176 | ), | ||
| 177 | }; | ||
| 178 | } | ||
| 179 | |||
| 180 | /// Closes the listener. | ||
| 181 | pub fn deinit(self: Listener) void { | ||
| 182 | self.socket.deinit(); | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Shuts down the underlying listener's socket. The next subsequent call, or | ||
| 186 | /// a current pending call to accept() after shutdown is called will return | ||
| 187 | /// an error. | ||
| 188 | pub fn shutdown(self: Listener) !void { | ||
| 189 | return self.socket.shutdown(.recv); | ||
| 190 | } | ||
| 191 | |||
| 192 | /// Binds the listener's socket to an address. | ||
| 193 | pub fn bind(self: Listener, address: TCP.Address) !void { | ||
| 194 | return self.socket.bind(TCP.Address, address); | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Start listening for incoming connections. | ||
| 198 | pub fn listen(self: Listener, max_backlog_size: u31) !void { | ||
| 199 | return self.socket.listen(max_backlog_size); | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Accept a pending incoming connection queued to the kernel backlog | ||
| 203 | /// of the listener's socket. | ||
| 204 | pub fn accept(self: Listener, flags: u32) !TCP.Connection { | ||
| 205 | return self.socket.accept(TCP.Connection, TCP.Address, flags); | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Query and return the latest cached error on the listener's underlying socket. | ||
| 209 | pub fn getError(self: Client) !void { | ||
| 210 | return self.socket.getError(); | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Query the address that the listener's socket is locally bounded to. | ||
| 214 | pub fn getLocalAddress(self: Listener) !TCP.Address { | ||
| 215 | return self.socket.getLocalAddress(TCP.Address); | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | ||
| 219 | /// the host does not support sockets listening the same address. | ||
| 220 | pub fn setReuseAddress(self: Listener, enabled: bool) !void { | ||
| 221 | return self.socket.setReuseAddress(enabled); | ||
| 222 | } | ||
| 223 | |||
| 224 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | ||
| 225 | /// the host does not supports sockets listening on the same port. | ||
| 226 | pub fn setReusePort(self: Listener, enabled: bool) !void { | ||
| 227 | return self.socket.setReusePort(enabled); | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | ||
| 231 | /// support TCP Fast Open. | ||
| 232 | pub fn setFastOpen(self: Listener, enabled: bool) !void { | ||
| 233 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | ||
| 234 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 235 | } | ||
| 236 | return error.UnsupportedSocketOption; | ||
| 237 | } | ||
| 238 | |||
| 239 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | ||
| 240 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | ||
| 241 | pub fn setQuickACK(self: Listener, enabled: bool) !void { | ||
| 242 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | ||
| 243 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 244 | } | ||
| 245 | return error.UnsupportedSocketOption; | ||
| 246 | } | ||
| 247 | |||
| 248 | /// Set a timeout on the listener that is to occur if no new incoming connections come in | ||
| 249 | /// after a specified number of milliseconds. A subsequent accept call to the listener | ||
| 250 | /// will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 251 | pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { | ||
| 252 | return self.socket.setReadTimeout(milliseconds); | ||
| 253 | } | ||
| 254 | }; | ||
| 255 | |||
| 256 | /// A TCP socket address designated by a host IP and port. A TCP socket | ||
| 257 | /// address comprises of 28 bytes. It may freely be used in place of | ||
| 258 | /// `sockaddr` when working with socket syscalls. | ||
| 259 | /// | ||
| 260 | /// It is not recommended to touch the fields of an `Address`, but to | ||
| 261 | /// instead make use of its available accessor methods. | ||
| 262 | pub const Address = extern struct { | ||
| 263 | family: u16, | ||
| 264 | port: u16, | ||
| 265 | host: extern union { | ||
| 266 | ipv4: extern struct { | ||
| 267 | address: IPv4, | ||
| 268 | }, | ||
| 269 | ipv6: extern struct { | ||
| 270 | flow_info: u32 = 0, | ||
| 271 | address: IPv6, | ||
| 272 | }, | ||
| 273 | }, | ||
| 274 | |||
| 275 | /// Instantiate a new TCP address with a IPv4 host and port. | ||
| 276 | pub fn initIPv4(host: IPv4, port: u16) Address { | ||
| 277 | return Address{ | ||
| 278 | .family = os.AF_INET, | ||
| 279 | .port = mem.nativeToBig(u16, port), | ||
| 280 | .host = .{ | ||
| 281 | .ipv4 = .{ | ||
| 282 | .address = host, | ||
| 283 | }, | ||
| 284 | }, | ||
| 285 | }; | ||
| 286 | } | ||
| 287 | |||
| 288 | /// Instantiate a new TCP address with a IPv6 host and port. | ||
| 289 | pub fn initIPv6(host: IPv6, port: u16) Address { | ||
| 290 | return Address{ | ||
| 291 | .family = os.AF_INET6, | ||
| 292 | .port = mem.nativeToBig(u16, port), | ||
| 293 | .host = .{ | ||
| 294 | .ipv6 = .{ | ||
| 295 | .address = host, | ||
| 296 | }, | ||
| 297 | }, | ||
| 298 | }; | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Extract the host of the address. | ||
| 302 | pub fn getHost(self: Address) union(enum) { v4: IPv4, v6: IPv6 } { | ||
| 303 | return switch (self.family) { | ||
| 304 | os.AF_INET => .{ .v4 = self.host.ipv4.address }, | ||
| 305 | os.AF_INET6 => .{ .v6 = self.host.ipv6.address }, | ||
| 306 | else => unreachable, | ||
| 307 | }; | ||
| 308 | } | ||
| 309 | |||
| 310 | /// Extract the port of the address. | ||
| 311 | pub fn getPort(self: Address) u16 { | ||
| 312 | return mem.nativeToBig(u16, self.port); | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Set the port of the address. | ||
| 316 | pub fn setPort(self: *Address, port: u16) void { | ||
| 317 | self.port = mem.nativeToBig(u16, port); | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Implements the `std.fmt.format` API. | ||
| 321 | pub fn format( | ||
| 322 | self: Address, | ||
| 323 | comptime layout: []const u8, | ||
| 324 | opts: fmt.FormatOptions, | ||
| 325 | writer: anytype, | ||
| 326 | ) !void { | ||
| 327 | switch (self.getHost()) { | ||
| 328 | .v4 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }), | ||
| 329 | .v6 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }), | ||
| 330 | } | ||
| 331 | } | ||
| 332 | }; | ||
| 333 | |||
| 334 | test { | ||
| 335 | testing.refAllDecls(@This()); | ||
| 336 | } | ||
| 337 | |||
| 338 | test "tcp: create non-blocking pair" { | ||
| 339 | const a = try TCP.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 340 | defer a.deinit(); | ||
| 341 | |||
| 342 | try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 343 | try a.listen(128); | ||
| 344 | |||
| 345 | const binded_address = try a.getLocalAddress(); | ||
| 346 | |||
| 347 | const b = try TCP.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 348 | defer b.deinit(); | ||
| 349 | |||
| 350 | testing.expectError(error.WouldBlock, b.connect(binded_address)); | ||
| 351 | try b.getError(); | ||
| 352 | |||
| 353 | const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 354 | defer ab.deinit(); | ||
| 355 | } | ||
| 356 | |||
| 357 | test "tcp/client: set read timeout of 1 millisecond on blocking client" { | ||
| 358 | const a = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 359 | defer a.deinit(); | ||
| 360 | |||
| 361 | try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 362 | try a.listen(128); | ||
| 363 | |||
| 364 | const binded_address = try a.getLocalAddress(); | ||
| 365 | |||
| 366 | const b = try TCP.Client.init(.ip, os.SOCK_CLOEXEC); | ||
| 367 | defer b.deinit(); | ||
| 368 | |||
| 369 | try b.connect(binded_address); | ||
| 370 | try b.setReadTimeout(1); | ||
| 371 | |||
| 372 | const ab = try a.accept(os.SOCK_CLOEXEC); | ||
| 373 | defer ab.deinit(); | ||
| 374 | |||
| 375 | var buf: [1]u8 = undefined; | ||
| 376 | testing.expectError(error.WouldBlock, b.read(&buf)); | ||
| 377 | } | ||
| 378 | |||
| 379 | test "tcp/listener: bind to unspecified ipv4 address" { | ||
| 380 | const socket = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 381 | defer socket.deinit(); | ||
| 382 | |||
| 383 | try socket.bind(TCP.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 384 | try socket.listen(128); | ||
| 385 | |||
| 386 | const address = try socket.getLocalAddress(); | ||
| 387 | testing.expect(address.getHost() == .v4); | ||
| 388 | } | ||
| 389 | |||
| 390 | test "tcp/listener: bind to unspecified ipv6 address" { | ||
| 391 | const socket = try TCP.Listener.init(.ipv6, os.SOCK_CLOEXEC); | ||
| 392 | defer socket.deinit(); | ||
| 393 | |||
| 394 | try socket.bind(TCP.Address.initIPv6(IPv6.unspecified, 0)); | ||
| 395 | try socket.listen(128); | ||
| 396 | |||
| 397 | const address = try socket.getLocalAddress(); | ||
| 398 | testing.expect(address.getHost() == .v6); | ||
| 399 | } | ||
lib/std/x/net/tcp.zig created+383| ... | @@ -0,0 +1,383 @@ | ||
| 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 testing = std.testing; | ||
| 13 | |||
| 14 | const IPv4 = std.x.os.IPv4; | ||
| 15 | const IPv6 = std.x.os.IPv6; | ||
| 16 | const Socket = std.x.os.Socket; | ||
| 17 | |||
| 18 | /// A generic TCP socket abstraction. | ||
| 19 | const tcp = @This(); | ||
| 20 | |||
| 21 | /// A union of all eligible types of socket addresses over TCP. | ||
| 22 | pub const Address = union(enum) { | ||
| 23 | ipv4: IPv4.Address, | ||
| 24 | ipv6: IPv6.Address, | ||
| 25 | |||
| 26 | /// Instantiate a new address with a IPv4 host and port. | ||
| 27 | pub fn initIPv4(host: IPv4, port: u16) Address { | ||
| 28 | return .{ .ipv4 = .{ .host = host, .port = port } }; | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Instantiate a new address with a IPv6 host and port. | ||
| 32 | pub fn initIPv6(host: IPv6, port: u16) Address { | ||
| 33 | return .{ .ipv6 = .{ .host = host, .port = port } }; | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Re-interpret a generic socket address into a TCP socket address. | ||
| 37 | pub fn from(address: Socket.Address) tcp.Address { | ||
| 38 | return switch (address) { | ||
| 39 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | ||
| 40 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Re-interpret a TCP socket address into a generic socket address. | ||
| 45 | pub fn into(self: tcp.Address) Socket.Address { | ||
| 46 | return switch (self) { | ||
| 47 | .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address }, | ||
| 48 | .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address }, | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Implements the `std.fmt.format` API. | ||
| 53 | pub fn format( | ||
| 54 | self: tcp.Address, | ||
| 55 | comptime layout: []const u8, | ||
| 56 | opts: fmt.FormatOptions, | ||
| 57 | writer: anytype, | ||
| 58 | ) !void { | ||
| 59 | switch (self) { | ||
| 60 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 61 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | ||
| 62 | } | ||
| 63 | } | ||
| 64 | }; | ||
| 65 | |||
| 66 | /// A TCP client-address pair. | ||
| 67 | pub const Connection = struct { | ||
| 68 | client: tcp.Client, | ||
| 69 | address: tcp.Address, | ||
| 70 | |||
| 71 | /// Enclose a TCP client and address into a client-address pair. | ||
| 72 | pub fn from(conn: Socket.Connection) tcp.Connection { | ||
| 73 | return .{ | ||
| 74 | .client = tcp.Client.from(conn.socket), | ||
| 75 | .address = tcp.Address.from(conn.address), | ||
| 76 | }; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Unravel a TCP client-address pair into a socket-address pair. | ||
| 80 | pub fn into(self: tcp.Connection) Socket.Connection { | ||
| 81 | return .{ | ||
| 82 | .socket = self.client.socket, | ||
| 83 | .address = self.address.into(), | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Closes the underlying client of the connection. | ||
| 88 | pub fn deinit(self: tcp.Connection) void { | ||
| 89 | self.client.deinit(); | ||
| 90 | } | ||
| 91 | }; | ||
| 92 | |||
| 93 | /// Possible domains that a TCP client/listener may operate over. | ||
| 94 | pub const Domain = extern enum(u16) { | ||
| 95 | ip = os.AF_INET, | ||
| 96 | ipv6 = os.AF_INET6, | ||
| 97 | }; | ||
| 98 | |||
| 99 | /// A TCP client. | ||
| 100 | pub const Client = struct { | ||
| 101 | socket: Socket, | ||
| 102 | |||
| 103 | /// Opens a new client. | ||
| 104 | pub fn init(domain: tcp.Domain, flags: u32) !Client { | ||
| 105 | return Client{ | ||
| 106 | .socket = try Socket.init( | ||
| 107 | @enumToInt(domain), | ||
| 108 | os.SOCK_STREAM | flags, | ||
| 109 | os.IPPROTO_TCP, | ||
| 110 | ), | ||
| 111 | }; | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Enclose a TCP client over an existing socket. | ||
| 115 | pub fn from(socket: Socket) Client { | ||
| 116 | return Client{ .socket = socket }; | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Closes the client. | ||
| 120 | pub fn deinit(self: Client) void { | ||
| 121 | self.socket.deinit(); | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Shutdown either the read side, write side, or all sides of the client's underlying socket. | ||
| 125 | pub fn shutdown(self: Client, how: os.ShutdownHow) !void { | ||
| 126 | return self.socket.shutdown(how); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Have the client attempt to the connect to an address. | ||
| 130 | pub fn connect(self: Client, address: tcp.Address) !void { | ||
| 131 | return self.socket.connect(address.into()); | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Read data from the socket into the buffer provided. It returns the | ||
| 135 | /// number of bytes read into the buffer provided. | ||
| 136 | pub fn read(self: Client, buf: []u8) !usize { | ||
| 137 | return self.socket.read(buf); | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Read data from the socket into the buffer provided with a set of flags | ||
| 141 | /// specified. It returns the number of bytes read into the buffer provided. | ||
| 142 | pub fn recv(self: Client, buf: []u8, flags: u32) !usize { | ||
| 143 | return self.socket.recv(buf, flags); | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Write a buffer of data provided to the socket. It returns the number | ||
| 147 | /// of bytes that are written to the socket. | ||
| 148 | pub fn write(self: Client, buf: []const u8) !usize { | ||
| 149 | return self.socket.write(buf); | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Writes multiple I/O vectors to the socket. It returns the number | ||
| 153 | /// of bytes that are written to the socket. | ||
| 154 | pub fn writev(self: Client, buffers: []const os.iovec_const) !usize { | ||
| 155 | return self.socket.writev(buffers); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Write a buffer of data provided to the socket with a set of flags specified. | ||
| 159 | /// It returns the number of bytes that are written to the socket. | ||
| 160 | pub fn send(self: Client, buf: []const u8, flags: u32) !usize { | ||
| 161 | return self.socket.send(buf, flags); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Writes multiple I/O vectors with a prepended message header to the socket | ||
| 165 | /// with a set of flags specified. It returns the number of bytes that are | ||
| 166 | /// written to the socket. | ||
| 167 | pub fn sendmsg(self: Client, msg: os.msghdr_const, flags: u32) !usize { | ||
| 168 | return self.socket.sendmsg(msg, flags); | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Query and return the latest cached error on the client's underlying socket. | ||
| 172 | pub fn getError(self: Client) !void { | ||
| 173 | return self.socket.getError(); | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Query the read buffer size of the client's underlying socket. | ||
| 177 | pub fn getReadBufferSize(self: Client) !u32 { | ||
| 178 | return self.socket.getReadBufferSize(); | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Query the write buffer size of the client's underlying socket. | ||
| 182 | pub fn getWriteBufferSize(self: Client) !u32 { | ||
| 183 | return self.socket.getWriteBufferSize(); | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Query the address that the client's socket is locally bounded to. | ||
| 187 | pub fn getLocalAddress(self: Client) !tcp.Address { | ||
| 188 | return tcp.Address.from(try self.socket.getLocalAddress()); | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if | ||
| 192 | /// the host does not support sockets disabling Nagle's algorithm. | ||
| 193 | pub fn setNoDelay(self: Client, enabled: bool) !void { | ||
| 194 | if (comptime @hasDecl(os, "TCP_NODELAY")) { | ||
| 195 | const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled))); | ||
| 196 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_NODELAY, bytes); | ||
| 197 | } | ||
| 198 | return error.UnsupportedSocketOption; | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Set the write buffer size of the socket. | ||
| 202 | pub fn setWriteBufferSize(self: Client, size: u32) !void { | ||
| 203 | return self.socket.setWriteBufferSize(size); | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Set the read buffer size of the socket. | ||
| 207 | pub fn setReadBufferSize(self: Client, size: u32) !void { | ||
| 208 | return self.socket.setReadBufferSize(size); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Set a timeout on the socket that is to occur if no messages are successfully written | ||
| 212 | /// to its bound destination after a specified number of milliseconds. A subsequent write | ||
| 213 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 214 | pub fn setWriteTimeout(self: Client, milliseconds: usize) !void { | ||
| 215 | return self.socket.setWriteTimeout(milliseconds); | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Set a timeout on the socket that is to occur if no messages are successfully read | ||
| 219 | /// from its bound destination after a specified number of milliseconds. A subsequent | ||
| 220 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | ||
| 221 | /// exceeded. | ||
| 222 | pub fn setReadTimeout(self: Client, milliseconds: usize) !void { | ||
| 223 | return self.socket.setReadTimeout(milliseconds); | ||
| 224 | } | ||
| 225 | }; | ||
| 226 | |||
| 227 | /// A TCP listener. | ||
| 228 | pub const Listener = struct { | ||
| 229 | socket: Socket, | ||
| 230 | |||
| 231 | /// Opens a new listener. | ||
| 232 | pub fn init(domain: tcp.Domain, flags: u32) !Listener { | ||
| 233 | return Listener{ | ||
| 234 | .socket = try Socket.init( | ||
| 235 | @enumToInt(domain), | ||
| 236 | os.SOCK_STREAM | flags, | ||
| 237 | os.IPPROTO_TCP, | ||
| 238 | ), | ||
| 239 | }; | ||
| 240 | } | ||
| 241 | |||
| 242 | /// Closes the listener. | ||
| 243 | pub fn deinit(self: Listener) void { | ||
| 244 | self.socket.deinit(); | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Shuts down the underlying listener's socket. The next subsequent call, or | ||
| 248 | /// a current pending call to accept() after shutdown is called will return | ||
| 249 | /// an error. | ||
| 250 | pub fn shutdown(self: Listener) !void { | ||
| 251 | return self.socket.shutdown(.recv); | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Binds the listener's socket to an address. | ||
| 255 | pub fn bind(self: Listener, address: tcp.Address) !void { | ||
| 256 | return self.socket.bind(address.into()); | ||
| 257 | } | ||
| 258 | |||
| 259 | /// Start listening for incoming connections. | ||
| 260 | pub fn listen(self: Listener, max_backlog_size: u31) !void { | ||
| 261 | return self.socket.listen(max_backlog_size); | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Accept a pending incoming connection queued to the kernel backlog | ||
| 265 | /// of the listener's socket. | ||
| 266 | pub fn accept(self: Listener, flags: u32) !tcp.Connection { | ||
| 267 | return tcp.Connection.from(try self.socket.accept(flags)); | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Query and return the latest cached error on the listener's underlying socket. | ||
| 271 | pub fn getError(self: Client) !void { | ||
| 272 | return self.socket.getError(); | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Query the address that the listener's socket is locally bounded to. | ||
| 276 | pub fn getLocalAddress(self: Listener) !tcp.Address { | ||
| 277 | return tcp.Address.from(try self.socket.getLocalAddress()); | ||
| 278 | } | ||
| 279 | |||
| 280 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | ||
| 281 | /// the host does not support sockets listening the same address. | ||
| 282 | pub fn setReuseAddress(self: Listener, enabled: bool) !void { | ||
| 283 | return self.socket.setReuseAddress(enabled); | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | ||
| 287 | /// the host does not supports sockets listening on the same port. | ||
| 288 | pub fn setReusePort(self: Listener, enabled: bool) !void { | ||
| 289 | return self.socket.setReusePort(enabled); | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not | ||
| 293 | /// support TCP Fast Open. | ||
| 294 | pub fn setFastOpen(self: Listener, enabled: bool) !void { | ||
| 295 | if (comptime @hasDecl(os, "TCP_FASTOPEN")) { | ||
| 296 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 297 | } | ||
| 298 | return error.UnsupportedSocketOption; | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns | ||
| 302 | /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK. | ||
| 303 | pub fn setQuickACK(self: Listener, enabled: bool) !void { | ||
| 304 | if (comptime @hasDecl(os, "TCP_QUICKACK")) { | ||
| 305 | return os.setsockopt(self.socket.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled)))); | ||
| 306 | } | ||
| 307 | return error.UnsupportedSocketOption; | ||
| 308 | } | ||
| 309 | |||
| 310 | /// Set a timeout on the listener that is to occur if no new incoming connections come in | ||
| 311 | /// after a specified number of milliseconds. A subsequent accept call to the listener | ||
| 312 | /// will thereafter return `error.WouldBlock` should the timeout be exceeded. | ||
| 313 | pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void { | ||
| 314 | return self.socket.setReadTimeout(milliseconds); | ||
| 315 | } | ||
| 316 | }; | ||
| 317 | |||
| 318 | test { | ||
| 319 | testing.refAllDecls(@This()); | ||
| 320 | } | ||
| 321 | |||
| 322 | test "tcp: create non-blocking pair" { | ||
| 323 | const listener = try tcp.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 324 | defer listener.deinit(); | ||
| 325 | |||
| 326 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 327 | try listener.listen(128); | ||
| 328 | |||
| 329 | const binded_address = try listener.getLocalAddress(); | ||
| 330 | |||
| 331 | const client = try tcp.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 332 | defer client.deinit(); | ||
| 333 | |||
| 334 | testing.expectError(error.WouldBlock, client.connect(binded_address)); | ||
| 335 | try client.getError(); | ||
| 336 | |||
| 337 | const conn = try listener.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC); | ||
| 338 | defer conn.deinit(); | ||
| 339 | } | ||
| 340 | |||
| 341 | test "tcp/client: set read timeout of 1 millisecond on blocking client" { | ||
| 342 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 343 | defer listener.deinit(); | ||
| 344 | |||
| 345 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 346 | try listener.listen(128); | ||
| 347 | |||
| 348 | const binded_address = try listener.getLocalAddress(); | ||
| 349 | |||
| 350 | const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); | ||
| 351 | defer client.deinit(); | ||
| 352 | |||
| 353 | try client.connect(binded_address); | ||
| 354 | try client.setReadTimeout(1); | ||
| 355 | |||
| 356 | const conn = try listener.accept(os.SOCK_CLOEXEC); | ||
| 357 | defer conn.deinit(); | ||
| 358 | |||
| 359 | var buf: [1]u8 = undefined; | ||
| 360 | testing.expectError(error.WouldBlock, client.read(&buf)); | ||
| 361 | } | ||
| 362 | |||
| 363 | test "tcp/listener: bind to unspecified ipv4 address" { | ||
| 364 | const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); | ||
| 365 | defer listener.deinit(); | ||
| 366 | |||
| 367 | try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0)); | ||
| 368 | try listener.listen(128); | ||
| 369 | |||
| 370 | const address = try listener.getLocalAddress(); | ||
| 371 | testing.expect(address == .ipv4); | ||
| 372 | } | ||
| 373 | |||
| 374 | test "tcp/listener: bind to unspecified ipv6 address" { | ||
| 375 | const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC); | ||
| 376 | defer listener.deinit(); | ||
| 377 | |||
| 378 | try listener.bind(tcp.Address.initIPv6(IPv6.unspecified, 0)); | ||
| 379 | try listener.listen(128); | ||
| 380 | |||
| 381 | const address = try listener.getLocalAddress(); | ||
| 382 | testing.expect(address == .ipv6); | ||
| 383 | } | ||
lib/std/x/os/Socket.zig+114-14| ... | @@ -1,4 +1,11 @@ | ... | @@ -1,4 +1,11 @@ |
| 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; |
| ... | @@ -7,6 +14,98 @@ const time = std.time; | ... | @@ -7,6 +14,98 @@ const time = std.time; |
| 7 | /// A generic socket abstraction. | 14 | /// A generic socket abstraction. |
| 8 | const Socket = @This(); | 15 | const Socket = @This(); |
| 9 | 16 | ||
| 17 | /// A socket-address pair. | ||
| 18 | pub const Connection = struct { | ||
| 19 | socket: Socket, | ||
| 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 | } | ||
| 107 | }; | ||
| 108 | |||
| 10 | /// The underlying handle of a socket. | 109 | /// The underlying handle of a socket. |
| 11 | fd: os.socket_t, | 110 | fd: os.socket_t, |
| 12 | 111 | ||
| ... | @@ -31,8 +130,8 @@ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | ... | @@ -31,8 +130,8 @@ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { |
| 31 | } | 130 | } |
| 32 | 131 | ||
| 33 | /// Binds the socket to an address. | 132 | /// Binds the socket to an address. |
| 34 | pub fn bind(self: Socket, comptime Address: type, address: Address) !void { | 133 | pub fn bind(self: Socket, address: Socket.Address) !void { |
| 35 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address)); | 134 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); |
| 36 | } | 135 | } |
| 37 | 136 | ||
| 38 | /// Start listening for incoming connections on the socket. | 137 | /// Start listening for incoming connections on the socket. |
| ... | @@ -41,19 +140,20 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void { | ... | @@ -41,19 +140,20 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void { |
| 41 | } | 140 | } |
| 42 | 141 | ||
| 43 | /// Have the socket attempt to the connect to an address. | 142 | /// Have the socket attempt to the connect to an address. |
| 44 | pub fn connect(self: Socket, comptime Address: type, address: Address) !void { | 143 | pub fn connect(self: Socket, address: Socket.Address) !void { |
| 45 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address)); | 144 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); |
| 46 | } | 145 | } |
| 47 | 146 | ||
| 48 | /// Accept a pending incoming connection queued to the kernel backlog | 147 | /// Accept a pending incoming connection queued to the kernel backlog |
| 49 | /// of the socket. | 148 | /// of the socket. |
| 50 | pub fn accept(self: Socket, comptime Connection: type, comptime Address: type, flags: u32) !Connection { | 149 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { |
| 51 | var address: Address = undefined; | 150 | var address: os.sockaddr = undefined; |
| 52 | var address_len: u32 = @sizeOf(Address); | 151 | var address_len: u32 = @sizeOf(os.sockaddr); |
| 53 | 152 | ||
| 54 | const fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &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)); | ||
| 55 | 155 | ||
| 56 | return Connection.from(.{ .fd = fd }, address); | 156 | return Socket.Connection.from(socket, socket_address); |
| 57 | } | 157 | } |
| 58 | 158 | ||
| 59 | /// 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 |
| ... | @@ -94,11 +194,11 @@ pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { | ... | @@ -94,11 +194,11 @@ pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize { |
| 94 | } | 194 | } |
| 95 | 195 | ||
| 96 | /// Query the address that the socket is locally bounded to. | 196 | /// Query the address that the socket is locally bounded to. |
| 97 | pub fn getLocalAddress(self: Socket, comptime Address: type) !Address { | 197 | pub fn getLocalAddress(self: Socket) !Socket.Address { |
| 98 | var address: Address = undefined; | 198 | var address: os.sockaddr = undefined; |
| 99 | var address_len: u32 = @sizeOf(Address); | 199 | var address_len: u32 = @sizeOf(os.sockaddr); |
| 100 | try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | 200 | try os.getsockname(self.fd, &address, &address_len); |
| 101 | return address; | 201 | return Socket.Address.fromNative(@alignCast(4, &address)); |
| 102 | } | 202 | } |
| 103 | 203 | ||
| 104 | /// Query and return the latest cached error on the socket. | 204 | /// Query and return the latest cached error on the socket. |
lib/std/x/os/net.zig+18| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 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"); |
| 2 | 8 | ||
| 3 | const os = std.os; | 9 | const os = std.os; |
| ... | @@ -27,6 +33,12 @@ pub fn resolveScopeID(name: []const u8) !u32 { | ... | @@ -27,6 +33,12 @@ pub fn resolveScopeID(name: []const u8) !u32 { |
| 27 | 33 | ||
| 28 | /// An IPv4 address comprised of 4 bytes. | 34 | /// An IPv4 address comprised of 4 bytes. |
| 29 | pub const IPv4 = extern struct { | 35 | pub const IPv4 = extern struct { |
| 36 | /// A IPv4 host-port pair. | ||
| 37 | pub const Address = extern struct { | ||
| 38 | host: IPv4, | ||
| 39 | port: u16, | ||
| 40 | }; | ||
| 41 | |||
| 30 | /// Octets of a IPv4 address designating the local host. | 42 | /// Octets of a IPv4 address designating the local host. |
| 31 | pub const localhost_octets = [_]u8{ 127, 0, 0, 1 }; | 43 | pub const localhost_octets = [_]u8{ 127, 0, 0, 1 }; |
| 32 | 44 | ||
| ... | @@ -200,6 +212,12 @@ pub const IPv4 = extern struct { | ... | @@ -200,6 +212,12 @@ pub const IPv4 = extern struct { |
| 200 | /// An IPv6 address comprised of 16 bytes for an address, and 4 bytes | 212 | /// An IPv6 address comprised of 16 bytes for an address, and 4 bytes |
| 201 | /// for a scope ID; cumulatively summing to 20 bytes in total. | 213 | /// for a scope ID; cumulatively summing to 20 bytes in total. |
| 202 | pub const IPv6 = extern struct { | 214 | pub const IPv6 = extern struct { |
| 215 | /// A IPv6 host-port pair. | ||
| 216 | pub const Address = extern struct { | ||
| 217 | host: IPv6, | ||
| 218 | port: u16, | ||
| 219 | }; | ||
| 220 | |||
| 203 | /// Octets of a IPv6 address designating the local host. | 221 | /// Octets of a IPv6 address designating the local host. |
| 204 | pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01}; | 222 | pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01}; |
| 205 | 223 |