| ... | @@ -85,9 +85,9 @@ pub const Address = struct { | ... | @@ -85,9 +85,9 @@ pub const Address = struct { |
| 85 | count: usize, | 85 | count: usize, |
| 86 | }; | 86 | }; |
| 87 | 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); |
| 88 | const big_endian_parts = &self.os_addr.in6.addr; | 88 | const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.os_addr.in6.addr); |
| 89 | const native_endian_parts = switch (builtin.endian) { | 89 | const native_endian_parts = switch (builtin.endian) { |
| 90 | .Big => big_endian_parts, | 90 | .Big => big_endian_parts.*, |
| 91 | .Little => blk: { | 91 | .Little => blk: { |
| 92 | var buf: [8]u16 = undefined; | 92 | var buf: [8]u16 = undefined; |
| 93 | for (big_endian_parts) |part, i| { | 93 | for (big_endian_parts) |part, i| { |
| ... | @@ -163,13 +163,8 @@ pub fn parseIp4(buf: []const u8) !u32 { | ... | @@ -163,13 +163,8 @@ pub fn parseIp4(buf: []const u8) !u32 { |
| 163 | saw_any_digits = false; | 163 | saw_any_digits = false; |
| 164 | } else if (c >= '0' and c <= '9') { | 164 | } else if (c >= '0' and c <= '9') { |
| 165 | saw_any_digits = true; | 165 | saw_any_digits = true; |
| 166 | const digit = c - '0'; | 166 | x = try std.math.mul(u8, x, 10); |
| 167 | if (@mulWithOverflow(u8, x, 10, &x)) { | 167 | x = try std.math.add(u8, x, c - '0'); |
| 168 | return error.Overflow; | | |
| 169 | } | | |
| 170 | if (@addWithOverflow(u8, x, digit, &x)) { | | |
| 171 | return error.Overflow; | | |
| 172 | } | | |
| 173 | } else { | 168 | } else { |
| 174 | return error.InvalidCharacter; | 169 | return error.InvalidCharacter; |
| 175 | } | 170 | } |
| ... | @@ -184,13 +179,13 @@ pub fn parseIp4(buf: []const u8) !u32 { | ... | @@ -184,13 +179,13 @@ pub fn parseIp4(buf: []const u8) !u32 { |
| 184 | | 179 | |
| 185 | pub const Ip6Addr = struct { | 180 | pub const Ip6Addr = struct { |
| 186 | scope_id: u32, | 181 | scope_id: u32, |
| 187 | addr: [8]u16, | 182 | addr: [16]u8, |
| 188 | }; | 183 | }; |
| 189 | | 184 | |
| 190 | pub fn parseIp6(buf: []const u8) !Ip6Addr { | 185 | pub fn parseIp6(buf: []const u8) !Ip6Addr { |
| 191 | var result: Ip6Addr = undefined; | 186 | var result: Ip6Addr = undefined; |
| 192 | result.scope_id = 0; | 187 | result.scope_id = 0; |
| 193 | const ip_slice = @sliceToBytes(result.addr[0..]); | 188 | const ip_slice = result.addr[0..]; |
| 194 | | 189 | |
| 195 | var x: u16 = 0; | 190 | var x: u16 = 0; |
| 196 | var saw_any_digits = false; | 191 | var saw_any_digits = false; |
| ... | @@ -323,7 +318,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { | ... | @@ -323,7 +318,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { |
| 323 | pub const AddressList = struct { | 318 | pub const AddressList = struct { |
| 324 | arena: std.heap.ArenaAllocator, | 319 | arena: std.heap.ArenaAllocator, |
| 325 | addrs: []Address, | 320 | addrs: []Address, |
| 326 | canon_names: []?[]u8, | 321 | canon_name: ?[]u8, |
| 327 | | 322 | |
| 328 | fn deinit(self: *AddressList) void { | 323 | fn deinit(self: *AddressList) void { |
| 329 | // Here we copy the arena allocator into stack memory, because | 324 | // Here we copy the arena allocator into stack memory, because |
| ... | @@ -336,7 +331,28 @@ pub const AddressList = struct { | ... | @@ -336,7 +331,28 @@ pub const AddressList = struct { |
| 336 | | 331 | |
| 337 | /// Call `AddressList.deinit` on the result. | 332 | /// Call `AddressList.deinit` on the result. |
| 338 | pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { | 333 | pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { |
| | 334 | const result = blk: { |
| | 335 | var arena = std.heap.ArenaAllocator.init(allocator); |
| | 336 | errdefer arena.deinit(); |
| | 337 | |
| | 338 | const result = try arena.allocator.create(AddressList); |
| | 339 | result.* = AddressList{ |
| | 340 | .arena = arena, |
| | 341 | .addrs = undefined, |
| | 342 | .canon_name = null, |
| | 343 | }; |
| | 344 | break :blk result; |
| | 345 | }; |
| | 346 | const arena = &result.arena.allocator; |
| | 347 | errdefer result.arena.deinit(); |
| | 348 | |
| 339 | if (builtin.link_libc) { | 349 | if (builtin.link_libc) { |
| | 350 | const name_c = try std.cstr.addNullByte(allocator, name); |
| | 351 | defer allocator.free(name_c); |
| | 352 | |
| | 353 | const port_c = try std.fmt.allocPrint(allocator, "{}\x00", port); |
| | 354 | defer allocator.free(port_c); |
| | 355 | |
| 340 | const hints = os.addrinfo{ | 356 | const hints = os.addrinfo{ |
| 341 | .flags = os.AI_NUMERICSERV, | 357 | .flags = os.AI_NUMERICSERV, |
| 342 | .family = os.AF_UNSPEC, | 358 | .family = os.AF_UNSPEC, |
| ... | @@ -347,27 +363,6 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* | ... | @@ -347,27 +363,6 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 347 | .addrlen = 0, | 363 | .addrlen = 0, |
| 348 | .next = null, | 364 | .next = null, |
| 349 | }; | 365 | }; |
| 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; | 366 | var res: *os.addrinfo = undefined; |
| 372 | switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { | 367 | switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { |
| 373 | 0 => {}, | 368 | 0 => {}, |
| ... | @@ -378,9 +373,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* | ... | @@ -378,9 +373,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 378 | os.EAI_FAMILY => return error.AddressFamilyNotSupported, | 373 | os.EAI_FAMILY => return error.AddressFamilyNotSupported, |
| 379 | os.EAI_MEMORY => return error.OutOfMemory, | 374 | os.EAI_MEMORY => return error.OutOfMemory, |
| 380 | os.EAI_NODATA => return error.HostLacksNetworkAddresses, | 375 | os.EAI_NODATA => return error.HostLacksNetworkAddresses, |
| 381 | // The node or service is not known; or both node and service are NULL; or AI_NUMERICSERV | 376 | os.EAI_NONAME => return error.UnknownName, |
| 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, | 377 | os.EAI_SERVICE => return error.ServiceUnavailable, |
| 385 | os.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints | 378 | os.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints |
| 386 | os.EAI_SYSTEM => switch (os.errno(-1)) { | 379 | os.EAI_SYSTEM => switch (os.errno(-1)) { |
| ... | @@ -401,25 +394,250 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* | ... | @@ -401,25 +394,250 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 401 | break :blk count; | 394 | break :blk count; |
| 402 | }; | 395 | }; |
| 403 | result.addrs = try arena.alloc(Address, addr_count); | 396 | result.addrs = try arena.alloc(Address, addr_count); |
| 404 | result.canon_names = try arena.alloc(?[]u8, addr_count); | | |
| 405 | | 397 | |
| 406 | var it: ?*os.addrinfo = res; | 398 | var it: ?*os.addrinfo = res; |
| 407 | var i: usize = 0; | 399 | var i: usize = 0; |
| 408 | while (it) |info| : (it = info.next) { | 400 | while (it) |info| : (it = info.next) { |
| 409 | const addr = info.addr orelse continue; | 401 | const addr = info.addr orelse continue; |
| 410 | result.addrs[i] = std.net.Address.initPosix(addr.*); | 402 | result.addrs[i] = Address.initPosix(addr.*); |
| 411 | result.canon_names[i] = null; | | |
| 412 | | 403 | |
| 413 | if (info.canonname) |n| { | 404 | if (info.canonname) |n| { |
| 414 | const name_len = mem.len(u8, n); | 405 | if (result.canon_name == null) { |
| 415 | const new_slice = try arena.alloc(u8, name_len + 1); | 406 | result.canon_name = try mem.dupe(arena, u8, mem.toSliceConst(u8, n)); |
| 416 | @memcpy(new_slice.ptr, n, name_len + 1); | 407 | } |
| 417 | result.canon_names[i] = new_slice[0..name_len]; | | |
| 418 | } | 408 | } |
| 419 | i += 1; | 409 | i += 1; |
| 420 | } | 410 | } |
| 421 | | 411 | |
| 422 | return result; | 412 | return result; |
| 423 | } | 413 | } |
| 424 | @compileError("TODO implement std.net.getAddresses for this OS"); | 414 | if (builtin.os == .linux) { |
| | 415 | const flags = os.AI_NUMERICSERV; |
| | 416 | const family = os.AF_INET; //TODO os.AF_UNSPEC; |
| | 417 | // The limit of 48 results is a non-sharp bound on the number of addresses |
| | 418 | // that can fit in one 512-byte DNS packet full of v4 results and a second |
| | 419 | // packet full of v6 results. Due to headers, the actual limit is lower. |
| | 420 | var buf: [48]LookupAddr = undefined; |
| | 421 | var canon_buf: [256]u8 = undefined; |
| | 422 | var canon_len: usize = 0; |
| | 423 | const cnt = try linuxLookupName(buf[0..], &canon_buf, &canon_len, name, family, flags); |
| | 424 | |
| | 425 | result.addrs = try arena.alloc(Address, cnt); |
| | 426 | |
| | 427 | if (canon_len != 0) { |
| | 428 | result.canon_name = try mem.dupe(arena, u8, canon_buf[0..canon_len]); |
| | 429 | } |
| | 430 | |
| | 431 | var i: usize = 0; |
| | 432 | while (i < cnt) : (i += 1) { |
| | 433 | const os_addr = if (buf[i].family == os.AF_INET6) |
| | 434 | os.sockaddr{ |
| | 435 | .in6 = os.sockaddr_in6{ |
| | 436 | .family = buf[i].family, |
| | 437 | .port = mem.nativeToBig(u16, port), |
| | 438 | .flowinfo = 0, |
| | 439 | .addr = buf[i].addr, |
| | 440 | .scope_id = buf[i].scope_id, |
| | 441 | }, |
| | 442 | } |
| | 443 | else |
| | 444 | os.sockaddr{ |
| | 445 | .in = os.sockaddr_in{ |
| | 446 | .family = buf[i].family, |
| | 447 | .port = mem.nativeToBig(u16, port), |
| | 448 | .addr = @ptrCast(*align(1) u32, &buf[i].addr).*, |
| | 449 | .zero = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
| | 450 | }, |
| | 451 | }; |
| | 452 | result.addrs[i] = Address.initPosix(os_addr); |
| | 453 | } |
| | 454 | |
| | 455 | return result; |
| | 456 | } |
| | 457 | @compileError("std.net.getAddresses unimplemented for this OS"); |
| | 458 | } |
| | 459 | |
| | 460 | const LookupAddr = struct { |
| | 461 | family: os.sa_family_t, |
| | 462 | scope_id: u32 = 0, |
| | 463 | addr: [16]u8, // could be IPv4 or IPv6 |
| | 464 | sortkey: i32 = 0, |
| | 465 | }; |
| | 466 | |
| | 467 | fn linuxLookupName( |
| | 468 | buf: []LookupAddr, |
| | 469 | canon_buf: []u8, |
| | 470 | canon_len: *usize, |
| | 471 | opt_name: ?[]const u8, |
| | 472 | family: i32, |
| | 473 | flags: u32, |
| | 474 | ) !usize { |
| | 475 | var cnt: usize = 0; |
| | 476 | if (opt_name) |name| { |
| | 477 | // reject empty name and check len so it fits into temp bufs |
| | 478 | if (name.len >= 254) return error.UnknownName; |
| | 479 | mem.copy(u8, canon_buf, name); |
| | 480 | canon_len.* = name.len; |
| | 481 | |
| | 482 | cnt = (linuxLookupNameFromNumeric(buf, name, family) catch |err| switch (err) { |
| | 483 | error.ExpectedIPv6ButFoundIPv4 => unreachable, |
| | 484 | error.ExpectedIPv4ButFoundIPv6 => unreachable, |
| | 485 | }); |
| | 486 | if (cnt == 0 and (flags & os.AI_NUMERICHOST) == 0) { |
| | 487 | cnt = try linuxLookupNameFromHosts(buf, canon_buf, canon_len, name, family); |
| | 488 | } |
| | 489 | } else { |
| | 490 | canon_len.* = 0; |
| | 491 | cnt = linuxLookupNameFromNull(buf, family, flags); |
| | 492 | } |
| | 493 | if (cnt == 0) return error.UnknownName; |
| | 494 | |
| | 495 | // No further processing is needed if there are fewer than 2 |
| | 496 | // results or if there are only IPv4 results. |
| | 497 | if (cnt == 1 or family == os.AF_INET) return cnt; |
| | 498 | |
| | 499 | @panic("port the RFC 3484/6724 destination address selection from musl libc"); |
| | 500 | } |
| | 501 | |
| | 502 | fn linuxLookupNameFromNumeric(buf: []LookupAddr, name: []const u8, family: i32) !usize { |
| | 503 | if (parseIp4(name)) |ip4| { |
| | 504 | if (family == os.AF_INET6) return error.ExpectedIPv6ButFoundIPv4; |
| | 505 | // TODO [0..4] should return *[4]u8, making this pointer cast unnecessary |
| | 506 | mem.writeIntNative(u32, @ptrCast(*[4]u8, &buf[0].addr), ip4); |
| | 507 | buf[0].family = os.AF_INET; |
| | 508 | buf[0].scope_id = 0; |
| | 509 | return 1; |
| | 510 | } else |err| switch (err) { |
| | 511 | error.Overflow, |
| | 512 | error.InvalidEnd, |
| | 513 | error.InvalidCharacter, |
| | 514 | error.Incomplete, |
| | 515 | => {}, |
| | 516 | } |
| | 517 | |
| | 518 | if (parseIp6(name)) |ip6| { |
| | 519 | if (family == os.AF_INET) return error.ExpectedIPv4ButFoundIPv6; |
| | 520 | @memcpy(&buf[0].addr, &ip6.addr, 16); |
| | 521 | buf[0].family = os.AF_INET6; |
| | 522 | buf[0].scope_id = ip6.scope_id; |
| | 523 | return 1; |
| | 524 | } else |err| switch (err) { |
| | 525 | error.Overflow, |
| | 526 | error.InvalidEnd, |
| | 527 | error.InvalidCharacter, |
| | 528 | error.Incomplete, |
| | 529 | => {}, |
| | 530 | } |
| | 531 | |
| | 532 | return 0; |
| | 533 | } |
| | 534 | |
| | 535 | fn linuxLookupNameFromNull(buf: []LookupAddr, family: i32, flags: u32) usize { |
| | 536 | var cnt: usize = 0; |
| | 537 | if ((flags & os.AI_PASSIVE) != 0) { |
| | 538 | if (family != os.AF_INET6) { |
| | 539 | buf[cnt] = LookupAddr{ |
| | 540 | .family = os.AF_INET, |
| | 541 | .addr = [1]u8{0} ** 16, |
| | 542 | }; |
| | 543 | cnt += 1; |
| | 544 | } |
| | 545 | if (family != os.AF_INET) { |
| | 546 | buf[cnt] = LookupAddr{ |
| | 547 | .family = os.AF_INET6, |
| | 548 | .addr = [1]u8{0} ** 16, |
| | 549 | }; |
| | 550 | cnt += 1; |
| | 551 | } |
| | 552 | } else { |
| | 553 | if (family != os.AF_INET6) { |
| | 554 | buf[cnt] = LookupAddr{ |
| | 555 | .family = os.AF_INET, |
| | 556 | .addr = [4]u8{ 127, 0, 0, 1 } ++ ([1]u8{0} ** 12), |
| | 557 | }; |
| | 558 | cnt += 1; |
| | 559 | } |
| | 560 | if (family != os.AF_INET) { |
| | 561 | buf[cnt] = LookupAddr{ |
| | 562 | .family = os.AF_INET6, |
| | 563 | .addr = ([1]u8{0} ** 15) ++ [1]u8{1}, |
| | 564 | }; |
| | 565 | cnt += 1; |
| | 566 | } |
| | 567 | } |
| | 568 | return cnt; |
| | 569 | } |
| | 570 | |
| | 571 | fn linuxLookupNameFromHosts( |
| | 572 | buf: []LookupAddr, |
| | 573 | canon_buf: []u8, |
| | 574 | canon_len: *usize, |
| | 575 | name: []const u8, |
| | 576 | family: i32, |
| | 577 | ) !usize { |
| | 578 | const file = std.fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) { |
| | 579 | error.FileNotFound, |
| | 580 | error.NotDir, |
| | 581 | error.AccessDenied, |
| | 582 | => return 0, |
| | 583 | else => |e| return e, |
| | 584 | }; |
| | 585 | defer file.close(); |
| | 586 | |
| | 587 | var cnt: usize = 0; |
| | 588 | const stream = &std.io.BufferedInStream(std.fs.File.ReadError).init(&file.inStream().stream).stream; |
| | 589 | var line_buf: [512]u8 = undefined; |
| | 590 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { |
| | 591 | error.StreamTooLong => blk: { |
| | 592 | // Skip to the delimiter in the stream, to fix parsing |
| | 593 | try stream.skipUntilDelimiterOrEof('\n'); |
| | 594 | // Use the truncated line. A truncated comment or hostname will be handled correctly. |
| | 595 | break :blk line_buf[0..]; |
| | 596 | }, |
| | 597 | else => |e| return e, |
| | 598 | }) |line| { |
| | 599 | const no_comment_line = mem.separate(line, "#").next().?; |
| | 600 | |
| | 601 | var line_it = mem.tokenize(no_comment_line, " \t"); |
| | 602 | const ip_text = line_it.next() orelse continue; |
| | 603 | var first_name_text: ?[]const u8 = null; |
| | 604 | while (line_it.next()) |name_text| { |
| | 605 | if (first_name_text == null) first_name_text = name_text; |
| | 606 | if (mem.eql(u8, name_text, name)) { |
| | 607 | break; |
| | 608 | } |
| | 609 | } else continue; |
| | 610 | |
| | 611 | switch (linuxLookupNameFromNumeric(buf[cnt..], ip_text, family) catch |err| switch (err) { |
| | 612 | error.ExpectedIPv6ButFoundIPv4 => continue, |
| | 613 | error.ExpectedIPv4ButFoundIPv6 => continue, |
| | 614 | }) { |
| | 615 | 0 => continue, |
| | 616 | 1 => { |
| | 617 | // first name is canonical name |
| | 618 | const name_text = first_name_text.?; |
| | 619 | if (isValidHostName(name_text)) { |
| | 620 | mem.copy(u8, canon_buf, name_text); |
| | 621 | canon_len.* = name_text.len; |
| | 622 | } |
| | 623 | |
| | 624 | cnt += 1; |
| | 625 | if (cnt == buf.len) break; |
| | 626 | }, |
| | 627 | else => unreachable, |
| | 628 | } |
| | 629 | } |
| | 630 | return cnt; |
| | 631 | } |
| | 632 | |
| | 633 | pub fn isValidHostName(hostname: []const u8) bool { |
| | 634 | if (hostname.len >= 254) return false; |
| | 635 | if (!std.unicode.utf8ValidateSlice(hostname)) return false; |
| | 636 | for (hostname) |byte| { |
| | 637 | if (byte >= 0x80 or byte == '.' or byte == '-' or std.ascii.isAlNum(byte)) { |
| | 638 | continue; |
| | 639 | } |
| | 640 | return false; |
| | 641 | } |
| | 642 | return true; |
| 425 | } | 643 | } |