mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 22:33:08 +00:00
x/os, x/net: re-approach Address, rename namespace TCP -> tcp
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.
This commit is contained in:
parent
2ab588049e
commit
13068da43e
@ -150,23 +150,7 @@ pub const Mode = enum {
|
|||||||
|
|
||||||
/// This data structure is used by the Zig language code generation and
|
/// This data structure is used by the Zig language code generation and
|
||||||
/// therefore must be kept in sync with the compiler implementation.
|
/// therefore must be kept in sync with the compiler implementation.
|
||||||
pub const CallingConvention = enum {
|
pub const CallingConvention = enum { Unspecified, C, Naked, Async, Inline, Interrupt, Signal, Stdcall, Fastcall, Vectorcall, Thiscall, APCS, AAPCS, AAPCSVFP, SysV };
|
||||||
Unspecified,
|
|
||||||
C,
|
|
||||||
Naked,
|
|
||||||
Async,
|
|
||||||
Inline,
|
|
||||||
Interrupt,
|
|
||||||
Signal,
|
|
||||||
Stdcall,
|
|
||||||
Fastcall,
|
|
||||||
Vectorcall,
|
|
||||||
Thiscall,
|
|
||||||
APCS,
|
|
||||||
AAPCS,
|
|
||||||
AAPCSVFP,
|
|
||||||
SysV
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This data structure is used by the Zig language code generation and
|
/// This data structure is used by the Zig language code generation and
|
||||||
/// therefore must be kept in sync with the compiler implementation.
|
/// therefore must be kept in sync with the compiler implementation.
|
||||||
|
|||||||
@ -662,9 +662,7 @@ test "lengths overflow" {
|
|||||||
// malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316)
|
// malformed final dynamic block, tries to write 321 code lengths (MAXCODES is 316)
|
||||||
// f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6
|
// f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6
|
||||||
// 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11
|
// 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11
|
||||||
const stream = [_]u8{
|
const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 };
|
||||||
0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110
|
|
||||||
};
|
|
||||||
|
|
||||||
const reader = std.io.fixedBufferStream(&stream).reader();
|
const reader = std.io.fixedBufferStream(&stream).reader();
|
||||||
var window: [0x8000]u8 = undefined;
|
var window: [0x8000]u8 = undefined;
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
const std = @import("../../../std.zig");
|
const std = @import("../../../std.zig");
|
||||||
const linux = std.os.linux;
|
const linux = std.os.linux;
|
||||||
const socklen_t = linux.socklen_t;
|
const socklen_t = linux.socklen_t;
|
||||||
|
const sockaddr = linux.sockaddr;
|
||||||
const iovec = linux.iovec;
|
const iovec = linux.iovec;
|
||||||
const iovec_const = linux.iovec_const;
|
const iovec_const = linux.iovec_const;
|
||||||
const uid_t = linux.uid_t;
|
const uid_t = linux.uid_t;
|
||||||
|
|||||||
@ -1,8 +1,22 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2015-2021 Zig Contributors
|
||||||
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||||
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
|
// and substantial portions of the software.
|
||||||
|
|
||||||
|
const std = @import("std.zig");
|
||||||
|
|
||||||
pub const os = struct {
|
pub const os = struct {
|
||||||
pub const Socket = @import("x/os/Socket.zig");
|
pub const Socket = @import("x/os/Socket.zig");
|
||||||
pub usingnamespace @import("x/os/net.zig");
|
pub usingnamespace @import("x/os/net.zig");
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const net = struct {
|
pub const net = struct {
|
||||||
pub const TCP = @import("x/net/TCP.zig");
|
pub const tcp = @import("x/net/tcp.zig");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test {
|
||||||
|
inline for (.{ os, net }) |module| {
|
||||||
|
std.testing.refAllDecls(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2015-2021 Zig Contributors
|
||||||
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||||
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
|
// and substantial portions of the software.
|
||||||
|
|
||||||
const std = @import("../../std.zig");
|
const std = @import("../../std.zig");
|
||||||
|
|
||||||
const os = std.os;
|
const os = std.os;
|
||||||
@ -10,20 +16,76 @@ const IPv6 = std.x.os.IPv6;
|
|||||||
const Socket = std.x.os.Socket;
|
const Socket = std.x.os.Socket;
|
||||||
|
|
||||||
/// A generic TCP socket abstraction.
|
/// A generic TCP socket abstraction.
|
||||||
const TCP = @This();
|
const tcp = @This();
|
||||||
|
|
||||||
|
/// A union of all eligible types of socket addresses over TCP.
|
||||||
|
pub const Address = union(enum) {
|
||||||
|
ipv4: IPv4.Address,
|
||||||
|
ipv6: IPv6.Address,
|
||||||
|
|
||||||
|
/// Instantiate a new address with a IPv4 host and port.
|
||||||
|
pub fn initIPv4(host: IPv4, port: u16) Address {
|
||||||
|
return .{ .ipv4 = .{ .host = host, .port = port } };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instantiate a new address with a IPv6 host and port.
|
||||||
|
pub fn initIPv6(host: IPv6, port: u16) Address {
|
||||||
|
return .{ .ipv6 = .{ .host = host, .port = port } };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Re-interpret a generic socket address into a TCP socket address.
|
||||||
|
pub fn from(address: Socket.Address) tcp.Address {
|
||||||
|
return switch (address) {
|
||||||
|
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
|
||||||
|
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Re-interpret a TCP socket address into a generic socket address.
|
||||||
|
pub fn into(self: tcp.Address) Socket.Address {
|
||||||
|
return switch (self) {
|
||||||
|
.ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
|
||||||
|
.ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `std.fmt.format` API.
|
||||||
|
pub fn format(
|
||||||
|
self: tcp.Address,
|
||||||
|
comptime layout: []const u8,
|
||||||
|
opts: fmt.FormatOptions,
|
||||||
|
writer: anytype,
|
||||||
|
) !void {
|
||||||
|
switch (self) {
|
||||||
|
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||||
|
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// A TCP client-address pair.
|
/// A TCP client-address pair.
|
||||||
pub const Connection = struct {
|
pub const Connection = struct {
|
||||||
client: TCP.Client,
|
client: tcp.Client,
|
||||||
address: TCP.Address,
|
address: tcp.Address,
|
||||||
|
|
||||||
/// Enclose a TCP client and address into a client-address pair.
|
/// Enclose a TCP client and address into a client-address pair.
|
||||||
pub fn from(socket: Socket, address: TCP.Address) Connection {
|
pub fn from(conn: Socket.Connection) tcp.Connection {
|
||||||
return .{ .client = TCP.Client.from(socket), .address = address };
|
return .{
|
||||||
|
.client = tcp.Client.from(conn.socket),
|
||||||
|
.address = tcp.Address.from(conn.address),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unravel a TCP client-address pair into a socket-address pair.
|
||||||
|
pub fn into(self: tcp.Connection) Socket.Connection {
|
||||||
|
return .{
|
||||||
|
.socket = self.client.socket,
|
||||||
|
.address = self.address.into(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the underlying client of the connection.
|
/// Closes the underlying client of the connection.
|
||||||
pub fn deinit(self: TCP.Connection) void {
|
pub fn deinit(self: tcp.Connection) void {
|
||||||
self.client.deinit();
|
self.client.deinit();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -39,7 +101,7 @@ pub const Client = struct {
|
|||||||
socket: Socket,
|
socket: Socket,
|
||||||
|
|
||||||
/// Opens a new client.
|
/// Opens a new client.
|
||||||
pub fn init(domain: TCP.Domain, flags: u32) !Client {
|
pub fn init(domain: tcp.Domain, flags: u32) !Client {
|
||||||
return Client{
|
return Client{
|
||||||
.socket = try Socket.init(
|
.socket = try Socket.init(
|
||||||
@enumToInt(domain),
|
@enumToInt(domain),
|
||||||
@ -65,8 +127,8 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Have the client attempt to the connect to an address.
|
/// Have the client attempt to the connect to an address.
|
||||||
pub fn connect(self: Client, address: TCP.Address) !void {
|
pub fn connect(self: Client, address: tcp.Address) !void {
|
||||||
return self.socket.connect(TCP.Address, address);
|
return self.socket.connect(address.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read data from the socket into the buffer provided. It returns the
|
/// Read data from the socket into the buffer provided. It returns the
|
||||||
@ -122,8 +184,8 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Query the address that the client's socket is locally bounded to.
|
/// Query the address that the client's socket is locally bounded to.
|
||||||
pub fn getLocalAddress(self: Client) !TCP.Address {
|
pub fn getLocalAddress(self: Client) !tcp.Address {
|
||||||
return self.socket.getLocalAddress(TCP.Address);
|
return tcp.Address.from(try self.socket.getLocalAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if
|
/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if
|
||||||
@ -167,7 +229,7 @@ pub const Listener = struct {
|
|||||||
socket: Socket,
|
socket: Socket,
|
||||||
|
|
||||||
/// Opens a new listener.
|
/// Opens a new listener.
|
||||||
pub fn init(domain: TCP.Domain, flags: u32) !Listener {
|
pub fn init(domain: tcp.Domain, flags: u32) !Listener {
|
||||||
return Listener{
|
return Listener{
|
||||||
.socket = try Socket.init(
|
.socket = try Socket.init(
|
||||||
@enumToInt(domain),
|
@enumToInt(domain),
|
||||||
@ -190,8 +252,8 @@ pub const Listener = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Binds the listener's socket to an address.
|
/// Binds the listener's socket to an address.
|
||||||
pub fn bind(self: Listener, address: TCP.Address) !void {
|
pub fn bind(self: Listener, address: tcp.Address) !void {
|
||||||
return self.socket.bind(TCP.Address, address);
|
return self.socket.bind(address.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start listening for incoming connections.
|
/// Start listening for incoming connections.
|
||||||
@ -201,8 +263,8 @@ pub const Listener = struct {
|
|||||||
|
|
||||||
/// Accept a pending incoming connection queued to the kernel backlog
|
/// Accept a pending incoming connection queued to the kernel backlog
|
||||||
/// of the listener's socket.
|
/// of the listener's socket.
|
||||||
pub fn accept(self: Listener, flags: u32) !TCP.Connection {
|
pub fn accept(self: Listener, flags: u32) !tcp.Connection {
|
||||||
return self.socket.accept(TCP.Connection, TCP.Address, flags);
|
return tcp.Connection.from(try self.socket.accept(flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query and return the latest cached error on the listener's underlying socket.
|
/// Query and return the latest cached error on the listener's underlying socket.
|
||||||
@ -211,8 +273,8 @@ pub const Listener = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Query the address that the listener's socket is locally bounded to.
|
/// Query the address that the listener's socket is locally bounded to.
|
||||||
pub fn getLocalAddress(self: Listener) !TCP.Address {
|
pub fn getLocalAddress(self: Listener) !tcp.Address {
|
||||||
return self.socket.getLocalAddress(TCP.Address);
|
return tcp.Address.from(try self.socket.getLocalAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||||
@ -253,147 +315,69 @@ pub const Listener = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A TCP socket address designated by a host IP and port. A TCP socket
|
|
||||||
/// address comprises of 28 bytes. It may freely be used in place of
|
|
||||||
/// `sockaddr` when working with socket syscalls.
|
|
||||||
///
|
|
||||||
/// It is not recommended to touch the fields of an `Address`, but to
|
|
||||||
/// instead make use of its available accessor methods.
|
|
||||||
pub const Address = extern struct {
|
|
||||||
family: u16,
|
|
||||||
port: u16,
|
|
||||||
host: extern union {
|
|
||||||
ipv4: extern struct {
|
|
||||||
address: IPv4,
|
|
||||||
},
|
|
||||||
ipv6: extern struct {
|
|
||||||
flow_info: u32 = 0,
|
|
||||||
address: IPv6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Instantiate a new TCP address with a IPv4 host and port.
|
|
||||||
pub fn initIPv4(host: IPv4, port: u16) Address {
|
|
||||||
return Address{
|
|
||||||
.family = os.AF_INET,
|
|
||||||
.port = mem.nativeToBig(u16, port),
|
|
||||||
.host = .{
|
|
||||||
.ipv4 = .{
|
|
||||||
.address = host,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Instantiate a new TCP address with a IPv6 host and port.
|
|
||||||
pub fn initIPv6(host: IPv6, port: u16) Address {
|
|
||||||
return Address{
|
|
||||||
.family = os.AF_INET6,
|
|
||||||
.port = mem.nativeToBig(u16, port),
|
|
||||||
.host = .{
|
|
||||||
.ipv6 = .{
|
|
||||||
.address = host,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the host of the address.
|
|
||||||
pub fn getHost(self: Address) union(enum) { v4: IPv4, v6: IPv6 } {
|
|
||||||
return switch (self.family) {
|
|
||||||
os.AF_INET => .{ .v4 = self.host.ipv4.address },
|
|
||||||
os.AF_INET6 => .{ .v6 = self.host.ipv6.address },
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the port of the address.
|
|
||||||
pub fn getPort(self: Address) u16 {
|
|
||||||
return mem.nativeToBig(u16, self.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the port of the address.
|
|
||||||
pub fn setPort(self: *Address, port: u16) void {
|
|
||||||
self.port = mem.nativeToBig(u16, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements the `std.fmt.format` API.
|
|
||||||
pub fn format(
|
|
||||||
self: Address,
|
|
||||||
comptime layout: []const u8,
|
|
||||||
opts: fmt.FormatOptions,
|
|
||||||
writer: anytype,
|
|
||||||
) !void {
|
|
||||||
switch (self.getHost()) {
|
|
||||||
.v4 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }),
|
|
||||||
.v6 => |host| try fmt.format(writer, "{}:{}", .{ host, self.getPort() }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test {
|
test {
|
||||||
testing.refAllDecls(@This());
|
testing.refAllDecls(@This());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "tcp: create non-blocking pair" {
|
test "tcp: create non-blocking pair" {
|
||||||
const a = try TCP.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
const listener = try tcp.Listener.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
||||||
defer a.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0));
|
try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
|
||||||
try a.listen(128);
|
try listener.listen(128);
|
||||||
|
|
||||||
const binded_address = try a.getLocalAddress();
|
const binded_address = try listener.getLocalAddress();
|
||||||
|
|
||||||
const b = try TCP.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
const client = try tcp.Client.init(.ip, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
||||||
defer b.deinit();
|
defer client.deinit();
|
||||||
|
|
||||||
testing.expectError(error.WouldBlock, b.connect(binded_address));
|
testing.expectError(error.WouldBlock, client.connect(binded_address));
|
||||||
try b.getError();
|
try client.getError();
|
||||||
|
|
||||||
const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
const conn = try listener.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
||||||
defer ab.deinit();
|
defer conn.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
test "tcp/client: set read timeout of 1 millisecond on blocking client" {
|
test "tcp/client: set read timeout of 1 millisecond on blocking client" {
|
||||||
const a = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC);
|
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
|
||||||
defer a.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
try a.bind(TCP.Address.initIPv4(IPv4.unspecified, 0));
|
try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
|
||||||
try a.listen(128);
|
try listener.listen(128);
|
||||||
|
|
||||||
const binded_address = try a.getLocalAddress();
|
const binded_address = try listener.getLocalAddress();
|
||||||
|
|
||||||
const b = try TCP.Client.init(.ip, os.SOCK_CLOEXEC);
|
const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC);
|
||||||
defer b.deinit();
|
defer client.deinit();
|
||||||
|
|
||||||
try b.connect(binded_address);
|
try client.connect(binded_address);
|
||||||
try b.setReadTimeout(1);
|
try client.setReadTimeout(1);
|
||||||
|
|
||||||
const ab = try a.accept(os.SOCK_CLOEXEC);
|
const conn = try listener.accept(os.SOCK_CLOEXEC);
|
||||||
defer ab.deinit();
|
defer conn.deinit();
|
||||||
|
|
||||||
var buf: [1]u8 = undefined;
|
var buf: [1]u8 = undefined;
|
||||||
testing.expectError(error.WouldBlock, b.read(&buf));
|
testing.expectError(error.WouldBlock, client.read(&buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "tcp/listener: bind to unspecified ipv4 address" {
|
test "tcp/listener: bind to unspecified ipv4 address" {
|
||||||
const socket = try TCP.Listener.init(.ip, os.SOCK_CLOEXEC);
|
const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
|
||||||
defer socket.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
try socket.bind(TCP.Address.initIPv4(IPv4.unspecified, 0));
|
try listener.bind(tcp.Address.initIPv4(IPv4.unspecified, 0));
|
||||||
try socket.listen(128);
|
try listener.listen(128);
|
||||||
|
|
||||||
const address = try socket.getLocalAddress();
|
const address = try listener.getLocalAddress();
|
||||||
testing.expect(address.getHost() == .v4);
|
testing.expect(address == .ipv4);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "tcp/listener: bind to unspecified ipv6 address" {
|
test "tcp/listener: bind to unspecified ipv6 address" {
|
||||||
const socket = try TCP.Listener.init(.ipv6, os.SOCK_CLOEXEC);
|
const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC);
|
||||||
defer socket.deinit();
|
defer listener.deinit();
|
||||||
|
|
||||||
try socket.bind(TCP.Address.initIPv6(IPv6.unspecified, 0));
|
try listener.bind(tcp.Address.initIPv6(IPv6.unspecified, 0));
|
||||||
try socket.listen(128);
|
try listener.listen(128);
|
||||||
|
|
||||||
const address = try socket.getLocalAddress();
|
const address = try listener.getLocalAddress();
|
||||||
testing.expect(address.getHost() == .v6);
|
testing.expect(address == .ipv6);
|
||||||
}
|
}
|
||||||
@ -1,4 +1,11 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2015-2021 Zig Contributors
|
||||||
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||||
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
|
// and substantial portions of the software.
|
||||||
|
|
||||||
const std = @import("../../std.zig");
|
const std = @import("../../std.zig");
|
||||||
|
const net = @import("net.zig");
|
||||||
|
|
||||||
const os = std.os;
|
const os = std.os;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
@ -7,6 +14,98 @@ const time = std.time;
|
|||||||
/// A generic socket abstraction.
|
/// A generic socket abstraction.
|
||||||
const Socket = @This();
|
const Socket = @This();
|
||||||
|
|
||||||
|
/// A socket-address pair.
|
||||||
|
pub const Connection = struct {
|
||||||
|
socket: Socket,
|
||||||
|
address: Socket.Address,
|
||||||
|
|
||||||
|
/// Enclose a socket and address into a socket-address pair.
|
||||||
|
pub fn from(socket: Socket, address: Socket.Address) Socket.Connection {
|
||||||
|
return .{ .socket = socket, .address = address };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A generic socket address abstraction. It is safe to directly access and modify
|
||||||
|
/// the fields of a `Socket.Address`.
|
||||||
|
pub const Address = union(enum) {
|
||||||
|
ipv4: net.IPv4.Address,
|
||||||
|
ipv6: net.IPv6.Address,
|
||||||
|
|
||||||
|
/// Instantiate a new address with a IPv4 host and port.
|
||||||
|
pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address {
|
||||||
|
return .{ .ipv4 = .{ .host = host, .port = port } };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instantiate a new address with a IPv6 host and port.
|
||||||
|
pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address {
|
||||||
|
return .{ .ipv6 = .{ .host = host, .port = port } };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a `sockaddr` into a generic socket address.
|
||||||
|
pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address {
|
||||||
|
switch (address.family) {
|
||||||
|
os.AF_INET => {
|
||||||
|
const info = @ptrCast(*const os.sockaddr_in, address);
|
||||||
|
const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
|
||||||
|
const port = mem.bigToNative(u16, info.port);
|
||||||
|
return Socket.Address.initIPv4(host, port);
|
||||||
|
},
|
||||||
|
os.AF_INET6 => {
|
||||||
|
const info = @ptrCast(*const os.sockaddr_in6, address);
|
||||||
|
const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
|
||||||
|
const port = mem.bigToNative(u16, info.port);
|
||||||
|
return Socket.Address.initIPv6(host, port);
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes a generic socket address into an extern union that may be reliably
|
||||||
|
/// casted into a `sockaddr` which may be passed into socket syscalls.
|
||||||
|
pub fn toNative(self: Socket.Address) extern union {
|
||||||
|
ipv4: os.sockaddr_in,
|
||||||
|
ipv6: os.sockaddr_in6,
|
||||||
|
} {
|
||||||
|
return switch (self) {
|
||||||
|
.ipv4 => |address| .{
|
||||||
|
.ipv4 = .{
|
||||||
|
.addr = @bitCast(u32, address.host.octets),
|
||||||
|
.port = mem.nativeToBig(u16, address.port),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.ipv6 => |address| .{
|
||||||
|
.ipv6 = .{
|
||||||
|
.addr = address.host.octets,
|
||||||
|
.port = mem.nativeToBig(u16, address.port),
|
||||||
|
.scope_id = address.host.scope_id,
|
||||||
|
.flowinfo = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
|
||||||
|
pub fn getNativeSize(self: Socket.Address) u32 {
|
||||||
|
return switch (self) {
|
||||||
|
.ipv4 => @sizeOf(os.sockaddr_in),
|
||||||
|
.ipv6 => @sizeOf(os.sockaddr_in6),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `std.fmt.format` API.
|
||||||
|
pub fn format(
|
||||||
|
self: Socket.Address,
|
||||||
|
comptime layout: []const u8,
|
||||||
|
opts: fmt.FormatOptions,
|
||||||
|
writer: anytype,
|
||||||
|
) !void {
|
||||||
|
switch (self) {
|
||||||
|
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||||
|
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// The underlying handle of a socket.
|
/// The underlying handle of a socket.
|
||||||
fd: os.socket_t,
|
fd: os.socket_t,
|
||||||
|
|
||||||
@ -31,8 +130,8 @@ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Binds the socket to an address.
|
/// Binds the socket to an address.
|
||||||
pub fn bind(self: Socket, comptime Address: type, address: Address) !void {
|
pub fn bind(self: Socket, address: Socket.Address) !void {
|
||||||
return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address));
|
return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start listening for incoming connections on the socket.
|
/// Start listening for incoming connections on the socket.
|
||||||
@ -41,19 +140,20 @@ pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Have the socket attempt to the connect to an address.
|
/// Have the socket attempt to the connect to an address.
|
||||||
pub fn connect(self: Socket, comptime Address: type, address: Address) !void {
|
pub fn connect(self: Socket, address: Socket.Address) !void {
|
||||||
return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address), @sizeOf(Address));
|
return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accept a pending incoming connection queued to the kernel backlog
|
/// Accept a pending incoming connection queued to the kernel backlog
|
||||||
/// of the socket.
|
/// of the socket.
|
||||||
pub fn accept(self: Socket, comptime Connection: type, comptime Address: type, flags: u32) !Connection {
|
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||||
var address: Address = undefined;
|
var address: os.sockaddr = undefined;
|
||||||
var address_len: u32 = @sizeOf(Address);
|
var address_len: u32 = @sizeOf(os.sockaddr);
|
||||||
|
|
||||||
const fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags);
|
const socket = Socket{ .fd = try os.accept(self.fd, &address, &address_len, flags) };
|
||||||
|
const socket_address = Socket.Address.fromNative(@alignCast(4, &address));
|
||||||
|
|
||||||
return Connection.from(.{ .fd = fd }, address);
|
return Socket.Connection.from(socket, socket_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read data from the socket into the buffer provided. It returns the
|
/// 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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Query the address that the socket is locally bounded to.
|
/// Query the address that the socket is locally bounded to.
|
||||||
pub fn getLocalAddress(self: Socket, comptime Address: type) !Address {
|
pub fn getLocalAddress(self: Socket) !Socket.Address {
|
||||||
var address: Address = undefined;
|
var address: os.sockaddr = undefined;
|
||||||
var address_len: u32 = @sizeOf(Address);
|
var address_len: u32 = @sizeOf(os.sockaddr);
|
||||||
try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
|
try os.getsockname(self.fd, &address, &address_len);
|
||||||
return address;
|
return Socket.Address.fromNative(@alignCast(4, &address));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query and return the latest cached error on the socket.
|
/// Query and return the latest cached error on the socket.
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2015-2021 Zig Contributors
|
||||||
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||||
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
|
// and substantial portions of the software.
|
||||||
|
|
||||||
const std = @import("../../std.zig");
|
const std = @import("../../std.zig");
|
||||||
|
|
||||||
const os = std.os;
|
const os = std.os;
|
||||||
@ -27,6 +33,12 @@ pub fn resolveScopeID(name: []const u8) !u32 {
|
|||||||
|
|
||||||
/// An IPv4 address comprised of 4 bytes.
|
/// An IPv4 address comprised of 4 bytes.
|
||||||
pub const IPv4 = extern struct {
|
pub const IPv4 = extern struct {
|
||||||
|
/// A IPv4 host-port pair.
|
||||||
|
pub const Address = extern struct {
|
||||||
|
host: IPv4,
|
||||||
|
port: u16,
|
||||||
|
};
|
||||||
|
|
||||||
/// Octets of a IPv4 address designating the local host.
|
/// Octets of a IPv4 address designating the local host.
|
||||||
pub const localhost_octets = [_]u8{ 127, 0, 0, 1 };
|
pub const localhost_octets = [_]u8{ 127, 0, 0, 1 };
|
||||||
|
|
||||||
@ -200,6 +212,12 @@ pub const IPv4 = extern struct {
|
|||||||
/// An IPv6 address comprised of 16 bytes for an address, and 4 bytes
|
/// An IPv6 address comprised of 16 bytes for an address, and 4 bytes
|
||||||
/// for a scope ID; cumulatively summing to 20 bytes in total.
|
/// for a scope ID; cumulatively summing to 20 bytes in total.
|
||||||
pub const IPv6 = extern struct {
|
pub const IPv6 = extern struct {
|
||||||
|
/// A IPv6 host-port pair.
|
||||||
|
pub const Address = extern struct {
|
||||||
|
host: IPv6,
|
||||||
|
port: u16,
|
||||||
|
};
|
||||||
|
|
||||||
/// Octets of a IPv6 address designating the local host.
|
/// Octets of a IPv6 address designating the local host.
|
||||||
pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01};
|
pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user