| ... | ... | @@ -85,9 +85,9 @@ pub const Address = struct { |
| 85 | 85 | count: usize, |
| 86 | 86 | }; |
| 87 | 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 | 89 | const native_endian_parts = switch (builtin.endian) { |
| 90 | | .Big => big_endian_parts, |
| 90 | .Big => big_endian_parts.*, |
| 91 | 91 | .Little => blk: { |
| 92 | 92 | var buf: [8]u16 = undefined; |
| 93 | 93 | for (big_endian_parts) |part, i| { |
| ... | ... | @@ -163,13 +163,8 @@ pub fn parseIp4(buf: []const u8) !u32 { |
| 163 | 163 | saw_any_digits = false; |
| 164 | 164 | } else if (c >= '0' and c <= '9') { |
| 165 | 165 | saw_any_digits = true; |
| 166 | | const digit = c - '0'; |
| 167 | | if (@mulWithOverflow(u8, x, 10, &x)) { |
| 168 | | return error.Overflow; |
| 169 | | } |
| 170 | | if (@addWithOverflow(u8, x, digit, &x)) { |
| 171 | | return error.Overflow; |
| 172 | | } |
| 166 | x = try std.math.mul(u8, x, 10); |
| 167 | x = try std.math.add(u8, x, c - '0'); |
| 173 | 168 | } else { |
| 174 | 169 | return error.InvalidCharacter; |
| 175 | 170 | } |
| ... | ... | @@ -184,13 +179,13 @@ pub fn parseIp4(buf: []const u8) !u32 { |
| 184 | 179 | |
| 185 | 180 | pub const Ip6Addr = struct { |
| 186 | 181 | scope_id: u32, |
| 187 | | addr: [8]u16, |
| 182 | addr: [16]u8, |
| 188 | 183 | }; |
| 189 | 184 | |
| 190 | 185 | pub fn parseIp6(buf: []const u8) !Ip6Addr { |
| 191 | 186 | var result: Ip6Addr = undefined; |
| 192 | 187 | result.scope_id = 0; |
| 193 | | const ip_slice = @sliceToBytes(result.addr[0..]); |
| 188 | const ip_slice = result.addr[0..]; |
| 194 | 189 | |
| 195 | 190 | var x: u16 = 0; |
| 196 | 191 | var saw_any_digits = false; |
| ... | ... | @@ -323,7 +318,7 @@ pub fn connectUnixSocket(path: []const u8) !std.fs.File { |
| 323 | 318 | pub const AddressList = struct { |
| 324 | 319 | arena: std.heap.ArenaAllocator, |
| 325 | 320 | addrs: []Address, |
| 326 | | canon_names: []?[]u8, |
| 321 | canon_name: ?[]u8, |
| 327 | 322 | |
| 328 | 323 | fn deinit(self: *AddressList) void { |
| 329 | 324 | // Here we copy the arena allocator into stack memory, because |
| ... | ... | @@ -336,7 +331,28 @@ pub const AddressList = struct { |
| 336 | 331 | |
| 337 | 332 | /// Call `AddressList.deinit` on the result. |
| 338 | 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 | 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 | 356 | const hints = os.addrinfo{ |
| 341 | 357 | .flags = os.AI_NUMERICSERV, |
| 342 | 358 | .family = os.AF_UNSPEC, |
| ... | ... | @@ -347,27 +363,6 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 347 | 363 | .addrlen = 0, |
| 348 | 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 | 366 | var res: *os.addrinfo = undefined; |
| 372 | 367 | switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { |
| 373 | 368 | 0 => {}, |
| ... | ... | @@ -378,9 +373,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 378 | 373 | os.EAI_FAMILY => return error.AddressFamilyNotSupported, |
| 379 | 374 | os.EAI_MEMORY => return error.OutOfMemory, |
| 380 | 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 |
| 382 | | // was specified in hints.ai_flags and service was not a numeric port-number string. |
| 383 | | os.EAI_NONAME => unreachable, // Invalid hints |
| 376 | os.EAI_NONAME => return error.UnknownName, |
| 384 | 377 | os.EAI_SERVICE => return error.ServiceUnavailable, |
| 385 | 378 | os.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints |
| 386 | 379 | os.EAI_SYSTEM => switch (os.errno(-1)) { |
| ... | ... | @@ -401,25 +394,250 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* |
| 401 | 394 | break :blk count; |
| 402 | 395 | }; |
| 403 | 396 | result.addrs = try arena.alloc(Address, addr_count); |
| 404 | | result.canon_names = try arena.alloc(?[]u8, addr_count); |
| 405 | 397 | |
| 406 | 398 | var it: ?*os.addrinfo = res; |
| 407 | 399 | var i: usize = 0; |
| 408 | 400 | while (it) |info| : (it = info.next) { |
| 409 | 401 | const addr = info.addr orelse continue; |
| 410 | | result.addrs[i] = std.net.Address.initPosix(addr.*); |
| 411 | | result.canon_names[i] = null; |
| 402 | result.addrs[i] = Address.initPosix(addr.*); |
| 412 | 403 | |
| 413 | 404 | 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]; |
| 405 | if (result.canon_name == null) { |
| 406 | result.canon_name = try mem.dupe(arena, u8, mem.toSliceConst(u8, n)); |
| 407 | } |
| 418 | 408 | } |
| 419 | 409 | i += 1; |
| 420 | 410 | } |
| 421 | 411 | |
| 422 | 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 | } |