From 4b80e376e331802925db3307d63dbf4f26c01a2b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 26 Oct 2019 15:05:42 -0400 Subject: [PATCH] std.net.getAddressList --- lib/std/c.zig | 21 ++++ lib/std/net.zig | 204 ++++++++++++++++++++++++++++++-- lib/std/os/bits/linux.zig | 84 ++++++++++++- src-self-hosted/translate_c.zig | 22 ++-- 4 files changed, 308 insertions(+), 23 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index f591481d04..1e8839bb6b 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -148,3 +148,24 @@ pub extern "c" fn kevent( nevents: c_int, timeout: ?*const timespec, ) c_int; + +pub extern "c" fn getaddrinfo( + noalias node: [*]const u8, + noalias service: [*]const u8, + noalias hints: *const addrinfo, + noalias res: **addrinfo, +) c_int; + +pub extern "c" fn freeaddrinfo(res: *addrinfo) void; + +pub extern "c" fn getnameinfo( + noalias addr: *const sockaddr, + addrlen: socklen_t, + noalias host: [*]u8, + hostlen: socklen_t, + noalias serv: [*]u8, + servlen: socklen_t, + flags: u32, +) c_int; + +pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8; diff --git a/lib/std/net.zig b/lib/std/net.zig index be9d18056c..06ecd4d285 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -11,10 +11,12 @@ pub const TmpWinAddr = struct { }; pub const OsAddress = switch (builtin.os) { - builtin.Os.windows => TmpWinAddr, + .windows => TmpWinAddr, else => os.sockaddr, }; +/// This data structure is a "view". The underlying data might have references +/// to owned memory which must live longer than this struct. pub const Address = struct { os_addr: OsAddress, @@ -31,7 +33,7 @@ pub const Address = struct { }; } - pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { + pub fn initIp6(ip6: Ip6Addr, _port: u16) Address { return Address{ .os_addr = os.sockaddr{ .in6 = os.sockaddr_in6{ @@ -53,18 +55,89 @@ pub const Address = struct { return Address{ .os_addr = addr }; } - pub fn format(self: *const Address, out_stream: var) !void { + pub fn format( + self: Address, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, + ) !void { switch (self.os_addr.in.family) { os.AF_INET => { const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); - const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); - try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); + const bytes = @ptrCast(*const [4]u8, &self.os_addr.in.addr); + try std.fmt.format( + context, + Errors, + output, + "{}.{}.{}.{}:{}", + bytes[0], + bytes[1], + bytes[2], + bytes[3], + native_endian_port, + ); }, os.AF_INET6 => { + const ZeroRun = struct { + index: usize, + count: usize, + }; const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); - try out_stream.print("[TODO render ip6 address]:{}", native_endian_port); + const big_endian_parts = &self.os_addr.in6.addr; + const native_endian_parts = switch (builtin.endian) { + .Big => big_endian_parts, + .Little => blk: { + var buf: [8]u16 = undefined; + for (big_endian_parts) |part, i| { + buf[i] = mem.bigToNative(u16, part); + } + break :blk buf; + }, + }; + + var longest_zero_run: ?ZeroRun = null; + var this_zero_run: ?ZeroRun = null; + for (native_endian_parts) |part, i| { + if (part == 0) { + if (this_zero_run) |*zr| { + zr.count += 1; + } else { + this_zero_run = ZeroRun{ + .index = i, + .count = 1, + }; + } + } else if (this_zero_run) |zr| { + if (longest_zero_run) |lzr| { + if (zr.count > lzr.count and zr.count > 1) { + longest_zero_run = zr; + } + } else { + longest_zero_run = zr; + } + } + } + try output(context, "["); + var i: usize = 0; + while (i < native_endian_parts.len) { + if (i != 0) try output(context, ":"); + + if (longest_zero_run) |lzr| { + if (lzr.index == i) { + i += lzr.count; + continue; + } + } + + const part = native_endian_parts[i]; + try std.fmt.format(context, Errors, output, "{x}", part); + i += 1; + } + try std.fmt.format(context, Errors, output, "]:{}", native_endian_port); }, - else => try out_stream.write("(unrecognized address family)"), + else => return output(context, "(unrecognized address family)"), } } }; @@ -111,13 +184,13 @@ pub fn parseIp4(buf: []const u8) !u32 { pub const Ip6Addr = struct { scope_id: u32, - addr: [16]u8, + addr: [8]u16, }; pub fn parseIp6(buf: []const u8) !Ip6Addr { var result: Ip6Addr = undefined; result.scope_id = 0; - const ip_slice = result.addr[0..]; + const ip_slice = @sliceToBytes(result.addr[0..]); var x: u16 = 0; var saw_any_digits = false; @@ -210,10 +283,11 @@ fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { } test "std.net.parseIp6" { - const addr = try parseIp6("FF01:0:0:0:0:0:0:FB"); - assert(addr.addr[0] == 0xff); - assert(addr.addr[1] == 0x01); - assert(addr.addr[2] == 0x00); + const ip6 = try parseIp6("FF01:0:0:0:0:0:0:FB"); + const addr = Address.initIp6(ip6, 80); + var buf: [100]u8 = undefined; + const printed = try std.fmt.bufPrint(&buf, "{}", addr); + std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed)); } pub fn connectUnixSocket(path: []const u8) !std.fs.File { @@ -245,3 +319,107 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { return std.fs.File.openHandle(sockfd); } + +pub const AddressList = struct { + arena: std.heap.ArenaAllocator, + addrs: []Address, + canon_names: []?[]u8, + + fn deinit(self: *AddressList) void { + // Here we copy the arena allocator into stack memory, because + // otherwise it would destroy itself while it was still working. + var arena = self.arena; + arena.deinit(); + // self is destroyed + } +}; + +/// Call `AddressList.deinit` on the result. +pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { + if (builtin.link_libc) { + const hints = os.addrinfo{ + .flags = os.AI_NUMERICSERV, + .family = os.AF_UNSPEC, + .socktype = os.SOCK_STREAM, + .protocol = os.IPPROTO_TCP, + .canonname = null, + .addr = null, + .addrlen = 0, + .next = null, + }; + const result = blk: { + var arena = std.heap.ArenaAllocator.init(allocator); + errdefer arena.deinit(); + + const result = try arena.allocator.create(AddressList); + result.* = AddressList{ + .arena = arena, + .addrs = undefined, + .canon_names = undefined, + }; + break :blk result; + }; + const arena = &result.arena.allocator; + errdefer result.arena.deinit(); + + const name_c = try std.cstr.addNullByte(allocator, name); + defer allocator.free(name_c); + + const port_c = try std.fmt.allocPrint(allocator, "{}\x00", port); + defer allocator.free(port_c); + + var res: *os.addrinfo = undefined; + switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { + 0 => {}, + os.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses, + os.EAI_AGAIN => return error.TemporaryNameServerFailure, + os.EAI_BADFLAGS => unreachable, // Invalid hints + os.EAI_FAIL => return error.NameServerFailure, + os.EAI_FAMILY => return error.AddressFamilyNotSupported, + os.EAI_MEMORY => return error.OutOfMemory, + os.EAI_NODATA => return error.HostLacksNetworkAddresses, + // The node or service is not known; or both node and service are NULL; or AI_NUMERICSERV + // was specified in hints.ai_flags and service was not a numeric port-number string. + os.EAI_NONAME => unreachable, // Invalid hints + os.EAI_SERVICE => return error.ServiceUnavailable, + os.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints + os.EAI_SYSTEM => switch (os.errno(-1)) { + else => |e| return os.unexpectedErrno(e), + }, + else => unreachable, + } + defer os.system.freeaddrinfo(res); + + const addr_count = blk: { + var count: usize = 0; + var it: ?*os.addrinfo = res; + while (it) |info| : (it = info.next) { + if (info.addr != null) { + count += 1; + } + } + break :blk count; + }; + result.addrs = try arena.alloc(Address, addr_count); + result.canon_names = try arena.alloc(?[]u8, addr_count); + + var it: ?*os.addrinfo = res; + var i: usize = 0; + while (it) |info| : (it = info.next) { + const addr = info.addr orelse continue; + result.addrs[i] = std.net.Address.initPosix(addr.*); + result.canon_names[i] = null; + + if (info.canonname) |n| { + const name_len = mem.len(u8, n); + const new_slice = try arena.alloc(u8, name_len + 1); + @memcpy(new_slice.ptr, n, name_len + 1); + result.canon_names[i] = new_slice[0..name_len]; + } + i += 1; + } + + return result; + } + @compileError("TODO implement std.net.getAddresses for this OS"); +} diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index d2d521a005..234971a79e 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -864,7 +864,7 @@ pub const sockaddr_in6 = extern struct { family: sa_family_t, port: in_port_t, flowinfo: u32, - addr: [16]u8, + addr: [8]u16, scope_id: u32, }; @@ -1384,3 +1384,85 @@ pub const Statx = extern struct { __pad2: [14]u64, }; + +pub const addrinfo = extern struct { + flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + addr: ?*sockaddr, + canonname: ?[*]u8, + next: ?*addrinfo, +}; + +pub const AI_PASSIVE = 0x01; +pub const AI_CANONNAME = 0x02; +pub const AI_NUMERICHOST = 0x04; +pub const AI_V4MAPPED = 0x08; +pub const AI_ALL = 0x10; +pub const AI_ADDRCONFIG = 0x20; +pub const AI_NUMERICSERV = 0x400; + +pub const NI_NUMERICHOST = 0x01; +pub const NI_NUMERICSERV = 0x02; +pub const NI_NOFQDN = 0x04; +pub const NI_NAMEREQD = 0x08; +pub const NI_DGRAM = 0x10; +pub const NI_NUMERICSCOPE = 0x100; + +pub const EAI_BADFLAGS = -1; +pub const EAI_NONAME = -2; +pub const EAI_AGAIN = -3; +pub const EAI_FAIL = -4; +pub const EAI_FAMILY = -6; +pub const EAI_SOCKTYPE = -7; +pub const EAI_SERVICE = -8; +pub const EAI_MEMORY = -10; +pub const EAI_SYSTEM = -11; +pub const EAI_OVERFLOW = -12; + +pub const EAI_NODATA = -5; +pub const EAI_ADDRFAMILY = -9; +pub const EAI_INPROGRESS = -100; +pub const EAI_CANCELED = -101; +pub const EAI_NOTCANCELED = -102; +pub const EAI_ALLDONE = -103; +pub const EAI_INTR = -104; +pub const EAI_IDN_ENCODE = -105; + +pub const IPPORT_RESERVED = 1024; + +pub const IPPROTO_IP = 0; +pub const IPPROTO_HOPOPTS = 0; +pub const IPPROTO_ICMP = 1; +pub const IPPROTO_IGMP = 2; +pub const IPPROTO_IPIP = 4; +pub const IPPROTO_TCP = 6; +pub const IPPROTO_EGP = 8; +pub const IPPROTO_PUP = 12; +pub const IPPROTO_UDP = 17; +pub const IPPROTO_IDP = 22; +pub const IPPROTO_TP = 29; +pub const IPPROTO_DCCP = 33; +pub const IPPROTO_IPV6 = 41; +pub const IPPROTO_ROUTING = 43; +pub const IPPROTO_FRAGMENT = 44; +pub const IPPROTO_RSVP = 46; +pub const IPPROTO_GRE = 47; +pub const IPPROTO_ESP = 50; +pub const IPPROTO_AH = 51; +pub const IPPROTO_ICMPV6 = 58; +pub const IPPROTO_NONE = 59; +pub const IPPROTO_DSTOPTS = 60; +pub const IPPROTO_MTP = 92; +pub const IPPROTO_BEETPH = 94; +pub const IPPROTO_ENCAP = 98; +pub const IPPROTO_PIM = 103; +pub const IPPROTO_COMP = 108; +pub const IPPROTO_SCTP = 132; +pub const IPPROTO_MH = 135; +pub const IPPROTO_UDPLITE = 136; +pub const IPPROTO_MPLS = 137; +pub const IPPROTO_RAW = 255; +pub const IPPROTO_MAX = 256; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index a5c4959d08..548add2624 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -151,18 +151,22 @@ pub fn translate( }; defer ZigClangASTUnit_delete(ast_unit); - var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); - errdefer tree_arena.deinit(); + const tree = blk: { + var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); + errdefer tree_arena.deinit(); - const tree = try tree_arena.allocator.create(ast.Tree); - tree.* = ast.Tree{ - .source = undefined, // need to use Buffer.toOwnedSlice later - .root_node = undefined, - .arena_allocator = tree_arena, - .tokens = undefined, // can't reference the allocator yet - .errors = undefined, // can't reference the allocator yet + const tree = try tree_arena.allocator.create(ast.Tree); + tree.* = ast.Tree{ + .source = undefined, // need to use Buffer.toOwnedSlice later + .root_node = undefined, + .arena_allocator = tree_arena, + .tokens = undefined, // can't reference the allocator yet + .errors = undefined, // can't reference the allocator yet + }; + break :blk tree; }; const arena = &tree.arena_allocator.allocator; // now we can reference the allocator + errdefer arena.deinit(); tree.tokens = ast.Tree.TokenList.init(arena); tree.errors = ast.Tree.ErrorList.init(arena);