mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 08:45:52 +00:00
basic DNS address resolution for linux without libc
This commit is contained in:
parent
d5865f5319
commit
67058b9b70
@ -72,11 +72,11 @@ pub const Buffer = struct {
|
||||
self.list.deinit();
|
||||
}
|
||||
|
||||
pub fn toSlice(self: *const Buffer) []u8 {
|
||||
pub fn toSlice(self: Buffer) []u8 {
|
||||
return self.list.toSlice()[0..self.len()];
|
||||
}
|
||||
|
||||
pub fn toSliceConst(self: *const Buffer) []const u8 {
|
||||
pub fn toSliceConst(self: Buffer) []const u8 {
|
||||
return self.list.toSliceConst()[0..self.len()];
|
||||
}
|
||||
|
||||
@ -91,11 +91,11 @@ pub const Buffer = struct {
|
||||
self.list.items[self.len()] = 0;
|
||||
}
|
||||
|
||||
pub fn isNull(self: *const Buffer) bool {
|
||||
pub fn isNull(self: Buffer) bool {
|
||||
return self.list.len == 0;
|
||||
}
|
||||
|
||||
pub fn len(self: *const Buffer) usize {
|
||||
pub fn len(self: Buffer) usize {
|
||||
return self.list.len - 1;
|
||||
}
|
||||
|
||||
@ -111,16 +111,16 @@ pub const Buffer = struct {
|
||||
self.list.toSlice()[old_len] = byte;
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Buffer, m: []const u8) bool {
|
||||
pub fn eql(self: Buffer, m: []const u8) bool {
|
||||
return mem.eql(u8, self.toSliceConst(), m);
|
||||
}
|
||||
|
||||
pub fn startsWith(self: *const Buffer, m: []const u8) bool {
|
||||
pub fn startsWith(self: Buffer, m: []const u8) bool {
|
||||
if (self.len() < m.len) return false;
|
||||
return mem.eql(u8, self.list.items[0..m.len], m);
|
||||
}
|
||||
|
||||
pub fn endsWith(self: *const Buffer, m: []const u8) bool {
|
||||
pub fn endsWith(self: Buffer, m: []const u8) bool {
|
||||
const l = self.len();
|
||||
if (l < m.len) return false;
|
||||
const start = l - m.len;
|
||||
@ -133,7 +133,7 @@ pub const Buffer = struct {
|
||||
}
|
||||
|
||||
/// For passing to C functions.
|
||||
pub fn ptr(self: *const Buffer) [*]u8 {
|
||||
pub fn ptr(self: Buffer) [*]u8 {
|
||||
return self.list.items.ptr;
|
||||
}
|
||||
};
|
||||
|
||||
@ -116,6 +116,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add
|
||||
pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
|
||||
pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
|
||||
pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
|
||||
pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
|
||||
pub extern "c" fn sendto(
|
||||
sockfd: fd_t,
|
||||
buf: *const c_void,
|
||||
len: usize,
|
||||
flags: u32,
|
||||
dest_addr: *const sockaddr,
|
||||
addrlen: socklen_t,
|
||||
) isize;
|
||||
|
||||
pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
|
||||
pub extern fn recvfrom(
|
||||
sockfd: fd_t,
|
||||
noalias buf: *c_void,
|
||||
len: usize,
|
||||
flags: u32,
|
||||
noalias src_addr: ?*sockaddr,
|
||||
noalias addrlen: ?*socklen_t,
|
||||
) isize;
|
||||
|
||||
pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
|
||||
pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
|
||||
pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
|
||||
@ -169,3 +189,5 @@ pub extern "c" fn getnameinfo(
|
||||
) c_int;
|
||||
|
||||
pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8;
|
||||
|
||||
pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int;
|
||||
|
||||
@ -466,6 +466,10 @@ pub const Loop = struct {
|
||||
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
|
||||
}
|
||||
|
||||
pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) !void {
|
||||
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
|
||||
}
|
||||
|
||||
pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {
|
||||
var resume_node = ResumeNode.Basic{
|
||||
.base = ResumeNode{
|
||||
|
||||
@ -704,7 +704,7 @@ pub const Dir = struct {
|
||||
|
||||
/// Call `File.close` on the result when done.
|
||||
pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {
|
||||
const flags = os.O_LARGEFILE | os.O_RDONLY;
|
||||
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
|
||||
const fd = try os.openatC(self.fd, sub_path, flags, 0);
|
||||
return File.openHandle(fd);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ pub const File = struct {
|
||||
const path_w = try windows.cStrToPrefixedFileW(path);
|
||||
return openReadW(&path_w);
|
||||
}
|
||||
const flags = os.O_LARGEFILE | os.O_RDONLY;
|
||||
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
|
||||
const fd = try os.openC(path, flags, 0);
|
||||
return openHandle(fd);
|
||||
}
|
||||
|
||||
592
lib/std/net.zig
592
lib/std/net.zig
@ -4,6 +4,7 @@ const assert = std.debug.assert;
|
||||
const net = @This();
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
const fs = std.fs;
|
||||
|
||||
pub const TmpWinAddr = struct {
|
||||
family: u8,
|
||||
@ -285,7 +286,7 @@ test "std.net.parseIp6" {
|
||||
std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
|
||||
}
|
||||
|
||||
pub fn connectUnixSocket(path: []const u8) !std.fs.File {
|
||||
pub fn connectUnixSocket(path: []const u8) !fs.File {
|
||||
const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
|
||||
const sockfd = try os.socket(
|
||||
os.AF_UNIX,
|
||||
@ -312,7 +313,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File {
|
||||
try os.connect(sockfd, &sock_addr, size);
|
||||
}
|
||||
|
||||
return std.fs.File.openHandle(sockfd);
|
||||
return fs.File.openHandle(sockfd);
|
||||
}
|
||||
|
||||
pub const AddressList = struct {
|
||||
@ -356,7 +357,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
|
||||
|
||||
const hints = os.addrinfo{
|
||||
.flags = c.AI_NUMERICSERV,
|
||||
.family = os.AF_UNSPEC,
|
||||
.family = os.AF_INET, // TODO os.AF_UNSPEC,
|
||||
.socktype = os.SOCK_STREAM,
|
||||
.protocol = os.IPPROTO_TCP,
|
||||
.canonname = null,
|
||||
@ -413,40 +414,41 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
|
||||
return result;
|
||||
}
|
||||
if (builtin.os == .linux) {
|
||||
const flags = os.AI_NUMERICSERV;
|
||||
const flags = std.c.AI_NUMERICSERV;
|
||||
const family = os.AF_INET; //TODO os.AF_UNSPEC;
|
||||
// The limit of 48 results is a non-sharp bound on the number of addresses
|
||||
// that can fit in one 512-byte DNS packet full of v4 results and a second
|
||||
// packet full of v6 results. Due to headers, the actual limit is lower.
|
||||
var buf: [48]LookupAddr = undefined;
|
||||
var canon_buf: [256]u8 = undefined;
|
||||
var canon_len: usize = 0;
|
||||
const cnt = try linuxLookupName(buf[0..], &canon_buf, &canon_len, name, family, flags);
|
||||
var addrs = std.ArrayList(LookupAddr).init(allocator);
|
||||
defer addrs.deinit();
|
||||
|
||||
result.addrs = try arena.alloc(Address, cnt);
|
||||
var canon = std.Buffer.initNull(allocator);
|
||||
defer canon.deinit();
|
||||
|
||||
if (canon_len != 0) {
|
||||
result.canon_name = try mem.dupe(arena, u8, canon_buf[0..canon_len]);
|
||||
try linuxLookupName(&addrs, &canon, name, family, flags);
|
||||
|
||||
result.addrs = try arena.alloc(Address, addrs.len);
|
||||
if (!canon.isNull()) {
|
||||
result.canon_name = canon.toOwnedSlice();
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < cnt) : (i += 1) {
|
||||
const os_addr = if (buf[i].family == os.AF_INET6)
|
||||
for (addrs.toSliceConst()) |addr, i| {
|
||||
const os_addr = if (addr.family == os.AF_INET6)
|
||||
os.sockaddr{
|
||||
.in6 = os.sockaddr_in6{
|
||||
.family = buf[i].family,
|
||||
.family = addr.family,
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.flowinfo = 0,
|
||||
.addr = buf[i].addr,
|
||||
.scope_id = buf[i].scope_id,
|
||||
.addr = addr.addr,
|
||||
.scope_id = addr.scope_id,
|
||||
},
|
||||
}
|
||||
else
|
||||
os.sockaddr{
|
||||
.in = os.sockaddr_in{
|
||||
.family = buf[i].family,
|
||||
.family = addr.family,
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.addr = @ptrCast(*align(1) u32, &buf[i].addr).*,
|
||||
.addr = @ptrCast(*align(1) const u32, &addr.addr).*,
|
||||
.zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
},
|
||||
};
|
||||
@ -466,51 +468,52 @@ const LookupAddr = struct {
|
||||
};
|
||||
|
||||
fn linuxLookupName(
|
||||
buf: []LookupAddr,
|
||||
canon_buf: []u8,
|
||||
canon_len: *usize,
|
||||
addrs: *std.ArrayList(LookupAddr),
|
||||
canon: *std.Buffer,
|
||||
opt_name: ?[]const u8,
|
||||
family: i32,
|
||||
flags: u32,
|
||||
) !usize {
|
||||
var cnt: usize = 0;
|
||||
) !void {
|
||||
if (opt_name) |name| {
|
||||
// reject empty name and check len so it fits into temp bufs
|
||||
if (name.len >= 254) return error.UnknownName;
|
||||
mem.copy(u8, canon_buf, name);
|
||||
canon_len.* = name.len;
|
||||
|
||||
cnt = (linuxLookupNameFromNumeric(buf, name, family) catch |err| switch (err) {
|
||||
error.ExpectedIPv6ButFoundIPv4 => unreachable,
|
||||
error.ExpectedIPv4ButFoundIPv6 => unreachable,
|
||||
});
|
||||
if (cnt == 0 and (flags & os.AI_NUMERICHOST) == 0) {
|
||||
cnt = try linuxLookupNameFromHosts(buf, canon_buf, canon_len, name, family);
|
||||
if (cnt == 0) {
|
||||
cnt = try linuxLookupNameFromDnsSearch(buf, canon_buf, canon_len, name, family);
|
||||
try canon.replaceContents(name);
|
||||
try linuxLookupNameFromNumeric(addrs, name, family);
|
||||
if (addrs.len == 0 and (flags & std.c.AI_NUMERICHOST) == 0) {
|
||||
try linuxLookupNameFromHosts(addrs, canon, name, family);
|
||||
if (addrs.len == 0) {
|
||||
try linuxLookupNameFromDnsSearch(addrs, canon, name, family);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canon_len.* = 0;
|
||||
cnt = linuxLookupNameFromNull(buf, family, flags);
|
||||
try canon.resize(0);
|
||||
try linuxLookupNameFromNull(addrs, family, flags);
|
||||
}
|
||||
if (cnt == 0) return error.UnknownName;
|
||||
if (addrs.len == 0) return error.UnknownName;
|
||||
|
||||
// No further processing is needed if there are fewer than 2
|
||||
// results or if there are only IPv4 results.
|
||||
if (cnt == 1 or family == os.AF_INET) return cnt;
|
||||
if (addrs.len == 1 or family == os.AF_INET) return;
|
||||
|
||||
@panic("port the RFC 3484/6724 destination address selection from musl libc");
|
||||
}
|
||||
|
||||
fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32) !usize {
|
||||
fn linuxLookupNameFromNumericUnspec(addrs: *std.ArrayList(LookupAddr), name: []const u8) !void {
|
||||
return linuxLookupNameFromNumeric(addrs, name, os.AF_UNSPEC) catch |err| switch (err) {
|
||||
error.ExpectedIPv6ButFoundIPv4 => unreachable,
|
||||
error.ExpectedIPv4ButFoundIPv6 => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
fn linuxLookupNameFromNumeric(addrs: *std.ArrayList(LookupAddr), name: []const u8, family: i32) !void {
|
||||
if (parseIp4(name)) |ip4| {
|
||||
if (family == os.AF_INET6) return error.ExpectedIPv6ButFoundIPv4;
|
||||
const item = try addrs.addOne();
|
||||
// TODO [0..4] should return *[4]u8, making this pointer cast unnecessary
|
||||
mem.writeIntNative(u32, @ptrCast(*[4]u8, &buf[0].addr), ip4);
|
||||
buf[0].family = os.AF_INET;
|
||||
buf[0].scope_id = 0;
|
||||
return 1;
|
||||
mem.writeIntNative(u32, @ptrCast(*[4]u8, &item.addr), ip4);
|
||||
item.family = os.AF_INET;
|
||||
item.scope_id = 0;
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.Overflow,
|
||||
error.InvalidEnd,
|
||||
@ -521,10 +524,11 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)
|
||||
|
||||
if (parseIp6(name)) |ip6| {
|
||||
if (family == os.AF_INET) return error.ExpectedIPv4ButFoundIPv6;
|
||||
@memcpy(&buf[0].addr, &ip6.addr, 16);
|
||||
buf[0].family = os.AF_INET6;
|
||||
buf[0].scope_id = ip6.scope_id;
|
||||
return 1;
|
||||
const item = try addrs.addOne();
|
||||
@memcpy(&item.addr, &ip6.addr, 16);
|
||||
item.family = os.AF_INET6;
|
||||
item.scope_id = ip6.scope_id;
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.Overflow,
|
||||
error.InvalidEnd,
|
||||
@ -532,64 +536,54 @@ fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32)
|
||||
error.Incomplete,
|
||||
=> {},
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn linuxLookupNameFromNull(buf: []LookupAddr, family: i32, flags: u32) usize {
|
||||
var cnt: usize = 0;
|
||||
if ((flags & os.AI_PASSIVE) != 0) {
|
||||
fn linuxLookupNameFromNull(addrs: *std.ArrayList(LookupAddr), family: i32, flags: u32) !void {
|
||||
if ((flags & std.c.AI_PASSIVE) != 0) {
|
||||
if (family != os.AF_INET6) {
|
||||
buf[cnt] = LookupAddr{
|
||||
(try addrs.addOne()).* = LookupAddr{
|
||||
.family = os.AF_INET,
|
||||
.addr = [1]u8{0} ** 16,
|
||||
};
|
||||
cnt += 1;
|
||||
}
|
||||
if (family != os.AF_INET) {
|
||||
buf[cnt] = LookupAddr{
|
||||
(try addrs.addOne()).* = LookupAddr{
|
||||
.family = os.AF_INET6,
|
||||
.addr = [1]u8{0} ** 16,
|
||||
};
|
||||
cnt += 1;
|
||||
}
|
||||
} else {
|
||||
if (family != os.AF_INET6) {
|
||||
buf[cnt] = LookupAddr{
|
||||
(try addrs.addOne()).* = LookupAddr{
|
||||
.family = os.AF_INET,
|
||||
.addr = [4]u8{ 127, 0, 0, 1 } ++ ([1]u8{0} ** 12),
|
||||
};
|
||||
cnt += 1;
|
||||
}
|
||||
if (family != os.AF_INET) {
|
||||
buf[cnt] = LookupAddr{
|
||||
(try addrs.addOne()).* = LookupAddr{
|
||||
.family = os.AF_INET6,
|
||||
.addr = ([1]u8{0} ** 15) ++ [1]u8{1},
|
||||
};
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
fn linuxLookupNameFromHosts(
|
||||
buf: []LookupAddr,
|
||||
canon_buf: []u8,
|
||||
canon_len: *usize,
|
||||
addrs: *std.ArrayList(LookupAddr),
|
||||
canon: *std.Buffer,
|
||||
name: []const u8,
|
||||
family: i32,
|
||||
) !usize {
|
||||
const file = std.fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) {
|
||||
) !void {
|
||||
const file = fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.AccessDenied,
|
||||
=> return 0,
|
||||
=> return,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
var cnt: usize = 0;
|
||||
const stream = &std.io.BufferedInStream(std.fs.File.ReadError).init(&file.inStream().stream).stream;
|
||||
const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
|
||||
var line_buf: [512]u8 = undefined;
|
||||
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
|
||||
error.StreamTooLong => blk: {
|
||||
@ -612,26 +606,20 @@ fn linuxLookupNameFromHosts(
|
||||
}
|
||||
} else continue;
|
||||
|
||||
switch (linuxLookupNameFromNumeric(buf[cnt..], ip_text, family) catch |err| switch (err) {
|
||||
const prev_len = addrs.len;
|
||||
linuxLookupNameFromNumeric(addrs, ip_text, family) catch |err| switch (err) {
|
||||
error.ExpectedIPv6ButFoundIPv4 => continue,
|
||||
error.ExpectedIPv4ButFoundIPv6 => continue,
|
||||
}) {
|
||||
0 => continue,
|
||||
1 => {
|
||||
// first name is canonical name
|
||||
const name_text = first_name_text.?;
|
||||
if (isValidHostName(name_text)) {
|
||||
mem.copy(u8, canon_buf, name_text);
|
||||
canon_len.* = name_text.len;
|
||||
}
|
||||
|
||||
cnt += 1;
|
||||
if (cnt == buf.len) break;
|
||||
},
|
||||
else => unreachable,
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
if (addrs.len > prev_len) {
|
||||
// first name is canonical name
|
||||
const name_text = first_name_text.?;
|
||||
if (isValidHostName(name_text)) {
|
||||
try canon.replaceContents(name_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
pub fn isValidHostName(hostname: []const u8) bool {
|
||||
@ -647,50 +635,414 @@ pub fn isValidHostName(hostname: []const u8) bool {
|
||||
}
|
||||
|
||||
fn linuxLookupNameFromDnsSearch(
|
||||
buf: []LookupAddr,
|
||||
canon_buf: []u8,
|
||||
canon_len: *usize,
|
||||
addrs: *std.ArrayList(LookupAddr),
|
||||
canon: *std.Buffer,
|
||||
name: []const u8,
|
||||
family: i32,
|
||||
) !usize {
|
||||
var search: [256]u8 = undefined;
|
||||
const resolv_conf = try getResolvConf(&search);
|
||||
) !void {
|
||||
var rc: ResolvConf = undefined;
|
||||
try getResolvConf(addrs.allocator, &rc);
|
||||
defer rc.deinit();
|
||||
|
||||
// Count dots, suppress search when >=ndots or name ends in
|
||||
// a dot, which is an explicit request for global scope.
|
||||
//var dots: usize = 0;
|
||||
//for (name) |byte| {
|
||||
// if (byte == '.') dots += 1;
|
||||
//}
|
||||
var dots: usize = 0;
|
||||
for (name) |byte| {
|
||||
if (byte == '.') dots += 1;
|
||||
}
|
||||
|
||||
//if (dots >= conf.ndots || name[l-1]=='.') *search = 0;
|
||||
const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, "."))
|
||||
[_]u8{}
|
||||
else
|
||||
rc.search.toSliceConst();
|
||||
|
||||
//// Strip final dot for canon, fail if multiple trailing dots.
|
||||
//if (name[l-1]=='.') l--;
|
||||
//if (!l || name[l-1]=='.') return EAI_NONAME;
|
||||
var canon_name = name;
|
||||
|
||||
//// This can never happen; the caller already checked length.
|
||||
//if (l >= 256) return EAI_NONAME;
|
||||
// Strip final dot for canon, fail if multiple trailing dots.
|
||||
if (mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1;
|
||||
if (mem.endsWith(u8, canon_name, ".")) return error.UnknownName;
|
||||
|
||||
//// Name with search domain appended is setup in canon[]. This both
|
||||
//// provides the desired default canonical name (if the requested
|
||||
//// name is not a CNAME record) and serves as a buffer for passing
|
||||
//// the full requested name to name_from_dns.
|
||||
//memcpy(canon, name, l);
|
||||
//canon[l] = '.';
|
||||
// Name with search domain appended is setup in canon[]. This both
|
||||
// provides the desired default canonical name (if the requested
|
||||
// name is not a CNAME record) and serves as a buffer for passing
|
||||
// the full requested name to name_from_dns.
|
||||
try canon.resize(canon_name.len);
|
||||
mem.copy(u8, canon.toSlice(), canon_name);
|
||||
try canon.appendByte('.');
|
||||
|
||||
//for (p=search; *p; p=z) {
|
||||
// for (; isspace(*p); p++);
|
||||
// for (z=p; *z && !isspace(*z); z++);
|
||||
// if (z==p) break;
|
||||
// if (z-p < 256 - l - 1) {
|
||||
// memcpy(canon+l+1, p, z-p);
|
||||
// canon[z-p+1+l] = 0;
|
||||
// int cnt = name_from_dns(buf, canon, canon, family, &conf);
|
||||
// if (cnt) return cnt;
|
||||
var tok_it = mem.tokenize(search, " \t");
|
||||
while (tok_it.next()) |tok| {
|
||||
canon.shrink(canon_name.len + 1);
|
||||
try canon.append(tok);
|
||||
try linuxLookupNameFromDns(addrs, canon, canon.toSliceConst(), family, rc);
|
||||
if (addrs.len != 0) return;
|
||||
}
|
||||
|
||||
canon.shrink(canon_name.len);
|
||||
return linuxLookupNameFromDns(addrs, canon, name, family, rc);
|
||||
}
|
||||
|
||||
const dpc_ctx = struct {
|
||||
addrs: *std.ArrayList(LookupAddr),
|
||||
canon: *std.Buffer,
|
||||
};
|
||||
|
||||
fn linuxLookupNameFromDns(
|
||||
addrs: *std.ArrayList(LookupAddr),
|
||||
canon: *std.Buffer,
|
||||
name: []const u8,
|
||||
family: i32,
|
||||
rc: ResolvConf,
|
||||
) !void {
|
||||
var ctx = dpc_ctx{
|
||||
.addrs = addrs,
|
||||
.canon = canon,
|
||||
};
|
||||
const AfRr = struct {
|
||||
af: i32,
|
||||
rr: u8,
|
||||
};
|
||||
const afrrs = [_]AfRr{
|
||||
AfRr{ .af = os.AF_INET6, .rr = os.RR_A },
|
||||
AfRr{ .af = os.AF_INET, .rr = os.RR_AAAA },
|
||||
};
|
||||
var qbuf: [2][280]u8 = undefined;
|
||||
var abuf: [2][512]u8 = undefined;
|
||||
var qp: [2][]const u8 = undefined;
|
||||
const apbuf = [2][]u8{ &abuf[0], &abuf[1] };
|
||||
var nq: usize = 0;
|
||||
|
||||
for (afrrs) |afrr| {
|
||||
if (family != afrr.af) {
|
||||
const len = os.res_mkquery(0, name, 1, afrr.rr, [_]u8{}, null, &qbuf[nq]);
|
||||
qp[nq] = qbuf[nq][0..len];
|
||||
nq += 1;
|
||||
}
|
||||
}
|
||||
|
||||
var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] };
|
||||
try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < nq) : (i += 1) {
|
||||
dnsParse(ap[i], ctx, dnsParseCallback) catch {};
|
||||
}
|
||||
|
||||
if (addrs.len != 0) return;
|
||||
if (ap[0].len < 4 or (ap[0][3] & 15) == 2) return error.TemporaryNameServerFailure;
|
||||
if ((ap[0][3] & 15) == 0) return error.UnknownName;
|
||||
if ((ap[0][3] & 15) == 3) return;
|
||||
return error.NameServerFailure;
|
||||
}
|
||||
|
||||
const ResolvConf = struct {
|
||||
attempts: u32,
|
||||
ndots: u32,
|
||||
timeout: u32,
|
||||
search: std.Buffer,
|
||||
ns: std.ArrayList(LookupAddr),
|
||||
|
||||
fn deinit(rc: *ResolvConf) void {
|
||||
rc.ns.deinit();
|
||||
rc.search.deinit();
|
||||
rc.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/// Ignores lines longer than 512 bytes.
|
||||
/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
|
||||
fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
|
||||
rc.* = ResolvConf{
|
||||
.ns = std.ArrayList(LookupAddr).init(allocator),
|
||||
.search = std.Buffer.initNull(allocator),
|
||||
.ndots = 1,
|
||||
.timeout = 5,
|
||||
.attempts = 2,
|
||||
};
|
||||
errdefer rc.deinit();
|
||||
|
||||
const file = fs.File.openReadC(c"/etc/resolv.conf") catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.AccessDenied,
|
||||
=> return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1"),
|
||||
else => |e| return e,
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
var cnt: usize = 0;
|
||||
const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream;
|
||||
var line_buf: [512]u8 = undefined;
|
||||
while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) {
|
||||
error.StreamTooLong => blk: {
|
||||
// Skip to the delimiter in the stream, to fix parsing
|
||||
try stream.skipUntilDelimiterOrEof('\n');
|
||||
// Give an empty line to the while loop, which will be skipped.
|
||||
break :blk line_buf[0..0];
|
||||
},
|
||||
else => |e| return e,
|
||||
}) |line| {
|
||||
const no_comment_line = mem.separate(line, "#").next().?;
|
||||
var line_it = mem.tokenize(no_comment_line, " \t");
|
||||
|
||||
const token = line_it.next() orelse continue;
|
||||
if (mem.eql(u8, token, "options")) {
|
||||
while (line_it.next()) |sub_tok| {
|
||||
var colon_it = mem.separate(sub_tok, ":");
|
||||
const name = colon_it.next().?;
|
||||
const value_txt = colon_it.next() orelse continue;
|
||||
const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) {
|
||||
error.Overflow => 255,
|
||||
error.InvalidCharacter => continue,
|
||||
};
|
||||
if (mem.eql(u8, name, "ndots")) {
|
||||
rc.ndots = std.math.min(value, 15);
|
||||
} else if (mem.eql(u8, name, "attempts")) {
|
||||
rc.attempts = std.math.min(value, 10);
|
||||
} else if (mem.eql(u8, name, "timeout")) {
|
||||
rc.timeout = std.math.min(value, 60);
|
||||
}
|
||||
}
|
||||
} else if (mem.eql(u8, token, "nameserver")) {
|
||||
const ip_txt = line_it.next() orelse continue;
|
||||
try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt);
|
||||
} else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) {
|
||||
try rc.search.replaceContents(line_it.rest());
|
||||
}
|
||||
}
|
||||
|
||||
if (rc.ns.len == 0) {
|
||||
return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1");
|
||||
}
|
||||
}
|
||||
|
||||
fn eqlSockAddr(a: *const os.sockaddr, b: *const os.sockaddr, len: usize) bool {
|
||||
const a_bytes = @ptrCast([*]const u8, a)[0..len];
|
||||
const b_bytes = @ptrCast([*]const u8, b)[0..len];
|
||||
return mem.eql(u8, a_bytes, b_bytes);
|
||||
}
|
||||
|
||||
fn resMSendRc(
|
||||
queries: []const []const u8,
|
||||
answers: [][]u8,
|
||||
answer_bufs: []const []u8,
|
||||
rc: ResolvConf,
|
||||
) !void {
|
||||
const timeout = 1000 * rc.timeout;
|
||||
const attempts = rc.attempts;
|
||||
|
||||
var sl: os.socklen_t = @sizeOf(os.sockaddr_in);
|
||||
var family: os.sa_family_t = os.AF_INET;
|
||||
|
||||
var ns_list = std.ArrayList(os.sockaddr).init(rc.ns.allocator);
|
||||
defer ns_list.deinit();
|
||||
|
||||
try ns_list.resize(rc.ns.len);
|
||||
const ns = ns_list.toSlice();
|
||||
|
||||
for (rc.ns.toSliceConst()) |iplit, i| {
|
||||
if (iplit.family == os.AF_INET) {
|
||||
ns[i] = os.sockaddr{
|
||||
.in = os.sockaddr_in{
|
||||
.family = os.AF_INET,
|
||||
.port = mem.nativeToBig(u16, 53),
|
||||
.addr = mem.readIntNative(u32, @ptrCast(*const [4]u8, &iplit.addr)),
|
||||
.zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
},
|
||||
};
|
||||
} else {
|
||||
ns[i] = os.sockaddr{
|
||||
.in6 = os.sockaddr_in6{
|
||||
.family = os.AF_INET6,
|
||||
.port = mem.nativeToBig(u16, 53),
|
||||
.flowinfo = 0,
|
||||
.addr = iplit.addr,
|
||||
.scope_id = iplit.scope_id,
|
||||
},
|
||||
};
|
||||
sl = @sizeOf(os.sockaddr_in6);
|
||||
family = os.AF_INET6;
|
||||
}
|
||||
}
|
||||
|
||||
// Get local address and open/bind a socket
|
||||
var sa: os.sockaddr = undefined;
|
||||
@memset(@ptrCast([*]u8, &sa), 0, @sizeOf(os.sockaddr));
|
||||
sa.in.family = family;
|
||||
const flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK;
|
||||
const fd = os.socket(family, flags, 0) catch |err| switch (err) {
|
||||
error.AddressFamilyNotSupported => blk: {
|
||||
// Handle case where system lacks IPv6 support
|
||||
if (family == os.AF_INET6) {
|
||||
family = os.AF_INET;
|
||||
break :blk try os.socket(os.AF_INET, flags, 0);
|
||||
}
|
||||
return err;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
defer os.close(fd);
|
||||
try os.bind(fd, &sa, sl);
|
||||
|
||||
// Past this point, there are no errors. Each individual query will
|
||||
// yield either no reply (indicated by zero length) or an answer
|
||||
// packet which is up to the caller to interpret.
|
||||
|
||||
// Convert any IPv4 addresses in a mixed environment to v4-mapped
|
||||
// TODO
|
||||
//if (family == AF_INET6) {
|
||||
// setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
|
||||
// for (i=0; i<nns; i++) {
|
||||
// if (ns[i].sin.sin_family != AF_INET) continue;
|
||||
// memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
|
||||
// &ns[i].sin.sin_addr, 4);
|
||||
// memcpy(ns[i].sin6.sin6_addr.s6_addr,
|
||||
// "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
|
||||
// ns[i].sin6.sin6_family = AF_INET6;
|
||||
// ns[i].sin6.sin6_flowinfo = 0;
|
||||
// ns[i].sin6.sin6_scope_id = 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
//canon[l] = 0;
|
||||
//return name_from_dns(buf, canon, name, family, &conf);
|
||||
var pfd = [1]os.pollfd{os.pollfd{
|
||||
.fd = fd,
|
||||
.events = os.POLLIN,
|
||||
.revents = undefined,
|
||||
}};
|
||||
const retry_interval = timeout / attempts;
|
||||
var next: u32 = 0;
|
||||
var t2: usize = std.time.milliTimestamp();
|
||||
var t0 = t2;
|
||||
var t1 = t2 - retry_interval;
|
||||
|
||||
var servfail_retry: usize = undefined;
|
||||
|
||||
outer: while (t2 - t0 < timeout) : (t2 = std.time.milliTimestamp()) {
|
||||
if (t2 - t1 >= retry_interval) {
|
||||
// Query all configured nameservers in parallel
|
||||
var i: usize = 0;
|
||||
while (i < queries.len) : (i += 1) {
|
||||
if (answers[i].len == 0) {
|
||||
var j: usize = 0;
|
||||
while (j < ns.len) : (j += 1) {
|
||||
_ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j], sl) catch undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
t1 = t2;
|
||||
servfail_retry = 2 * queries.len;
|
||||
}
|
||||
|
||||
// Wait for a response, or until time to retry
|
||||
const clamped_timeout = std.math.min(u31(std.math.maxInt(u31)), t1 + retry_interval - t2);
|
||||
const nevents = os.poll(&pfd, clamped_timeout) catch 0;
|
||||
if (nevents == 0) continue;
|
||||
|
||||
while (true) {
|
||||
var sl_copy = sl;
|
||||
const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa, &sl_copy) catch break;
|
||||
|
||||
// Ignore non-identifiable packets
|
||||
if (rlen < 4) continue;
|
||||
|
||||
// Ignore replies from addresses we didn't send to
|
||||
var j: usize = 0;
|
||||
while (j < ns.len and !eqlSockAddr(&ns[j], &sa, sl)) : (j += 1) {}
|
||||
if (j == ns.len) continue;
|
||||
|
||||
// Find which query this answer goes with, if any
|
||||
var i: usize = next;
|
||||
while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or
|
||||
answer_bufs[next][1] != queries[i][1])) : (i += 1)
|
||||
{}
|
||||
|
||||
if (i == queries.len) continue;
|
||||
if (answers[i].len != 0) continue;
|
||||
|
||||
// Only accept positive or negative responses;
|
||||
// retry immediately on server failure, and ignore
|
||||
// all other codes such as refusal.
|
||||
switch (answer_bufs[next][3] & 15) {
|
||||
0, 3 => {},
|
||||
2 => if (servfail_retry != 0) {
|
||||
servfail_retry -= 1;
|
||||
_ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j], sl) catch undefined;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
|
||||
// Store answer in the right slot, or update next
|
||||
// available temp slot if it's already in place.
|
||||
answers[i].len = rlen;
|
||||
if (i == next) {
|
||||
while (next < queries.len and answers[next].len != 0) : (next += 1) {}
|
||||
} else {
|
||||
mem.copy(u8, answer_bufs[i], answer_bufs[next][0..rlen]);
|
||||
}
|
||||
|
||||
if (next == queries.len) break :outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dnsParse(
|
||||
r: []const u8,
|
||||
ctx: var,
|
||||
comptime callback: var,
|
||||
) !void {
|
||||
if (r.len < 12) return error.InvalidDnsPacket;
|
||||
if ((r[3] & 15) != 0) return;
|
||||
var p = r.ptr + 12;
|
||||
var qdcount = r[4] * usize(256) + r[5];
|
||||
var ancount = r[6] * usize(256) + r[7];
|
||||
if (qdcount + ancount > 64) return error.InvalidDnsPacket;
|
||||
while (qdcount != 0) {
|
||||
qdcount -= 1;
|
||||
while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1;
|
||||
if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6)
|
||||
return error.InvalidDnsPacket;
|
||||
p += usize(5) + @boolToInt(p[0] != 0);
|
||||
}
|
||||
while (ancount != 0) {
|
||||
ancount -= 1;
|
||||
while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1;
|
||||
if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6)
|
||||
return error.InvalidDnsPacket;
|
||||
p += usize(1) + @boolToInt(p[0] != 0);
|
||||
const len = p[8] * usize(256) + p[9];
|
||||
if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;
|
||||
try callback(ctx, p[1], p[10 .. 10 + len], r);
|
||||
p += 10 + len;
|
||||
}
|
||||
}
|
||||
|
||||
fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
|
||||
var tmp: [256]u8 = undefined;
|
||||
switch (rr) {
|
||||
os.RR_A => {
|
||||
if (data.len != 4) return error.InvalidDnsARecord;
|
||||
const new_addr = try ctx.addrs.addOne();
|
||||
new_addr.* = LookupAddr{
|
||||
.family = os.AF_INET,
|
||||
.addr = undefined,
|
||||
};
|
||||
mem.copy(u8, &new_addr.addr, data);
|
||||
},
|
||||
os.RR_AAAA => {
|
||||
if (data.len != 16) return error.InvalidDnsAAAARecord;
|
||||
const new_addr = try ctx.addrs.addOne();
|
||||
new_addr.* = LookupAddr{
|
||||
.family = os.AF_INET6,
|
||||
.addr = undefined,
|
||||
};
|
||||
mem.copy(u8, &new_addr.addr, data);
|
||||
},
|
||||
os.RR_CNAME => {
|
||||
@panic("TODO dn_expand");
|
||||
//if (__dn_expand(packet, (const unsigned char *)packet + 512,
|
||||
// data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp))
|
||||
// strcpy(ctx->canon, tmp);
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
||||
259
lib/std/os.zig
259
lib/std/os.zig
@ -1508,16 +1508,17 @@ pub const SocketError = error{
|
||||
ProtocolNotSupported,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {
|
||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
|
||||
const rc = system.socket(domain, socket_type, protocol);
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(i32, rc),
|
||||
0 => return @intCast(fd_t, rc),
|
||||
EACCES => return error.PermissionDenied,
|
||||
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
|
||||
EINVAL => return error.ProtocolFamilyNotAvailable,
|
||||
EMFILE => return error.ProcessFdQuotaExceeded,
|
||||
ENFILE => return error.SystemFdQuotaExceeded,
|
||||
ENOBUFS, ENOMEM => return error.SystemResources,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
EPROTONOSUPPORT => return error.ProtocolNotSupported,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
@ -1559,17 +1560,17 @@ pub const BindError = error{
|
||||
} || UnexpectedError;
|
||||
|
||||
/// addr is `*const T` where T is one of the sockaddr
|
||||
pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {
|
||||
const rc = system.bind(fd, addr, @sizeOf(sockaddr));
|
||||
pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
|
||||
const rc = system.bind(sockfd, addr, len);
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EADDRINUSE => return error.AddressInUse,
|
||||
EBADF => unreachable, // always a race condition if this error is returned
|
||||
EINVAL => unreachable,
|
||||
ENOTSOCK => unreachable,
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
ENOTSOCK => unreachable, // invalid `sockfd`
|
||||
EADDRNOTAVAIL => return error.AddressNotAvailable,
|
||||
EFAULT => unreachable,
|
||||
EFAULT => unreachable, // invalid `addr` pointer
|
||||
ELOOP => return error.SymLinkLoop,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
@ -2833,3 +2834,245 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
|
||||
|
||||
@compileError("TODO implement gethostname for this OS");
|
||||
}
|
||||
|
||||
pub fn res_mkquery(
|
||||
op: u4,
|
||||
dname: []const u8,
|
||||
class: u8,
|
||||
ty: u8,
|
||||
data: []const u8,
|
||||
newrr: ?[*]const u8,
|
||||
buf: []u8,
|
||||
) usize {
|
||||
var name = dname;
|
||||
if (mem.endsWith(u8, name, ".")) name.len -= 1;
|
||||
assert(name.len <= 253);
|
||||
const n = 17 + name.len + @boolToInt(name.len != 0);
|
||||
|
||||
// Construct query template - ID will be filled later
|
||||
var q: [280]u8 = undefined;
|
||||
@memset(&q, 0, n);
|
||||
q[2] = u8(op) * 8 + 1;
|
||||
q[5] = 1;
|
||||
mem.copy(u8, q[13..], name);
|
||||
var i: usize = 13;
|
||||
var j: usize = undefined;
|
||||
while (q[i] != 0) : (i = j + 1) {
|
||||
j = i;
|
||||
while (q[j] != 0 and q[j] != '.') : (j += 1) {}
|
||||
// TODO determine the circumstances for this and whether or
|
||||
// not this should be an error.
|
||||
if (j - i - 1 > 62) unreachable;
|
||||
q[i - 1] = @intCast(u8, j - i);
|
||||
}
|
||||
q[i + 1] = ty;
|
||||
q[i + 3] = class;
|
||||
|
||||
// Make a reasonably unpredictable id
|
||||
var ts: timespec = undefined;
|
||||
clock_gettime(CLOCK_REALTIME, &ts) catch {};
|
||||
const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count);
|
||||
const unsec = @bitCast(UInt, ts.tv_nsec);
|
||||
const id = @truncate(u32, unsec + unsec / 65536);
|
||||
q[0] = @truncate(u8, id / 256);
|
||||
q[1] = @truncate(u8, id);
|
||||
|
||||
mem.copy(u8, buf, q[0..n]);
|
||||
return n;
|
||||
}
|
||||
|
||||
pub const SendError = error{
|
||||
/// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
|
||||
/// on the destination socket file, or search permission is denied for one of the
|
||||
/// directories the path prefix. (See path_resolution(7).)
|
||||
/// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
|
||||
/// it was a unicast address.
|
||||
AccessDenied,
|
||||
|
||||
/// The socket is marked nonblocking and the requested operation would block, and
|
||||
/// there is no global event loop configured.
|
||||
/// It's also possible to get this error under the following condition:
|
||||
/// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
|
||||
/// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
|
||||
/// determined that all port numbers in the ephemeral port range are currently in use. See
|
||||
/// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
|
||||
WouldBlock,
|
||||
|
||||
/// Another Fast Open is already in progress.
|
||||
FastOpenAlreadyInProgress,
|
||||
|
||||
/// Connection reset by peer.
|
||||
ConnectionResetByPeer,
|
||||
|
||||
/// The socket type requires that message be sent atomically, and the size of the message
|
||||
/// to be sent made this impossible. The message is not transmitted.
|
||||
///
|
||||
MessageTooBig,
|
||||
|
||||
/// The output queue for a network interface was full. This generally indicates that the
|
||||
/// interface has stopped sending, but may be caused by transient congestion. (Normally,
|
||||
/// this does not occur in Linux. Packets are just silently dropped when a device queue
|
||||
/// overflows.)
|
||||
/// This is also caused when there is not enough kernel memory available.
|
||||
SystemResources,
|
||||
|
||||
/// The local end has been shut down on a connection oriented socket. In this case, the
|
||||
/// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
|
||||
BrokenPipe,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Transmit a message to another socket.
|
||||
///
|
||||
/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
|
||||
/// recipient is known). The following call
|
||||
///
|
||||
/// send(sockfd, buf, len, flags);
|
||||
///
|
||||
/// is equivalent to
|
||||
///
|
||||
/// sendto(sockfd, buf, len, flags, NULL, 0);
|
||||
///
|
||||
/// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
|
||||
/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
|
||||
/// that the socket was actually connected.
|
||||
/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
|
||||
///
|
||||
/// If the message is too long to pass atomically through the underlying protocol,
|
||||
/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
|
||||
///
|
||||
/// There is no indication of failure to deliver.
|
||||
///
|
||||
/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
|
||||
/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
|
||||
/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
|
||||
/// possible to send more data.
|
||||
pub fn sendto(
|
||||
/// The file descriptor of the sending socket.
|
||||
sockfd: fd_t,
|
||||
/// Message to send.
|
||||
buf: []const u8,
|
||||
flags: u32,
|
||||
dest_addr: ?*const sockaddr,
|
||||
addrlen: socklen_t,
|
||||
) SendError!usize {
|
||||
while (true) {
|
||||
const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
|
||||
switch (errno(rc)) {
|
||||
0 => return rc,
|
||||
|
||||
EACCES => return error.AccessDenied,
|
||||
EAGAIN => if (std.event.Loop.instance) |loop| {
|
||||
loop.waitUntilFdWritable(sockfd) catch return error.WouldBlock;
|
||||
continue;
|
||||
} else {
|
||||
return error.WouldBlock;
|
||||
},
|
||||
EALREADY => return error.FastOpenAlreadyInProgress,
|
||||
EBADF => unreachable, // always a race condition
|
||||
ECONNRESET => return error.ConnectionResetByPeer,
|
||||
EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
|
||||
EFAULT => unreachable, // An invalid user space address was specified for an argument.
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable, // Invalid argument passed.
|
||||
EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
|
||||
EMSGSIZE => return error.MessageTooBig,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
|
||||
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
|
||||
EPIPE => return error.BrokenPipe,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transmit a message to another socket.
|
||||
///
|
||||
/// The `send` call may be used only when the socket is in a connected state (so that the intended
|
||||
/// recipient is known). The only difference between `send` and `write` is the presence of
|
||||
/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
|
||||
/// call
|
||||
///
|
||||
/// send(sockfd, buf, len, flags);
|
||||
///
|
||||
/// is equivalent to
|
||||
///
|
||||
/// sendto(sockfd, buf, len, flags, NULL, 0);
|
||||
///
|
||||
/// There is no indication of failure to deliver.
|
||||
///
|
||||
/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
|
||||
/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
|
||||
/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
|
||||
/// possible to send more data.
|
||||
pub fn send(
|
||||
/// The file descriptor of the sending socket.
|
||||
sockfd: fd_t,
|
||||
buf: []const u8,
|
||||
flags: u32,
|
||||
) SendError!usize {
|
||||
return sendto(sockfd, buf, flags, null, 0);
|
||||
}
|
||||
|
||||
pub const PollError = error{
|
||||
/// The kernel had no space to allocate file descriptor tables.
|
||||
SystemResources,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
|
||||
while (true) {
|
||||
const rc = system.poll(fds.ptr, fds.len, timeout);
|
||||
switch (errno(rc)) {
|
||||
0 => return rc,
|
||||
EFAULT => unreachable,
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable,
|
||||
ENOMEM => return error.SystemResources,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const RecvFromError = error{
|
||||
/// The socket is marked nonblocking and the requested operation would block, and
|
||||
/// there is no global event loop configured.
|
||||
WouldBlock,
|
||||
|
||||
/// A remote host refused to allow the network connection, typically because it is not
|
||||
/// running the requested service.
|
||||
ConnectionRefused,
|
||||
|
||||
/// Could not allocate kernel memory.
|
||||
SystemResources,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn recvfrom(
|
||||
sockfd: fd_t,
|
||||
buf: []u8,
|
||||
flags: u32,
|
||||
src_addr: ?*sockaddr,
|
||||
addrlen: ?*socklen_t,
|
||||
) RecvFromError!usize {
|
||||
while (true) {
|
||||
const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
|
||||
switch (errno(rc)) {
|
||||
0 => return rc,
|
||||
EBADF => unreachable, // always a race condition
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable,
|
||||
ENOTCONN => unreachable,
|
||||
ENOTSOCK => unreachable,
|
||||
EINTR => continue,
|
||||
EAGAIN => if (std.event.Loop.instance) |loop| {
|
||||
loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
|
||||
continue;
|
||||
} else {
|
||||
return error.WouldBlock;
|
||||
},
|
||||
ENOMEM => return error.SystemResources,
|
||||
ECONNREFUSED => return error.ConnectionRefused,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1431,3 +1431,23 @@ pub const IPPROTO_UDPLITE = 136;
|
||||
pub const IPPROTO_MPLS = 137;
|
||||
pub const IPPROTO_RAW = 255;
|
||||
pub const IPPROTO_MAX = 256;
|
||||
|
||||
pub const RR_A = 1;
|
||||
pub const RR_CNAME = 5;
|
||||
pub const RR_AAAA = 28;
|
||||
|
||||
pub const nfds_t = usize;
|
||||
pub const pollfd = extern struct {
|
||||
fd: fd_t,
|
||||
events: i16,
|
||||
revents: i16,
|
||||
};
|
||||
|
||||
pub const POLLIN = 0x001;
|
||||
pub const POLLPRI = 0x002;
|
||||
pub const POLLOUT = 0x004;
|
||||
pub const POLLERR = 0x008;
|
||||
pub const POLLHUP = 0x010;
|
||||
pub const POLLNVAL = 0x020;
|
||||
pub const POLLRDNORM = 0x040;
|
||||
pub const POLLRDBAND = 0x080;
|
||||
|
||||
@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize {
|
||||
return syscall2(SYS_munmap, @ptrToInt(address), length);
|
||||
}
|
||||
|
||||
pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize {
|
||||
if (@hasDecl(@This(), "SYS_poll")) {
|
||||
return syscall3(SYS_poll, @ptrToInt(fds), n, @bitCast(u32, timeout));
|
||||
} else {
|
||||
return syscall6(
|
||||
SYS_ppoll,
|
||||
@ptrToInt(fds),
|
||||
n,
|
||||
@ptrToInt(if (timeout >= 0)
|
||||
×pec{
|
||||
.tv_sec = timeout / 1000,
|
||||
.tv_nsec = (timeout % 1000) * 1000000,
|
||||
}
|
||||
else
|
||||
null),
|
||||
0,
|
||||
0,
|
||||
NSIG / 8,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
|
||||
return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user