| ... | ... | @@ -11,10 +11,12 @@ pub const TmpWinAddr = struct { |
| 11 | 11 | }; |
| 12 | 12 | |
| 13 | 13 | pub const OsAddress = switch (builtin.os) { |
| 14 | | builtin.Os.windows => TmpWinAddr, |
| 14 | .windows => TmpWinAddr, |
| 15 | 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 | 20 | pub const Address = struct { |
| 19 | 21 | os_addr: OsAddress, |
| 20 | 22 | |
| ... | ... | @@ -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 | 37 | return Address{ |
| 36 | 38 | .os_addr = os.sockaddr{ |
| 37 | 39 | .in6 = os.sockaddr_in6{ |
| ... | ... | @@ -53,18 +55,89 @@ pub const Address = struct { |
| 53 | 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 | 66 | switch (self.os_addr.in.family) { |
| 58 | 67 | os.AF_INET => { |
| 59 | 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]); |
| 61 | | try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); |
| 69 | const bytes = @ptrCast(*const [4]u8, &self.os_addr.in.addr); |
| 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 | 82 | os.AF_INET6 => { |
| 83 | const ZeroRun = struct { |
| 84 | index: usize, |
| 85 | count: usize, |
| 86 | }; |
| 64 | 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 | 184 | |
| 112 | 185 | pub const Ip6Addr = struct { |
| 113 | 186 | scope_id: u32, |
| 114 | | addr: [16]u8, |
| 187 | addr: [8]u16, |
| 115 | 188 | }; |
| 116 | 189 | |
| 117 | 190 | pub fn parseIp6(buf: []const u8) !Ip6Addr { |
| 118 | 191 | var result: Ip6Addr = undefined; |
| 119 | 192 | result.scope_id = 0; |
| 120 | | const ip_slice = result.addr[0..]; |
| 193 | const ip_slice = @sliceToBytes(result.addr[0..]); |
| 121 | 194 | |
| 122 | 195 | var x: u16 = 0; |
| 123 | 196 | var saw_any_digits = false; |
| ... | ... | @@ -210,10 +283,11 @@ fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { |
| 210 | 283 | } |
| 211 | 284 | |
| 212 | 285 | test "std.net.parseIp6" { |
| 213 | | const addr = try parseIp6("FF01:0:0:0:0:0:0:FB"); |
| 214 | | assert(addr.addr[0] == 0xff); |
| 215 | | assert(addr.addr[1] == 0x01); |
| 216 | | assert(addr.addr[2] == 0x00); |
| 286 | const ip6 = try parseIp6("FF01:0:0:0:0:0:0:FB"); |
| 287 | const addr = Address.initIp6(ip6, 80); |
| 288 | var buf: [100]u8 = undefined; |
| 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 | 293 | 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 | 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 | } |