| ... | @@ -11,10 +11,12 @@ pub const TmpWinAddr = struct { | ... | @@ -11,10 +11,12 @@ pub const TmpWinAddr = struct { |
| 11 | }; | 11 | }; |
| 12 | | 12 | |
| 13 | pub const OsAddress = switch (builtin.os) { | 13 | pub const OsAddress = switch (builtin.os) { |
| 14 | builtin.Os.windows => TmpWinAddr, | 14 | .windows => TmpWinAddr, |
| 15 | else => os.sockaddr, | 15 | else => os.sockaddr, |
| 16 | }; | 16 | }; |
| 17 | | 17 | |
| | 18 | /// This data structure is a "view". The underlying data might have references |
| | 19 | /// to owned memory which must live longer than this struct. |
| 18 | pub const Address = struct { | 20 | pub const Address = struct { |
| 19 | os_addr: OsAddress, | 21 | os_addr: OsAddress, |
| 20 | | 22 | |
| ... | @@ -31,7 +33,7 @@ pub const Address = struct { | ... | @@ -31,7 +33,7 @@ pub const Address = struct { |
| 31 | }; | 33 | }; |
| 32 | } | 34 | } |
| 33 | | 35 | |
| 34 | pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { | 36 | pub fn initIp6(ip6: Ip6Addr, _port: u16) Address { |
| 35 | return Address{ | 37 | return Address{ |
| 36 | .os_addr = os.sockaddr{ | 38 | .os_addr = os.sockaddr{ |
| 37 | .in6 = os.sockaddr_in6{ | 39 | .in6 = os.sockaddr_in6{ |
| ... | @@ -53,18 +55,89 @@ pub const Address = struct { | ... | @@ -53,18 +55,89 @@ pub const Address = struct { |
| 53 | return Address{ .os_addr = addr }; | 55 | return Address{ .os_addr = addr }; |
| 54 | } | 56 | } |
| 55 | | 57 | |
| 56 | pub fn format(self: *const Address, out_stream: var) !void { | 58 | pub fn format( |
| | 59 | self: Address, |
| | 60 | comptime fmt: []const u8, |
| | 61 | options: std.fmt.FormatOptions, |
| | 62 | context: var, |
| | 63 | comptime Errors: type, |
| | 64 | output: fn (@typeOf(context), []const u8) Errors!void, |
| | 65 | ) !void { |
| 57 | switch (self.os_addr.in.family) { | 66 | switch (self.os_addr.in.family) { |
| 58 | os.AF_INET => { | 67 | os.AF_INET => { |
| 59 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); | 68 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); |
| 60 | const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); | 69 | const bytes = @ptrCast(*const [4]u8, &self.os_addr.in.addr); |
| 61 | try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); | 70 | try std.fmt.format( |
| | 71 | context, |
| | 72 | Errors, |
| | 73 | output, |
| | 74 | "{}.{}.{}.{}:{}", |
| | 75 | bytes[0], |
| | 76 | bytes[1], |
| | 77 | bytes[2], |
| | 78 | bytes[3], |
| | 79 | native_endian_port, |
| | 80 | ); |
| 62 | }, | 81 | }, |
| 63 | os.AF_INET6 => { | 82 | os.AF_INET6 => { |
| | 83 | const ZeroRun = struct { |
| | 84 | index: usize, |
| | 85 | count: usize, |
| | 86 | }; |
| 64 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); | 87 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); |
| 65 | try out_stream.print("[TODO render ip6 address]:{}", native_endian_port); | 88 | const big_endian_parts = &self.os_addr.in6.addr; |
| | 89 | const native_endian_parts = switch (builtin.endian) { |
| | 90 | .Big => big_endian_parts, |
| | 91 | .Little => blk: { |
| | 92 | var buf: [8]u16 = undefined; |
| | 93 | for (big_endian_parts) |part, i| { |
| | 94 | buf[i] = mem.bigToNative(u16, part); |
| | 95 | } |
| | 96 | break :blk buf; |
| | 97 | }, |
| | 98 | }; |
| | 99 | |
| | 100 | var longest_zero_run: ?ZeroRun = null; |
| | 101 | var this_zero_run: ?ZeroRun = null; |
| | 102 | for (native_endian_parts) |part, i| { |
| | 103 | if (part == 0) { |
| | 104 | if (this_zero_run) |*zr| { |
| | 105 | zr.count += 1; |
| | 106 | } else { |
| | 107 | this_zero_run = ZeroRun{ |
| | 108 | .index = i, |
| | 109 | .count = 1, |
| | 110 | }; |
| | 111 | } |
| | 112 | } else if (this_zero_run) |zr| { |
| | 113 | if (longest_zero_run) |lzr| { |
| | 114 | if (zr.count > lzr.count and zr.count > 1) { |
| | 115 | longest_zero_run = zr; |
| | 116 | } |
| | 117 | } else { |
| | 118 | longest_zero_run = zr; |
| | 119 | } |
| | 120 | } |
| | 121 | } |
| | 122 | try output(context, "["); |
| | 123 | var i: usize = 0; |
| | 124 | while (i < native_endian_parts.len) { |
| | 125 | if (i != 0) try output(context, ":"); |
| | 126 | |
| | 127 | if (longest_zero_run) |lzr| { |
| | 128 | if (lzr.index == i) { |
| | 129 | i += lzr.count; |
| | 130 | continue; |
| | 131 | } |
| | 132 | } |
| | 133 | |
| | 134 | const part = native_endian_parts[i]; |
| | 135 | try std.fmt.format(context, Errors, output, "{x}", part); |
| | 136 | i += 1; |
| | 137 | } |
| | 138 | try std.fmt.format(context, Errors, output, "]:{}", native_endian_port); |
| 66 | }, | 139 | }, |
| 67 | else => try out_stream.write("(unrecognized address family)"), | 140 | else => return output(context, "(unrecognized address family)"), |
| 68 | } | 141 | } |
| 69 | } | 142 | } |
| 70 | }; | 143 | }; |
| ... | @@ -111,13 +184,13 @@ pub fn parseIp4(buf: []const u8) !u32 { | ... | @@ -111,13 +184,13 @@ pub fn parseIp4(buf: []const u8) !u32 { |
| 111 | | 184 | |
| 112 | pub const Ip6Addr = struct { | 185 | pub const Ip6Addr = struct { |
| 113 | scope_id: u32, | 186 | scope_id: u32, |
| 114 | addr: [16]u8, | 187 | addr: [8]u16, |
| 115 | }; | 188 | }; |
| 116 | | 189 | |
| 117 | pub fn parseIp6(buf: []const u8) !Ip6Addr { | 190 | pub fn parseIp6(buf: []const u8) !Ip6Addr { |
| 118 | var result: Ip6Addr = undefined; | 191 | var result: Ip6Addr = undefined; |
| 119 | result.scope_id = 0; | 192 | result.scope_id = 0; |
| 120 | const ip_slice = result.addr[0..]; | 193 | const ip_slice = @sliceToBytes(result.addr[0..]); |
| 121 | | 194 | |
| 122 | var x: u16 = 0; | 195 | var x: u16 = 0; |
| 123 | var saw_any_digits = false; | 196 | var saw_any_digits = false; |
| ... | @@ -210,10 +283,11 @@ fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { | ... | @@ -210,10 +283,11 @@ fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { |
| 210 | } | 283 | } |
| 211 | | 284 | |
| 212 | test "std.net.parseIp6" { | 285 | test "std.net.parseIp6" { |
| 213 | const addr = try parseIp6("FF01:0:0:0:0:0:0:FB"); | 286 | const ip6 = try parseIp6("FF01:0:0:0:0:0:0:FB"); |
| 214 | assert(addr.addr[0] == 0xff); | 287 | const addr = Address.initIp6(ip6, 80); |
| 215 | assert(addr.addr[1] == 0x01); | 288 | var buf: [100]u8 = undefined; |
| 216 | assert(addr.addr[2] == 0x00); | 289 | const printed = try std.fmt.bufPrint(&buf, "{}", addr); |
| | 290 | std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed)); |
| 217 | } | 291 | } |
| 218 | | 292 | |
| 219 | pub fn connectUnixSocket(path: []const u8) !std.fs.File { | 293 | pub fn connectUnixSocket(path: []const u8) !std.fs.File { |
| ... | @@ -245,3 +319,107 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { | ... | @@ -245,3 +319,107 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { |
| 245 | | 319 | |
| 246 | return std.fs.File.openHandle(sockfd); | 320 | return std.fs.File.openHandle(sockfd); |
| 247 | } | 321 | } |
| | 322 | |
| | 323 | pub const AddressList = struct { |
| | 324 | arena: std.heap.ArenaAllocator, |
| | 325 | addrs: []Address, |
| | 326 | canon_names: []?[]u8, |
| | 327 | |
| | 328 | fn deinit(self: *AddressList) void { |
| | 329 | // Here we copy the arena allocator into stack memory, because |
| | 330 | // otherwise it would destroy itself while it was still working. |
| | 331 | var arena = self.arena; |
| | 332 | arena.deinit(); |
| | 333 | // self is destroyed |
| | 334 | } |
| | 335 | }; |
| | 336 | |
| | 337 | /// Call `AddressList.deinit` on the result. |
| | 338 | pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { |
| | 339 | if (builtin.link_libc) { |
| | 340 | const hints = os.addrinfo{ |
| | 341 | .flags = os.AI_NUMERICSERV, |
| | 342 | .family = os.AF_UNSPEC, |
| | 343 | .socktype = os.SOCK_STREAM, |
| | 344 | .protocol = os.IPPROTO_TCP, |
| | 345 | .canonname = null, |
| | 346 | .addr = null, |
| | 347 | .addrlen = 0, |
| | 348 | .next = null, |
| | 349 | }; |
| | 350 | const result = blk: { |
| | 351 | var arena = std.heap.ArenaAllocator.init(allocator); |
| | 352 | errdefer arena.deinit(); |
| | 353 | |
| | 354 | const result = try arena.allocator.create(AddressList); |
| | 355 | result.* = AddressList{ |
| | 356 | .arena = arena, |
| | 357 | .addrs = undefined, |
| | 358 | .canon_names = undefined, |
| | 359 | }; |
| | 360 | break :blk result; |
| | 361 | }; |
| | 362 | const arena = &result.arena.allocator; |
| | 363 | errdefer result.arena.deinit(); |
| | 364 | |
| | 365 | const name_c = try std.cstr.addNullByte(allocator, name); |
| | 366 | defer allocator.free(name_c); |
| | 367 | |
| | 368 | const port_c = try std.fmt.allocPrint(allocator, "{}\x00", port); |
| | 369 | defer allocator.free(port_c); |
| | 370 | |
| | 371 | var res: *os.addrinfo = undefined; |
| | 372 | switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { |
| | 373 | 0 => {}, |
| | 374 | os.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses, |
| | 375 | os.EAI_AGAIN => return error.TemporaryNameServerFailure, |
| | 376 | os.EAI_BADFLAGS => unreachable, // Invalid hints |
| | 377 | os.EAI_FAIL => return error.NameServerFailure, |
| | 378 | os.EAI_FAMILY => return error.AddressFamilyNotSupported, |
| | 379 | os.EAI_MEMORY => return error.OutOfMemory, |
| | 380 | os.EAI_NODATA => return error.HostLacksNetworkAddresses, |
| | 381 | // The node or service is not known; or both node and service are NULL; or AI_NUMERICSERV |
| | 382 | // was specified in hints.ai_flags and service was not a numeric port-number string. |
| | 383 | os.EAI_NONAME => unreachable, // Invalid hints |
| | 384 | os.EAI_SERVICE => return error.ServiceUnavailable, |
| | 385 | os.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints |
| | 386 | os.EAI_SYSTEM => switch (os.errno(-1)) { |
| | 387 | else => |e| return os.unexpectedErrno(e), |
| | 388 | }, |
| | 389 | else => unreachable, |
| | 390 | } |
| | 391 | defer os.system.freeaddrinfo(res); |
| | 392 | |
| | 393 | const addr_count = blk: { |
| | 394 | var count: usize = 0; |
| | 395 | var it: ?*os.addrinfo = res; |
| | 396 | while (it) |info| : (it = info.next) { |
| | 397 | if (info.addr != null) { |
| | 398 | count += 1; |
| | 399 | } |
| | 400 | } |
| | 401 | break :blk count; |
| | 402 | }; |
| | 403 | result.addrs = try arena.alloc(Address, addr_count); |
| | 404 | result.canon_names = try arena.alloc(?[]u8, addr_count); |
| | 405 | |
| | 406 | var it: ?*os.addrinfo = res; |
| | 407 | var i: usize = 0; |
| | 408 | while (it) |info| : (it = info.next) { |
| | 409 | const addr = info.addr orelse continue; |
| | 410 | result.addrs[i] = std.net.Address.initPosix(addr.*); |
| | 411 | result.canon_names[i] = null; |
| | 412 | |
| | 413 | if (info.canonname) |n| { |
| | 414 | const name_len = mem.len(u8, n); |
| | 415 | const new_slice = try arena.alloc(u8, name_len + 1); |
| | 416 | @memcpy(new_slice.ptr, n, name_len + 1); |
| | 417 | result.canon_names[i] = new_slice[0..name_len]; |
| | 418 | } |
| | 419 | i += 1; |
| | 420 | } |
| | 421 | |
| | 422 | return result; |
| | 423 | } |
| | 424 | @compileError("TODO implement std.net.getAddresses for this OS"); |
| | 425 | } |