mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.Io.net.HostName: finish implementing DNS lookup
This commit is contained in:
parent
2e1ab5d3f7
commit
b22400271f
4 changed files with 237 additions and 148 deletions
|
|
@ -641,8 +641,8 @@ pub const VTable = struct {
|
||||||
context_alignment: std.mem.Alignment,
|
context_alignment: std.mem.Alignment,
|
||||||
start: *const fn (context: *const anyopaque) void,
|
start: *const fn (context: *const anyopaque) void,
|
||||||
) void,
|
) void,
|
||||||
groupWait: *const fn (?*anyopaque, *Group) void,
|
groupWait: *const fn (?*anyopaque, *Group, token: *anyopaque) void,
|
||||||
groupCancel: *const fn (?*anyopaque, *Group) void,
|
groupCancel: *const fn (?*anyopaque, *Group, token: *anyopaque) void,
|
||||||
|
|
||||||
/// Blocks until one of the futures from the list has a result ready, such
|
/// Blocks until one of the futures from the list has a result ready, such
|
||||||
/// that awaiting it will not block. Returns that index.
|
/// that awaiting it will not block. Returns that index.
|
||||||
|
|
@ -665,14 +665,14 @@ pub const VTable = struct {
|
||||||
fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void,
|
fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void,
|
||||||
fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void,
|
fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void,
|
||||||
|
|
||||||
now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp,
|
now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) NowError!Timestamp,
|
||||||
sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void,
|
sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, timeout: Timeout) SleepError!void,
|
||||||
|
|
||||||
listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
|
listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
|
||||||
accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream,
|
accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream,
|
||||||
ipBind: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,
|
ipBind: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,
|
||||||
netSend: *const fn (?*anyopaque, handle: net.Socket.Handle, address: net.IpAddress, data: []const u8) net.Socket.SendError!void,
|
netSend: *const fn (?*anyopaque, handle: net.Socket.Handle, address: *const net.IpAddress, data: []const u8) net.Socket.SendError!void,
|
||||||
netReceive: *const fn (?*anyopaque, handle: net.Socket.Handle, address: net.IpAddress, buffer: []u8) net.Socket.ReceiveError!void,
|
netReceive: *const fn (?*anyopaque, handle: net.Socket.Handle, buffer: []u8, timeout: Timeout) net.Socket.ReceiveTimeoutError!net.ReceivedMessage,
|
||||||
netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize,
|
netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize,
|
||||||
netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,
|
netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,
|
||||||
netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void,
|
netClose: *const fn (?*anyopaque, handle: net.Socket.Handle) void,
|
||||||
|
|
@ -710,6 +710,15 @@ pub const Timestamp = enum(i96) {
|
||||||
pub fn addDuration(from: Timestamp, duration: Duration) Timestamp {
|
pub fn addDuration(from: Timestamp, duration: Duration) Timestamp {
|
||||||
return @enumFromInt(@intFromEnum(from) + duration.nanoseconds);
|
return @enumFromInt(@intFromEnum(from) + duration.nanoseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fromNow(io: Io, clockid: std.posix.clockid_t, duration: Duration) NowError!Timestamp {
|
||||||
|
const now_ts = try now(io, clockid);
|
||||||
|
return addDuration(now_ts, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(lhs: Timestamp, op: std.math.CompareOperator, rhs: Timestamp) bool {
|
||||||
|
return std.math.compare(@intFromEnum(lhs), op, @intFromEnum(rhs));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
pub const Duration = struct {
|
pub const Duration = struct {
|
||||||
nanoseconds: i96,
|
nanoseconds: i96,
|
||||||
|
|
@ -722,11 +731,14 @@ pub const Duration = struct {
|
||||||
return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_s };
|
return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_s };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
pub const Deadline = union(enum) {
|
pub const Timeout = union(enum) {
|
||||||
|
none,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
timestamp: Timestamp,
|
deadline: Timestamp,
|
||||||
|
|
||||||
|
pub const Error = error{Timeout};
|
||||||
};
|
};
|
||||||
pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable;
|
pub const NowError = std.posix.ClockGetTimeError || Cancelable;
|
||||||
pub const SleepError = error{ UnsupportedClock, Unexpected, Canceled };
|
pub const SleepError = error{ UnsupportedClock, Unexpected, Canceled };
|
||||||
|
|
||||||
pub const AnyFuture = opaque {};
|
pub const AnyFuture = opaque {};
|
||||||
|
|
@ -768,8 +780,10 @@ pub const Group = struct {
|
||||||
///
|
///
|
||||||
/// `function` *may* be called immediately, before `async` returns.
|
/// `function` *may* be called immediately, before `async` returns.
|
||||||
///
|
///
|
||||||
/// After this is called, `wait` must be called before the group is
|
/// After this is called, `wait` or `cancel` must be called before the
|
||||||
/// deinitialized.
|
/// group is deinitialized.
|
||||||
|
///
|
||||||
|
/// Threadsafe.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// * `Io.async`
|
/// * `Io.async`
|
||||||
|
|
@ -789,7 +803,9 @@ pub const Group = struct {
|
||||||
///
|
///
|
||||||
/// Idempotent. Not threadsafe.
|
/// Idempotent. Not threadsafe.
|
||||||
pub fn wait(g: *Group, io: Io) void {
|
pub fn wait(g: *Group, io: Io) void {
|
||||||
io.vtable.groupWait(io.userdata, g);
|
const token = g.token orelse return;
|
||||||
|
g.token = null;
|
||||||
|
io.vtable.groupWait(io.userdata, g, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to `wait` but requests cancellation on all tasks owned by
|
/// Equivalent to `wait` but requests cancellation on all tasks owned by
|
||||||
|
|
@ -797,9 +813,9 @@ pub const Group = struct {
|
||||||
///
|
///
|
||||||
/// Idempotent. Not threadsafe.
|
/// Idempotent. Not threadsafe.
|
||||||
pub fn cancel(g: *Group, io: Io) void {
|
pub fn cancel(g: *Group, io: Io) void {
|
||||||
if (g.token == null) return;
|
const token = g.token orelse return;
|
||||||
io.vtable.groupCancel(io.userdata, g);
|
g.token = null;
|
||||||
assert(g.token == null);
|
io.vtable.groupCancel(io.userdata, g, token);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1215,12 +1231,12 @@ pub fn cancelRequested(io: Io) bool {
|
||||||
return io.vtable.cancelRequested(io.userdata);
|
return io.vtable.cancelRequested(io.userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp {
|
pub fn now(io: Io, clockid: std.posix.clockid_t) NowError!Timestamp {
|
||||||
return io.vtable.now(io.userdata, clockid);
|
return io.vtable.now(io.userdata, clockid);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void {
|
pub fn sleep(io: Io, clockid: std.posix.clockid_t, timeout: Timeout) SleepError!void {
|
||||||
return io.vtable.sleep(io.userdata, clockid, deadline);
|
return io.vtable.sleep(io.userdata, clockid, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleepDuration(io: Io, duration: Duration) SleepError!void {
|
pub fn sleepDuration(io: Io, duration: Duration) SleepError!void {
|
||||||
|
|
|
||||||
|
|
@ -463,16 +463,19 @@ fn groupAsync(
|
||||||
},
|
},
|
||||||
.pool = pool,
|
.pool = pool,
|
||||||
.group = group,
|
.group = group,
|
||||||
.node = .{ .next = @ptrCast(@alignCast(group.token)) },
|
.node = undefined,
|
||||||
.func = start,
|
.func = start,
|
||||||
.context_alignment = context_alignment,
|
.context_alignment = context_alignment,
|
||||||
.context_len = context.len,
|
.context_len = context.len,
|
||||||
};
|
};
|
||||||
group.token = &gc.node;
|
|
||||||
@memcpy(gc.contextPointer()[0..context.len], context);
|
@memcpy(gc.contextPointer()[0..context.len], context);
|
||||||
|
|
||||||
pool.mutex.lock();
|
pool.mutex.lock();
|
||||||
|
|
||||||
|
// Append to the group linked list inside the mutex to make `Io.Group.async` thread-safe.
|
||||||
|
gc.node = .{ .next = @ptrCast(@alignCast(group.token)) };
|
||||||
|
group.token = &gc.node;
|
||||||
|
|
||||||
const thread_capacity = cpu_count - 1 + pool.concurrent_count;
|
const thread_capacity = cpu_count - 1 + pool.concurrent_count;
|
||||||
|
|
||||||
pool.threads.ensureTotalCapacityPrecise(gpa, thread_capacity) catch {
|
pool.threads.ensureTotalCapacityPrecise(gpa, thread_capacity) catch {
|
||||||
|
|
@ -493,6 +496,8 @@ fn groupAsync(
|
||||||
pool.threads.appendAssumeCapacity(thread);
|
pool.threads.appendAssumeCapacity(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This needs to be done before unlocking the mutex to avoid a race with
|
||||||
|
// the associated task finishing.
|
||||||
const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
|
const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
|
||||||
std.Thread.WaitGroup.startStateless(group_state);
|
std.Thread.WaitGroup.startStateless(group_state);
|
||||||
|
|
||||||
|
|
@ -500,21 +505,16 @@ fn groupAsync(
|
||||||
pool.cond.signal();
|
pool.cond.signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn groupWait(userdata: ?*anyopaque, group: *Io.Group) void {
|
fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
|
||||||
if (builtin.single_threaded) return;
|
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
_ = pool;
|
_ = pool;
|
||||||
|
|
||||||
|
if (builtin.single_threaded) return;
|
||||||
|
|
||||||
const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
|
const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
|
||||||
const reset_event: *ResetEvent = @ptrCast(&group.context);
|
const reset_event: *ResetEvent = @ptrCast(&group.context);
|
||||||
std.Thread.WaitGroup.waitStateless(group_state, reset_event);
|
std.Thread.WaitGroup.waitStateless(group_state, reset_event);
|
||||||
}
|
|
||||||
|
|
||||||
fn groupCancel(userdata: ?*anyopaque, group: *Io.Group) void {
|
|
||||||
if (builtin.single_threaded) return;
|
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
|
||||||
_ = pool;
|
|
||||||
const token = group.token.?;
|
|
||||||
group.token = null;
|
|
||||||
var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
|
var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
|
||||||
while (true) {
|
while (true) {
|
||||||
const gc: *GroupClosure = @fieldParentPtr("node", node);
|
const gc: *GroupClosure = @fieldParentPtr("node", node);
|
||||||
|
|
@ -523,6 +523,36 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
|
||||||
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
|
const gpa = pool.allocator;
|
||||||
|
|
||||||
|
if (builtin.single_threaded) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
|
||||||
|
while (true) {
|
||||||
|
const gc: *GroupClosure = @fieldParentPtr("node", node);
|
||||||
|
gc.closure.requestCancel();
|
||||||
|
node = node.next orelse break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
|
||||||
|
const reset_event: *ResetEvent = @ptrCast(&group.context);
|
||||||
|
std.Thread.WaitGroup.waitStateless(group_state, reset_event);
|
||||||
|
|
||||||
|
{
|
||||||
|
var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
|
||||||
|
while (true) {
|
||||||
|
const gc: *GroupClosure = @fieldParentPtr("node", node);
|
||||||
|
const node_next = node.next;
|
||||||
|
gc.free(gpa);
|
||||||
|
node = node_next orelse break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn await(
|
fn await(
|
||||||
userdata: ?*anyopaque,
|
userdata: ?*anyopaque,
|
||||||
any_future: *Io.AnyFuture,
|
any_future: *Io.AnyFuture,
|
||||||
|
|
@ -774,7 +804,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
|
||||||
.SUCCESS => return nread,
|
.SUCCESS => return nread,
|
||||||
.INTR => unreachable,
|
.INTR => unreachable,
|
||||||
.INVAL => unreachable,
|
.INVAL => unreachable,
|
||||||
.FAULT => unreachable,
|
.FAULT => |err| return errnoBug(err),
|
||||||
.AGAIN => unreachable, // currently not support in WASI
|
.AGAIN => unreachable, // currently not support in WASI
|
||||||
.BADF => return error.NotOpenForReading, // can be a race condition
|
.BADF => return error.NotOpenForReading, // can be a race condition
|
||||||
.IO => return error.InputOutput,
|
.IO => return error.InputOutput,
|
||||||
|
|
@ -796,7 +826,7 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
|
||||||
.SUCCESS => return @intCast(rc),
|
.SUCCESS => return @intCast(rc),
|
||||||
.INTR => continue,
|
.INTR => continue,
|
||||||
.INVAL => unreachable,
|
.INVAL => unreachable,
|
||||||
.FAULT => unreachable,
|
.FAULT => |err| return errnoBug(err),
|
||||||
.SRCH => return error.ProcessNotFound,
|
.SRCH => return error.ProcessNotFound,
|
||||||
.AGAIN => return error.WouldBlock,
|
.AGAIN => return error.WouldBlock,
|
||||||
.BADF => return error.NotOpenForReading, // can be a race condition
|
.BADF => return error.NotOpenForReading, // can be a race condition
|
||||||
|
|
@ -896,7 +926,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
|
||||||
.SUCCESS => return nread,
|
.SUCCESS => return nread,
|
||||||
.INTR => unreachable,
|
.INTR => unreachable,
|
||||||
.INVAL => unreachable,
|
.INVAL => unreachable,
|
||||||
.FAULT => unreachable,
|
.FAULT => |err| return errnoBug(err),
|
||||||
.AGAIN => unreachable,
|
.AGAIN => unreachable,
|
||||||
.BADF => return error.NotOpenForReading, // can be a race condition
|
.BADF => return error.NotOpenForReading, // can be a race condition
|
||||||
.IO => return error.InputOutput,
|
.IO => return error.InputOutput,
|
||||||
|
|
@ -922,7 +952,7 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
|
||||||
.SUCCESS => return @bitCast(rc),
|
.SUCCESS => return @bitCast(rc),
|
||||||
.INTR => continue,
|
.INTR => continue,
|
||||||
.INVAL => unreachable,
|
.INVAL => unreachable,
|
||||||
.FAULT => unreachable,
|
.FAULT => |err| return errnoBug(err),
|
||||||
.SRCH => return error.ProcessNotFound,
|
.SRCH => return error.ProcessNotFound,
|
||||||
.AGAIN => return error.WouldBlock,
|
.AGAIN => return error.WouldBlock,
|
||||||
.BADF => return error.NotOpenForReading, // can be a race condition
|
.BADF => return error.NotOpenForReading, // can be a race condition
|
||||||
|
|
@ -969,18 +999,19 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp {
|
fn now(userdata: ?*anyopaque, clockid: posix.clockid_t) Io.NowError!Io.Timestamp {
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
try pool.checkCancel();
|
try pool.checkCancel();
|
||||||
const timespec = try posix.clock_gettime(clockid);
|
const timespec = try posix.clock_gettime(clockid);
|
||||||
return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec);
|
return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void {
|
fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, timeout: Io.Timeout) Io.SleepError!void {
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
const deadline_nanoseconds: i96 = switch (deadline) {
|
const deadline_nanoseconds: i96 = switch (timeout) {
|
||||||
|
.none => std.math.maxInt(i96),
|
||||||
.duration => |duration| duration.nanoseconds,
|
.duration => |duration| duration.nanoseconds,
|
||||||
.timestamp => |timestamp| @intFromEnum(timestamp),
|
.deadline => |deadline| @intFromEnum(deadline),
|
||||||
};
|
};
|
||||||
var timespec: posix.timespec = .{
|
var timespec: posix.timespec = .{
|
||||||
.sec = @intCast(@divFloor(deadline_nanoseconds, std.time.ns_per_s)),
|
.sec = @intCast(@divFloor(deadline_nanoseconds, std.time.ns_per_s)),
|
||||||
|
|
@ -988,12 +1019,12 @@ fn sleep(userdata: ?*anyopaque, clockid: posix.clockid_t, deadline: Io.Deadline)
|
||||||
};
|
};
|
||||||
while (true) {
|
while (true) {
|
||||||
try pool.checkCancel();
|
try pool.checkCancel();
|
||||||
switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clockid, .{ .ABSTIME = switch (deadline) {
|
switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clockid, .{ .ABSTIME = switch (timeout) {
|
||||||
.duration => false,
|
.none, .duration => false,
|
||||||
.timestamp => true,
|
.deadline => true,
|
||||||
} }, ×pec, ×pec))) {
|
} }, ×pec, ×pec))) {
|
||||||
.SUCCESS => return,
|
.SUCCESS => return,
|
||||||
.FAULT => unreachable,
|
.FAULT => |err| return errnoBug(err),
|
||||||
.INTR => {},
|
.INTR => {},
|
||||||
.INVAL => return error.UnsupportedClock,
|
.INVAL => return error.UnsupportedClock,
|
||||||
else => |err| return posix.unexpectedErrno(err),
|
else => |err| return posix.unexpectedErrno(err),
|
||||||
|
|
@ -1278,7 +1309,7 @@ fn netReadPosix(userdata: ?*anyopaque, stream: Io.net.Stream, data: [][]u8) Io.n
|
||||||
fn netSend(
|
fn netSend(
|
||||||
userdata: ?*anyopaque,
|
userdata: ?*anyopaque,
|
||||||
handle: Io.net.Socket.Handle,
|
handle: Io.net.Socket.Handle,
|
||||||
address: Io.net.IpAddress,
|
address: *const Io.net.IpAddress,
|
||||||
data: []const u8,
|
data: []const u8,
|
||||||
) Io.net.Socket.SendError!void {
|
) Io.net.Socket.SendError!void {
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
|
|
@ -1293,15 +1324,15 @@ fn netSend(
|
||||||
fn netReceive(
|
fn netReceive(
|
||||||
userdata: ?*anyopaque,
|
userdata: ?*anyopaque,
|
||||||
handle: Io.net.Socket.Handle,
|
handle: Io.net.Socket.Handle,
|
||||||
address: Io.net.IpAddress,
|
|
||||||
buffer: []u8,
|
buffer: []u8,
|
||||||
) Io.net.Socket.ReceiveError!void {
|
timeout: Io.Timeout,
|
||||||
|
) Io.net.Socket.ReceiveTimeoutError!Io.net.ReceivedMessage {
|
||||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||||
try pool.checkCancel();
|
try pool.checkCancel();
|
||||||
|
|
||||||
_ = handle;
|
_ = handle;
|
||||||
_ = address;
|
|
||||||
_ = buffer;
|
_ = buffer;
|
||||||
|
_ = timeout;
|
||||||
@panic("TODO");
|
@panic("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,13 +134,13 @@ pub const IpAddress = union(enum) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eql(a: IpAddress, b: IpAddress) bool {
|
pub fn eql(a: *const IpAddress, b: *const IpAddress) bool {
|
||||||
return switch (a) {
|
return switch (a.*) {
|
||||||
.ip4 => |a_ip4| switch (b) {
|
.ip4 => |a_ip4| switch (b.*) {
|
||||||
.ip4 => |b_ip4| a_ip4.eql(b_ip4),
|
.ip4 => |b_ip4| a_ip4.eql(b_ip4),
|
||||||
else => false,
|
else => false,
|
||||||
},
|
},
|
||||||
.ip6 => |a_ip6| switch (b) {
|
.ip6 => |a_ip6| switch (b.*) {
|
||||||
.ip6 => |b_ip6| a_ip6.eql(b_ip6),
|
.ip6 => |b_ip6| a_ip6.eql(b_ip6),
|
||||||
else => false,
|
else => false,
|
||||||
},
|
},
|
||||||
|
|
@ -695,6 +695,11 @@ pub const Ip6Address = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ReceivedMessage = struct {
|
||||||
|
from: IpAddress,
|
||||||
|
len: usize,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Interface = struct {
|
pub const Interface = struct {
|
||||||
/// Value 0 indicates `none`.
|
/// Value 0 indicates `none`.
|
||||||
index: u32,
|
index: u32,
|
||||||
|
|
@ -816,14 +821,31 @@ pub const Socket = struct {
|
||||||
return io.vtable.netSend(io.userdata, s.handle, dest, data);
|
return io.vtable.netSend(io.userdata, s.handle, dest, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ReceiveError = error{} || Io.Cancelable;
|
pub const ReceiveError = error{} || Io.UnexpectedError || Io.Cancelable;
|
||||||
|
|
||||||
/// Transfers `data` from `source`, connectionless.
|
/// Waits for data. Connectionless.
|
||||||
///
|
///
|
||||||
/// Returned slice has same pointer as `buffer` with possibly shorter length.
|
/// See also:
|
||||||
pub fn receive(s: *const Socket, io: Io, source: *const IpAddress, buffer: []u8) ReceiveError![]u8 {
|
/// * `receiveTimeout`
|
||||||
const n = try io.vtable.netReceive(io.userdata, s.handle, source, buffer);
|
pub fn receive(s: *const Socket, io: Io, source: *const IpAddress, buffer: []u8) ReceiveError!ReceivedMessage {
|
||||||
return buffer[0..n];
|
return io.vtable.netReceive(io.userdata, s.handle, source, buffer, .none);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error;
|
||||||
|
|
||||||
|
/// Waits for data. Connectionless.
|
||||||
|
///
|
||||||
|
/// Returns `error.Timeout` if no message arrives early enough.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// * `receive`
|
||||||
|
pub fn receiveTimeout(
|
||||||
|
s: *const Socket,
|
||||||
|
io: Io,
|
||||||
|
buffer: []u8,
|
||||||
|
timeout: Io.Timeout,
|
||||||
|
) ReceiveTimeoutError!ReceivedMessage {
|
||||||
|
return io.vtable.netReceive(io.userdata, s.handle, buffer, timeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,15 +46,13 @@ pub const LookupOptions = struct {
|
||||||
family: ?IpAddress.Family = null,
|
family: ?IpAddress.Family = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const LookupError = Io.Cancelable || Io.File.OpenError || Io.File.Reader.Error || error{
|
pub const LookupError = error{
|
||||||
UnknownHostName,
|
UnknownHostName,
|
||||||
ResolvConfParseFailed,
|
ResolvConfParseFailed,
|
||||||
// TODO remove from error set; retry a few times then report a different error
|
|
||||||
TemporaryNameServerFailure,
|
|
||||||
InvalidDnsARecord,
|
InvalidDnsARecord,
|
||||||
InvalidDnsAAAARecord,
|
InvalidDnsAAAARecord,
|
||||||
NameServerFailure,
|
NameServerFailure,
|
||||||
};
|
} || Io.NowError || IpAddress.BindError || Io.File.OpenError || Io.File.Reader.Error || Io.Cancelable;
|
||||||
|
|
||||||
pub const LookupResult = struct {
|
pub const LookupResult = struct {
|
||||||
/// How many `LookupOptions.addresses_buffer` elements are populated.
|
/// How many `LookupOptions.addresses_buffer` elements are populated.
|
||||||
|
|
@ -185,7 +183,7 @@ fn sortLookupResults(options: LookupOptions, result: LookupResult) !LookupResult
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) !LookupResult {
|
fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult {
|
||||||
const rc = ResolvConf.init(io) catch return error.ResolvConfParseFailed;
|
const rc = ResolvConf.init(io) catch return error.ResolvConfParseFailed;
|
||||||
|
|
||||||
// Count dots, suppress search when >=ndots or name ends in
|
// Count dots, suppress search when >=ndots or name ends in
|
||||||
|
|
@ -218,19 +216,17 @@ fn lookupDnsSearch(host_name: HostName, io: Io, options: LookupOptions) !LookupR
|
||||||
return lookupDns(io, lookup_canon_name, &rc, options);
|
return lookupDns(io, lookup_canon_name, &rc, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DnsReply = struct {
|
fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, options: LookupOptions) LookupError!LookupResult {
|
||||||
buf: [512]u8,
|
|
||||||
len: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, options: LookupOptions) !LookupResult {
|
|
||||||
const family_records: [2]struct { af: IpAddress.Family, rr: u8 } = .{
|
const family_records: [2]struct { af: IpAddress.Family, rr: u8 } = .{
|
||||||
.{ .af = .ip6, .rr = std.posix.RR.A },
|
.{ .af = .ip6, .rr = std.posix.RR.A },
|
||||||
.{ .af = .ip4, .rr = std.posix.RR.AAAA },
|
.{ .af = .ip4, .rr = std.posix.RR.AAAA },
|
||||||
};
|
};
|
||||||
var query_buffers: [2][280]u8 = undefined;
|
var query_buffers: [2][280]u8 = undefined;
|
||||||
|
var answer_buffers: [2][512]u8 = undefined;
|
||||||
var queries_buffer: [2][]const u8 = undefined;
|
var queries_buffer: [2][]const u8 = undefined;
|
||||||
|
var answers_buffer: [2][]const u8 = undefined;
|
||||||
var nq: usize = 0;
|
var nq: usize = 0;
|
||||||
|
var next_answer_buffer: usize = 0;
|
||||||
|
|
||||||
for (family_records) |fr| {
|
for (family_records) |fr| {
|
||||||
if (options.family != fr.af) {
|
if (options.family != fr.af) {
|
||||||
|
|
@ -241,41 +237,123 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const queries = queries_buffer[0..nq];
|
var ip4_mapped: [ResolvConf.max_nameservers]IpAddress = undefined;
|
||||||
var replies_buffer: [2]DnsReply = undefined;
|
var any_ip6 = false;
|
||||||
var replies: Io.Queue(DnsReply) = .init(&replies_buffer);
|
for (rc.nameservers(), &ip4_mapped) |*ns, *m| {
|
||||||
try rc.sendMessage(io, queries, &replies);
|
m.* = .{ .ip6 = .fromAny(ns.*) };
|
||||||
|
any_ip6 = any_ip6 or ns.* == .ip6;
|
||||||
|
}
|
||||||
|
var socket = s: {
|
||||||
|
if (any_ip6) ip6: {
|
||||||
|
const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) };
|
||||||
|
const socket = ip6_addr.bind(io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) {
|
||||||
|
error.AddressFamilyUnsupported => break :ip6,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
break :s socket;
|
||||||
|
}
|
||||||
|
any_ip6 = false;
|
||||||
|
const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) };
|
||||||
|
const socket = try ip4_addr.bind(io, .{ .mode = .dgram });
|
||||||
|
break :s socket;
|
||||||
|
};
|
||||||
|
defer socket.close(io);
|
||||||
|
|
||||||
for (replies) |reply| {
|
const mapped_nameservers = if (any_ip6) ip4_mapped[0..rc.nameservers_len] else rc.nameservers();
|
||||||
if (reply.len < 4 or (reply[3] & 15) == 2) return error.TemporaryNameServerFailure;
|
const queries = queries_buffer[0..nq];
|
||||||
if ((reply[3] & 15) == 3) return .empty;
|
const answers = answers_buffer[0..queries.len];
|
||||||
if ((reply[3] & 15) != 0) return error.UnknownHostName;
|
for (answers) |*answer| answer.len = 0;
|
||||||
|
|
||||||
|
var now_ts = try io.now(.MONOTONIC);
|
||||||
|
const final_ts = now_ts.addDuration(.seconds(rc.timeout_seconds));
|
||||||
|
const attempt_duration: Io.Duration = .{
|
||||||
|
.nanoseconds = std.time.ns_per_s * @as(usize, rc.timeout_seconds) / rc.attempts,
|
||||||
|
};
|
||||||
|
|
||||||
|
send: while (now_ts.compare(.lt, final_ts)) : (now_ts = try io.now(.MONOTONIC)) {
|
||||||
|
var group: Io.Group = .init;
|
||||||
|
defer group.cancel(io);
|
||||||
|
|
||||||
|
for (queries, answers) |query, *answer| {
|
||||||
|
if (answer.len != 0) continue;
|
||||||
|
for (mapped_nameservers) |*ns| {
|
||||||
|
group.async(io, sendIgnoringResult, .{ io, socket.handle, ns, query });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout: Io.Timeout = .{ .deadline = now_ts.addDuration(attempt_duration) };
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const buf = &answer_buffers[next_answer_buffer];
|
||||||
|
const reply = socket.receiveTimeout(io, buf, timeout) catch |err| switch (err) {
|
||||||
|
error.Canceled => return error.Canceled,
|
||||||
|
error.Timeout => continue :send,
|
||||||
|
else => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ignore non-identifiable packets.
|
||||||
|
if (reply.len < 4) continue;
|
||||||
|
|
||||||
|
// Ignore replies from addresses we didn't send to.
|
||||||
|
const ns = for (mapped_nameservers) |*ns| {
|
||||||
|
if (reply.from.eql(ns)) break ns;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reply_msg = buf[0..reply.len];
|
||||||
|
|
||||||
|
// Find which query this answer goes with, if any.
|
||||||
|
const query, const answer = for (queries, answers) |query, *answer| {
|
||||||
|
if (reply_msg[0] == query[0] and reply_msg[1] == query[1]) break .{ query, answer };
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if (answer.len != 0) continue;
|
||||||
|
|
||||||
|
// Only accept positive or negative responses; retry immediately on
|
||||||
|
// server failure, and ignore all other codes such as refusal.
|
||||||
|
switch (reply_msg[3] & 15) {
|
||||||
|
0, 3 => {
|
||||||
|
answer.* = reply_msg;
|
||||||
|
next_answer_buffer += 1;
|
||||||
|
if (next_answer_buffer == answers.len) break :send;
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
group.async(io, sendIgnoringResult, .{ io, socket.handle, ns, query });
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error.NameServerFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addresses_len: usize = 0;
|
var addresses_len: usize = 0;
|
||||||
var canonical_name: ?HostName = null;
|
var canonical_name: ?HostName = null;
|
||||||
|
|
||||||
for (replies) |reply| {
|
for (answers) |answer| {
|
||||||
var it = DnsResponse.init(reply) catch {
|
var it = DnsResponse.init(answer) catch {
|
||||||
// TODO accept a diagnostics struct and append warnings
|
// TODO accept a diagnostics struct and append warnings
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
while (it.next() catch {
|
while (it.next() catch {
|
||||||
// TODO accept a diagnostics struct and append warnings
|
// TODO accept a diagnostics struct and append warnings
|
||||||
continue;
|
continue;
|
||||||
}) |answer| switch (answer.rr) {
|
}) |record| switch (record.rr) {
|
||||||
std.posix.RR.A => {
|
std.posix.RR.A => {
|
||||||
if (answer.data.len != 4) return error.InvalidDnsARecord;
|
if (record.data.len != 4) return error.InvalidDnsARecord;
|
||||||
options.addresses_buffer[addresses_len] = .{ .ip4 = .{
|
options.addresses_buffer[addresses_len] = .{ .ip4 = .{
|
||||||
.bytes = answer.data[0..4].*,
|
.bytes = record.data[0..4].*,
|
||||||
.port = options.port,
|
.port = options.port,
|
||||||
} };
|
} };
|
||||||
addresses_len += 1;
|
addresses_len += 1;
|
||||||
},
|
},
|
||||||
std.posix.RR.AAAA => {
|
std.posix.RR.AAAA => {
|
||||||
if (answer.data.len != 16) return error.InvalidDnsAAAARecord;
|
if (record.data.len != 16) return error.InvalidDnsAAAARecord;
|
||||||
options.addresses_buffer[addresses_len] = .{ .ip6 = .{
|
options.addresses_buffer[addresses_len] = .{ .ip6 = .{
|
||||||
.bytes = answer.data[0..16].*,
|
.bytes = record.data[0..16].*,
|
||||||
.port = options.port,
|
.port = options.port,
|
||||||
} };
|
} };
|
||||||
addresses_len += 1;
|
addresses_len += 1;
|
||||||
|
|
@ -285,7 +363,7 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio
|
||||||
@panic("TODO");
|
@panic("TODO");
|
||||||
//var tmp: [256]u8 = undefined;
|
//var tmp: [256]u8 = undefined;
|
||||||
//// Returns len of compressed name. strlen to get canon name.
|
//// Returns len of compressed name. strlen to get canon name.
|
||||||
//_ = try posix.dn_expand(packet, answer.data, &tmp);
|
//_ = try posix.dn_expand(packet, record.data, &tmp);
|
||||||
//const canon_name = mem.sliceTo(&tmp, 0);
|
//const canon_name = mem.sliceTo(&tmp, 0);
|
||||||
//if (isValidHostName(canon_name)) {
|
//if (isValidHostName(canon_name)) {
|
||||||
// ctx.canon.items.len = 0;
|
// ctx.canon.items.len = 0;
|
||||||
|
|
@ -304,6 +382,10 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio
|
||||||
return error.NameServerFailure;
|
return error.NameServerFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sendIgnoringResult(io: Io, socket_handle: Io.net.Socket.Handle, dest: *const IpAddress, msg: []const u8) void {
|
||||||
|
_ = io.vtable.netSend(io.userdata, socket_handle, dest, msg) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResult {
|
fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResult {
|
||||||
const file = Io.File.openAbsolute(io, "/etc/hosts", .{}) catch |err| switch (err) {
|
const file = Io.File.openAbsolute(io, "/etc/hosts", .{}) catch |err| switch (err) {
|
||||||
error.FileNotFound,
|
error.FileNotFound,
|
||||||
|
|
@ -523,7 +605,7 @@ pub fn connectTcp(host_name: HostName, io: Io, port: u16) ConnectTcpError!Stream
|
||||||
pub const ResolvConf = struct {
|
pub const ResolvConf = struct {
|
||||||
attempts: u32,
|
attempts: u32,
|
||||||
ndots: u32,
|
ndots: u32,
|
||||||
timeout: Io.Duration,
|
timeout_seconds: u32,
|
||||||
nameservers_buffer: [max_nameservers]IpAddress,
|
nameservers_buffer: [max_nameservers]IpAddress,
|
||||||
nameservers_len: usize,
|
nameservers_len: usize,
|
||||||
search_buffer: [max_len]u8,
|
search_buffer: [max_len]u8,
|
||||||
|
|
@ -539,7 +621,7 @@ pub const ResolvConf = struct {
|
||||||
.search_buffer = undefined,
|
.search_buffer = undefined,
|
||||||
.search_len = 0,
|
.search_len = 0,
|
||||||
.ndots = 1,
|
.ndots = 1,
|
||||||
.timeout = .seconds(5),
|
.timeout_seconds = 5,
|
||||||
.attempts = 2,
|
.attempts = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -589,7 +671,7 @@ pub const ResolvConf = struct {
|
||||||
switch (std.meta.stringToEnum(Option, name) orelse continue) {
|
switch (std.meta.stringToEnum(Option, name) orelse continue) {
|
||||||
.ndots => rc.ndots = @min(value, 15),
|
.ndots => rc.ndots = @min(value, 15),
|
||||||
.attempts => rc.attempts = @min(value, 10),
|
.attempts => rc.attempts = @min(value, 10),
|
||||||
.timeout => rc.timeout = .seconds(@min(value, 60)),
|
.timeout => rc.timeout_seconds = @min(value, 60),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.nameserver => {
|
.nameserver => {
|
||||||
|
|
@ -621,68 +703,6 @@ pub const ResolvConf = struct {
|
||||||
fn nameservers(rc: *const ResolvConf) []const IpAddress {
|
fn nameservers(rc: *const ResolvConf) []const IpAddress {
|
||||||
return rc.nameservers_buffer[0..rc.nameservers_len];
|
return rc.nameservers_buffer[0..rc.nameservers_len];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sendMessage(
|
|
||||||
rc: *const ResolvConf,
|
|
||||||
io: Io,
|
|
||||||
queries: []const []const u8,
|
|
||||||
replies: *Io.Queue(DnsReply),
|
|
||||||
) !void {
|
|
||||||
var ip4_mapped: [ResolvConf.max_nameservers]IpAddress = undefined;
|
|
||||||
var any_ip6 = false;
|
|
||||||
for (rc.nameservers(), &ip4_mapped) |*ns, *m| {
|
|
||||||
m.* = .{ .ip6 = .fromAny(ns.*) };
|
|
||||||
any_ip6 = any_ip6 or ns.* == .ip6;
|
|
||||||
}
|
|
||||||
|
|
||||||
const socket = s: {
|
|
||||||
if (any_ip6) ip6: {
|
|
||||||
const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) };
|
|
||||||
const socket = ip6_addr.bind(io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) {
|
|
||||||
error.AddressFamilyUnsupported => break :ip6,
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
break :s socket;
|
|
||||||
}
|
|
||||||
any_ip6 = false;
|
|
||||||
const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) };
|
|
||||||
const socket = try ip4_addr.bind(io, .{ .mode = .dgram });
|
|
||||||
break :s socket;
|
|
||||||
};
|
|
||||||
defer socket.close();
|
|
||||||
|
|
||||||
const mapped_nameservers = if (any_ip6) ip4_mapped[0..rc.nameservers_len] else rc.nameservers();
|
|
||||||
|
|
||||||
var group: Io.Group = .init;
|
|
||||||
defer group.cancel();
|
|
||||||
|
|
||||||
for (queries) |query| {
|
|
||||||
for (mapped_nameservers) |*ns| {
|
|
||||||
group.async(sendOneMessage, .{ io, query, ns });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deadline: Io.Deadline = .fromDuration(rc.timeout);
|
|
||||||
|
|
||||||
for (0..queries.len) |_| {
|
|
||||||
const msg = socket.receiveDeadline(deadline) catch |err| switch (err) {
|
|
||||||
error.Timeout => return error.Timeout,
|
|
||||||
error.Canceled => return error.Canceled,
|
|
||||||
else => continue,
|
|
||||||
};
|
|
||||||
_ = msg;
|
|
||||||
_ = replies;
|
|
||||||
@panic("TODO check msg for dns reply and put into replies queue");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sendOneMessage(
|
|
||||||
io: Io,
|
|
||||||
query: []const u8,
|
|
||||||
ns: *const IpAddress,
|
|
||||||
) void {
|
|
||||||
io.vtable.netSend(io.userdata, ns.*, &.{query}) catch |err| switch (err) {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
test ResolvConf {
|
test ResolvConf {
|
||||||
|
|
@ -702,7 +722,7 @@ test ResolvConf {
|
||||||
.search_buffer = undefined,
|
.search_buffer = undefined,
|
||||||
.search_len = 0,
|
.search_len = 0,
|
||||||
.ndots = 1,
|
.ndots = 1,
|
||||||
.timeout = .seconds(5),
|
.timeout_seconds = 5,
|
||||||
.attempts = 2,
|
.attempts = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue