| ... | ... | @@ -11,6 +11,8 @@ const io = std.io; |
| 11 | 11 | const native_endian = builtin.target.cpu.arch.endian(); |
| 12 | 12 | const native_os = builtin.os.tag; |
| 13 | 13 | const windows = std.os.windows; |
| 14 | const Allocator = std.mem.Allocator; |
| 15 | const ArrayList = std.ArrayListUnmanaged; |
| 14 | 16 | |
| 15 | 17 | // Windows 10 added support for unix sockets in build 17063, redstone 4 is the |
| 16 | 18 | // first release to support them. |
| ... | ... | @@ -818,7 +820,7 @@ pub const AddressList = struct { |
| 818 | 820 | pub const TcpConnectToHostError = GetAddressListError || TcpConnectToAddressError; |
| 819 | 821 | |
| 820 | 822 | /// All memory allocated with `allocator` will be freed before this function returns. |
| 821 | | pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream { |
| 823 | pub fn tcpConnectToHost(allocator: Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream { |
| 822 | 824 | const list = try getAddressList(allocator, name, port); |
| 823 | 825 | defer list.deinit(); |
| 824 | 826 | |
| ... | ... | @@ -851,7 +853,7 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { |
| 851 | 853 | |
| 852 | 854 | // TODO: Instead of having a massive error set, make the error set have categories, and then |
| 853 | 855 | // store the sub-error as a diagnostic value. |
| 854 | | const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ |
| 856 | const GetAddressListError = Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ |
| 855 | 857 | TemporaryNameServerFailure, |
| 856 | 858 | NameServerFailure, |
| 857 | 859 | AddressFamilyNotSupported, |
| ... | ... | @@ -871,12 +873,13 @@ const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || |
| 871 | 873 | |
| 872 | 874 | InterfaceNotFound, |
| 873 | 875 | FileSystem, |
| 876 | ResolveConfParseFailed, |
| 874 | 877 | }; |
| 875 | 878 | |
| 876 | 879 | /// Call `AddressList.deinit` on the result. |
| 877 | | pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList { |
| 880 | pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList { |
| 878 | 881 | const result = blk: { |
| 879 | | var arena = std.heap.ArenaAllocator.init(allocator); |
| 882 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 880 | 883 | errdefer arena.deinit(); |
| 881 | 884 | |
| 882 | 885 | const result = try arena.allocator().create(AddressList); |
| ... | ... | @@ -891,11 +894,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 891 | 894 | errdefer result.deinit(); |
| 892 | 895 | |
| 893 | 896 | if (native_os == .windows) { |
| 894 | | const name_c = try allocator.dupeZ(u8, name); |
| 895 | | defer allocator.free(name_c); |
| 897 | const name_c = try gpa.dupeZ(u8, name); |
| 898 | defer gpa.free(name_c); |
| 896 | 899 | |
| 897 | | const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); |
| 898 | | defer allocator.free(port_c); |
| 900 | const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port}); |
| 901 | defer gpa.free(port_c); |
| 899 | 902 | |
| 900 | 903 | const ws2_32 = windows.ws2_32; |
| 901 | 904 | const hints: posix.addrinfo = .{ |
| ... | ... | @@ -963,11 +966,11 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 963 | 966 | } |
| 964 | 967 | |
| 965 | 968 | if (builtin.link_libc) { |
| 966 | | const name_c = try allocator.dupeZ(u8, name); |
| 967 | | defer allocator.free(name_c); |
| 969 | const name_c = try gpa.dupeZ(u8, name); |
| 970 | defer gpa.free(name_c); |
| 968 | 971 | |
| 969 | | const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); |
| 970 | | defer allocator.free(port_c); |
| 972 | const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port}); |
| 973 | defer gpa.free(port_c); |
| 971 | 974 | |
| 972 | 975 | const hints: posix.addrinfo = .{ |
| 973 | 976 | .flags = .{ .NUMERICSERV = true }, |
| ... | ... | @@ -1030,17 +1033,17 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 1030 | 1033 | |
| 1031 | 1034 | if (native_os == .linux) { |
| 1032 | 1035 | const family = posix.AF.UNSPEC; |
| 1033 | | var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); |
| 1034 | | defer lookup_addrs.deinit(); |
| 1036 | var lookup_addrs: ArrayList(LookupAddr) = .empty; |
| 1037 | defer lookup_addrs.deinit(gpa); |
| 1035 | 1038 | |
| 1036 | | var canon = std.ArrayList(u8).init(arena); |
| 1037 | | defer canon.deinit(); |
| 1039 | var canon: ArrayList(u8) = .empty; |
| 1040 | defer canon.deinit(gpa); |
| 1038 | 1041 | |
| 1039 | | try linuxLookupName(&lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port); |
| 1042 | try linuxLookupName(gpa, &lookup_addrs, &canon, name, family, .{ .NUMERICSERV = true }, port); |
| 1040 | 1043 | |
| 1041 | 1044 | result.addrs = try arena.alloc(Address, lookup_addrs.items.len); |
| 1042 | 1045 | if (canon.items.len != 0) { |
| 1043 | | result.canon_name = try canon.toOwnedSlice(); |
| 1046 | result.canon_name = try arena.dupe(u8, canon.items); |
| 1044 | 1047 | } |
| 1045 | 1048 | |
| 1046 | 1049 | for (lookup_addrs.items, 0..) |lookup_addr, i| { |
| ... | ... | @@ -1067,8 +1070,9 @@ const DAS_PREFIX_SHIFT = 8; |
| 1067 | 1070 | const DAS_ORDER_SHIFT = 0; |
| 1068 | 1071 | |
| 1069 | 1072 | fn linuxLookupName( |
| 1070 | | addrs: *std.ArrayList(LookupAddr), |
| 1071 | | canon: *std.ArrayList(u8), |
| 1073 | gpa: Allocator, |
| 1074 | addrs: *ArrayList(LookupAddr), |
| 1075 | canon: *ArrayList(u8), |
| 1072 | 1076 | opt_name: ?[]const u8, |
| 1073 | 1077 | family: posix.sa_family_t, |
| 1074 | 1078 | flags: posix.AI, |
| ... | ... | @@ -1077,13 +1081,13 @@ fn linuxLookupName( |
| 1077 | 1081 | if (opt_name) |name| { |
| 1078 | 1082 | // reject empty name and check len so it fits into temp bufs |
| 1079 | 1083 | canon.items.len = 0; |
| 1080 | | try canon.appendSlice(name); |
| 1084 | try canon.appendSlice(gpa, name); |
| 1081 | 1085 | if (Address.parseExpectingFamily(name, family, port)) |addr| { |
| 1082 | | try addrs.append(LookupAddr{ .addr = addr }); |
| 1086 | try addrs.append(gpa, .{ .addr = addr }); |
| 1083 | 1087 | } else |name_err| if (flags.NUMERICHOST) { |
| 1084 | 1088 | return name_err; |
| 1085 | 1089 | } else { |
| 1086 | | try linuxLookupNameFromHosts(addrs, canon, name, family, port); |
| 1090 | try linuxLookupNameFromHosts(gpa, addrs, canon, name, family, port); |
| 1087 | 1091 | if (addrs.items.len == 0) { |
| 1088 | 1092 | // RFC 6761 Section 6.3.3 |
| 1089 | 1093 | // Name resolution APIs and libraries SHOULD recognize localhost |
| ... | ... | @@ -1094,17 +1098,18 @@ fn linuxLookupName( |
| 1094 | 1098 | // Check for equal to "localhost(.)" or ends in ".localhost(.)" |
| 1095 | 1099 | const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost"; |
| 1096 | 1100 | if (mem.endsWith(u8, name, localhost) and (name.len == localhost.len or name[name.len - localhost.len] == '.')) { |
| 1097 | | try addrs.append(LookupAddr{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } }); |
| 1098 | | try addrs.append(LookupAddr{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } }); |
| 1101 | try addrs.append(gpa, .{ .addr = .{ .in = Ip4Address.parse("127.0.0.1", port) catch unreachable } }); |
| 1102 | try addrs.append(gpa, .{ .addr = .{ .in6 = Ip6Address.parse("::1", port) catch unreachable } }); |
| 1099 | 1103 | return; |
| 1100 | 1104 | } |
| 1101 | 1105 | |
| 1102 | | try linuxLookupNameFromDnsSearch(addrs, canon, name, family, port); |
| 1106 | try linuxLookupNameFromDnsSearch(gpa, addrs, canon, name, family, port); |
| 1103 | 1107 | } |
| 1104 | 1108 | } |
| 1105 | 1109 | } else { |
| 1106 | | try canon.resize(0); |
| 1107 | | try linuxLookupNameFromNull(addrs, family, flags, port); |
| 1110 | try canon.resize(gpa, 0); |
| 1111 | try addrs.ensureUnusedCapacity(gpa, 1); |
| 1112 | linuxLookupNameFromNull(addrs, family, flags, port); |
| 1108 | 1113 | } |
| 1109 | 1114 | if (addrs.items.len == 0) return error.UnknownHostName; |
| 1110 | 1115 | |
| ... | ... | @@ -1310,39 +1315,40 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool { |
| 1310 | 1315 | } |
| 1311 | 1316 | |
| 1312 | 1317 | fn linuxLookupNameFromNull( |
| 1313 | | addrs: *std.ArrayList(LookupAddr), |
| 1318 | addrs: *ArrayList(LookupAddr), |
| 1314 | 1319 | family: posix.sa_family_t, |
| 1315 | 1320 | flags: posix.AI, |
| 1316 | 1321 | port: u16, |
| 1317 | | ) !void { |
| 1322 | ) void { |
| 1318 | 1323 | if (flags.PASSIVE) { |
| 1319 | 1324 | if (family != posix.AF.INET6) { |
| 1320 | | (try addrs.addOne()).* = LookupAddr{ |
| 1325 | addrs.appendAssumeCapacity(.{ |
| 1321 | 1326 | .addr = Address.initIp4([1]u8{0} ** 4, port), |
| 1322 | | }; |
| 1327 | }); |
| 1323 | 1328 | } |
| 1324 | 1329 | if (family != posix.AF.INET) { |
| 1325 | | (try addrs.addOne()).* = LookupAddr{ |
| 1330 | addrs.appendAssumeCapacity(.{ |
| 1326 | 1331 | .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0), |
| 1327 | | }; |
| 1332 | }); |
| 1328 | 1333 | } |
| 1329 | 1334 | } else { |
| 1330 | 1335 | if (family != posix.AF.INET6) { |
| 1331 | | (try addrs.addOne()).* = LookupAddr{ |
| 1336 | addrs.appendAssumeCapacity(.{ |
| 1332 | 1337 | .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port), |
| 1333 | | }; |
| 1338 | }); |
| 1334 | 1339 | } |
| 1335 | 1340 | if (family != posix.AF.INET) { |
| 1336 | | (try addrs.addOne()).* = LookupAddr{ |
| 1341 | addrs.appendAssumeCapacity(.{ |
| 1337 | 1342 | .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), |
| 1338 | | }; |
| 1343 | }); |
| 1339 | 1344 | } |
| 1340 | 1345 | } |
| 1341 | 1346 | } |
| 1342 | 1347 | |
| 1343 | 1348 | fn linuxLookupNameFromHosts( |
| 1344 | | addrs: *std.ArrayList(LookupAddr), |
| 1345 | | canon: *std.ArrayList(u8), |
| 1349 | gpa: Allocator, |
| 1350 | addrs: *ArrayList(LookupAddr), |
| 1351 | canon: *ArrayList(u8), |
| 1346 | 1352 | name: []const u8, |
| 1347 | 1353 | family: posix.sa_family_t, |
| 1348 | 1354 | port: u16, |
| ... | ... | @@ -1359,7 +1365,34 @@ fn linuxLookupNameFromHosts( |
| 1359 | 1365 | var line_buf: [512]u8 = undefined; |
| 1360 | 1366 | var file_reader = file.reader(); |
| 1361 | 1367 | var br = file_reader.interface().buffered(&line_buf); |
| 1362 | | while (br.takeSentinel('\n')) |line| { |
| 1368 | return parseHosts(gpa, addrs, canon, name, family, port, &br) catch |err| switch (err) { |
| 1369 | error.OutOfMemory => return error.OutOfMemory, |
| 1370 | error.ReadFailed => return file_reader.err.?, |
| 1371 | }; |
| 1372 | } |
| 1373 | |
| 1374 | fn parseHosts( |
| 1375 | gpa: Allocator, |
| 1376 | addrs: *ArrayList(LookupAddr), |
| 1377 | canon: *ArrayList(u8), |
| 1378 | name: []const u8, |
| 1379 | family: posix.sa_family_t, |
| 1380 | port: u16, |
| 1381 | br: *std.io.BufferedReader, |
| 1382 | ) error{ OutOfMemory, ReadFailed }!void { |
| 1383 | while (true) { |
| 1384 | const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) { |
| 1385 | error.StreamTooLong => { |
| 1386 | // Skip lines that are too long. |
| 1387 | br.discardDelimiterInclusive('\n') catch |e| switch (e) { |
| 1388 | error.EndOfStream => break, |
| 1389 | error.ReadFailed => return error.ReadFailed, |
| 1390 | }; |
| 1391 | continue; |
| 1392 | }, |
| 1393 | error.ReadFailed => return error.ReadFailed, |
| 1394 | error.EndOfStream => break, |
| 1395 | }; |
| 1363 | 1396 | var split_it = mem.splitScalar(u8, line, '#'); |
| 1364 | 1397 | const no_comment_line = split_it.first(); |
| 1365 | 1398 | |
| ... | ... | @@ -1383,15 +1416,15 @@ fn linuxLookupNameFromHosts( |
| 1383 | 1416 | error.NonCanonical, |
| 1384 | 1417 | => continue, |
| 1385 | 1418 | }; |
| 1386 | | try addrs.append(LookupAddr{ .addr = addr }); |
| 1419 | try addrs.append(gpa, .{ .addr = addr }); |
| 1387 | 1420 | |
| 1388 | 1421 | // first name is canonical name |
| 1389 | 1422 | const name_text = first_name_text.?; |
| 1390 | 1423 | if (isValidHostName(name_text)) { |
| 1391 | 1424 | canon.items.len = 0; |
| 1392 | | try canon.appendSlice(name_text); |
| 1425 | try canon.appendSlice(gpa, name_text); |
| 1393 | 1426 | } |
| 1394 | | } else |err| return err; |
| 1427 | } |
| 1395 | 1428 | } |
| 1396 | 1429 | |
| 1397 | 1430 | pub fn isValidHostName(hostname: []const u8) bool { |
| ... | ... | @@ -1407,14 +1440,15 @@ pub fn isValidHostName(hostname: []const u8) bool { |
| 1407 | 1440 | } |
| 1408 | 1441 | |
| 1409 | 1442 | fn linuxLookupNameFromDnsSearch( |
| 1410 | | addrs: *std.ArrayList(LookupAddr), |
| 1411 | | canon: *std.ArrayList(u8), |
| 1443 | gpa: Allocator, |
| 1444 | addrs: *ArrayList(LookupAddr), |
| 1445 | canon: *ArrayList(u8), |
| 1412 | 1446 | name: []const u8, |
| 1413 | 1447 | family: posix.sa_family_t, |
| 1414 | 1448 | port: u16, |
| 1415 | 1449 | ) !void { |
| 1416 | 1450 | var rc: ResolvConf = undefined; |
| 1417 | | try getResolvConf(addrs.allocator, &rc); |
| 1451 | rc.init(gpa) catch return error.ResolveConfParseFailed; |
| 1418 | 1452 | defer rc.deinit(); |
| 1419 | 1453 | |
| 1420 | 1454 | // Count dots, suppress search when >=ndots or name ends in |
| ... | ... | @@ -1439,37 +1473,40 @@ fn linuxLookupNameFromDnsSearch( |
| 1439 | 1473 | // provides the desired default canonical name (if the requested |
| 1440 | 1474 | // name is not a CNAME record) and serves as a buffer for passing |
| 1441 | 1475 | // the full requested name to name_from_dns. |
| 1442 | | try canon.resize(canon_name.len); |
| 1476 | try canon.resize(gpa, canon_name.len); |
| 1443 | 1477 | @memcpy(canon.items, canon_name); |
| 1444 | | try canon.append('.'); |
| 1478 | try canon.append(gpa, '.'); |
| 1445 | 1479 | |
| 1446 | 1480 | var tok_it = mem.tokenizeAny(u8, search, " \t"); |
| 1447 | 1481 | while (tok_it.next()) |tok| { |
| 1448 | 1482 | canon.shrinkRetainingCapacity(canon_name.len + 1); |
| 1449 | | try canon.appendSlice(tok); |
| 1450 | | try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port); |
| 1483 | try canon.appendSlice(gpa, tok); |
| 1484 | try linuxLookupNameFromDns(gpa, addrs, canon, canon.items, family, rc, port); |
| 1451 | 1485 | if (addrs.items.len != 0) return; |
| 1452 | 1486 | } |
| 1453 | 1487 | |
| 1454 | 1488 | canon.shrinkRetainingCapacity(canon_name.len); |
| 1455 | | return linuxLookupNameFromDns(addrs, canon, name, family, rc, port); |
| 1489 | return linuxLookupNameFromDns(gpa, addrs, canon, name, family, rc, port); |
| 1456 | 1490 | } |
| 1457 | 1491 | |
| 1458 | 1492 | const dpc_ctx = struct { |
| 1459 | | addrs: *std.ArrayList(LookupAddr), |
| 1460 | | canon: *std.ArrayList(u8), |
| 1493 | gpa: Allocator, |
| 1494 | addrs: *ArrayList(LookupAddr), |
| 1495 | canon: *ArrayList(u8), |
| 1461 | 1496 | port: u16, |
| 1462 | 1497 | }; |
| 1463 | 1498 | |
| 1464 | 1499 | fn linuxLookupNameFromDns( |
| 1465 | | addrs: *std.ArrayList(LookupAddr), |
| 1466 | | canon: *std.ArrayList(u8), |
| 1500 | gpa: Allocator, |
| 1501 | addrs: *ArrayList(LookupAddr), |
| 1502 | canon: *ArrayList(u8), |
| 1467 | 1503 | name: []const u8, |
| 1468 | 1504 | family: posix.sa_family_t, |
| 1469 | 1505 | rc: ResolvConf, |
| 1470 | 1506 | port: u16, |
| 1471 | 1507 | ) !void { |
| 1472 | | const ctx = dpc_ctx{ |
| 1508 | const ctx: dpc_ctx = .{ |
| 1509 | .gpa = gpa, |
| 1473 | 1510 | .addrs = addrs, |
| 1474 | 1511 | .canon = canon, |
| 1475 | 1512 | .port = port, |
| ... | ... | @@ -1479,8 +1516,8 @@ fn linuxLookupNameFromDns( |
| 1479 | 1516 | rr: u8, |
| 1480 | 1517 | }; |
| 1481 | 1518 | const afrrs = [_]AfRr{ |
| 1482 | | AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A }, |
| 1483 | | AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA }, |
| 1519 | .{ .af = posix.AF.INET6, .rr = posix.RR.A }, |
| 1520 | .{ .af = posix.AF.INET, .rr = posix.RR.AAAA }, |
| 1484 | 1521 | }; |
| 1485 | 1522 | var qbuf: [2][280]u8 = undefined; |
| 1486 | 1523 | var abuf: [2][512]u8 = undefined; |
| ... | ... | @@ -1500,7 +1537,7 @@ fn linuxLookupNameFromDns( |
| 1500 | 1537 | ap[0].len = 0; |
| 1501 | 1538 | ap[1].len = 0; |
| 1502 | 1539 | |
| 1503 | | try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); |
| 1540 | try rc.resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq]); |
| 1504 | 1541 | |
| 1505 | 1542 | var i: usize = 0; |
| 1506 | 1543 | while (i < nq) : (i += 1) { |
| ... | ... | @@ -1515,240 +1552,252 @@ fn linuxLookupNameFromDns( |
| 1515 | 1552 | } |
| 1516 | 1553 | |
| 1517 | 1554 | const ResolvConf = struct { |
| 1555 | gpa: Allocator, |
| 1518 | 1556 | attempts: u32, |
| 1519 | 1557 | ndots: u32, |
| 1520 | 1558 | timeout: u32, |
| 1521 | | search: std.ArrayList(u8), |
| 1522 | | ns: std.ArrayList(LookupAddr), |
| 1523 | | |
| 1524 | | fn deinit(rc: *ResolvConf) void { |
| 1525 | | rc.ns.deinit(); |
| 1526 | | rc.search.deinit(); |
| 1527 | | rc.* = undefined; |
| 1559 | search: ArrayList(u8), |
| 1560 | /// TODO there are actually only allowed to be maximum 3 nameservers, no need |
| 1561 | /// for an array list. |
| 1562 | ns: ArrayList(LookupAddr), |
| 1563 | |
| 1564 | /// Returns `error.StreamTooLong` if a line is longer than 512 bytes. |
| 1565 | /// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 |
| 1566 | fn init(rc: *ResolvConf, gpa: Allocator) !void { |
| 1567 | rc.* = .{ |
| 1568 | .gpa = gpa, |
| 1569 | .ns = .empty, |
| 1570 | .search = .empty, |
| 1571 | .ndots = 1, |
| 1572 | .timeout = 5, |
| 1573 | .attempts = 2, |
| 1574 | }; |
| 1575 | errdefer rc.deinit(); |
| 1576 | |
| 1577 | const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) { |
| 1578 | error.FileNotFound, |
| 1579 | error.NotDir, |
| 1580 | error.AccessDenied, |
| 1581 | => return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53), |
| 1582 | else => |e| return e, |
| 1583 | }; |
| 1584 | defer file.close(); |
| 1585 | |
| 1586 | var line_buf: [512]u8 = undefined; |
| 1587 | var file_reader = file.reader(); |
| 1588 | var br = file_reader.interface().buffered(&line_buf); |
| 1589 | return parse(rc, &br) catch |err| switch (err) { |
| 1590 | error.ReadFailed => return file_reader.err.?, |
| 1591 | else => |e| return e, |
| 1592 | }; |
| 1528 | 1593 | } |
| 1529 | | }; |
| 1530 | | |
| 1531 | | /// Returns `error.StreamTooLong` if a line is longer than 512 bytes. |
| 1532 | | /// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 |
| 1533 | | fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { |
| 1534 | | rc.* = .{ |
| 1535 | | .ns = std.ArrayList(LookupAddr).init(allocator), |
| 1536 | | .search = std.ArrayList(u8).init(allocator), |
| 1537 | | .ndots = 1, |
| 1538 | | .timeout = 5, |
| 1539 | | .attempts = 2, |
| 1540 | | }; |
| 1541 | | errdefer rc.deinit(); |
| 1542 | | |
| 1543 | | const file = fs.openFileAbsoluteZ("/etc/resolv.conf", .{}) catch |err| switch (err) { |
| 1544 | | error.FileNotFound, |
| 1545 | | error.NotDir, |
| 1546 | | error.AccessDenied, |
| 1547 | | => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53), |
| 1548 | | else => |e| return e, |
| 1549 | | }; |
| 1550 | | defer file.close(); |
| 1551 | 1594 | |
| 1552 | | var line_buf: [512]u8 = undefined; |
| 1553 | | var file_reader = file.reader(); |
| 1554 | | var br = file_reader.interface().buffered(&line_buf); |
| 1555 | | while (br.takeSentinel('\n')) |line_with_comment| { |
| 1556 | | const line = line: { |
| 1557 | | var split = mem.splitScalar(u8, line_with_comment, '#'); |
| 1558 | | break :line split.first(); |
| 1559 | | }; |
| 1560 | | var line_it = mem.tokenizeAny(u8, line, " \t"); |
| 1561 | | |
| 1562 | | const token = line_it.next() orelse continue; |
| 1563 | | if (mem.eql(u8, token, "options")) { |
| 1564 | | while (line_it.next()) |sub_tok| { |
| 1565 | | var colon_it = mem.splitScalar(u8, sub_tok, ':'); |
| 1566 | | const name = colon_it.first(); |
| 1567 | | const value_txt = colon_it.next() orelse continue; |
| 1568 | | const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { |
| 1569 | | // TODO https://github.com/ziglang/zig/issues/11812 |
| 1570 | | error.Overflow => @as(u8, 255), |
| 1571 | | error.InvalidCharacter => continue, |
| 1572 | | }; |
| 1573 | | if (mem.eql(u8, name, "ndots")) { |
| 1574 | | rc.ndots = @min(value, 15); |
| 1575 | | } else if (mem.eql(u8, name, "attempts")) { |
| 1576 | | rc.attempts = @min(value, 10); |
| 1577 | | } else if (mem.eql(u8, name, "timeout")) { |
| 1578 | | rc.timeout = @min(value, 60); |
| 1595 | fn parse(rc: *ResolvConf, br: *std.io.BufferedReader) !void { |
| 1596 | const gpa = rc.gpa; |
| 1597 | while (br.takeSentinel('\n')) |line_with_comment| { |
| 1598 | const line = line: { |
| 1599 | var split = mem.splitScalar(u8, line_with_comment, '#'); |
| 1600 | break :line split.first(); |
| 1601 | }; |
| 1602 | var line_it = mem.tokenizeAny(u8, line, " \t"); |
| 1603 | |
| 1604 | const token = line_it.next() orelse continue; |
| 1605 | if (mem.eql(u8, token, "options")) { |
| 1606 | while (line_it.next()) |sub_tok| { |
| 1607 | var colon_it = mem.splitScalar(u8, sub_tok, ':'); |
| 1608 | const name = colon_it.first(); |
| 1609 | const value_txt = colon_it.next() orelse continue; |
| 1610 | const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { |
| 1611 | error.Overflow => 255, |
| 1612 | error.InvalidCharacter => continue, |
| 1613 | }; |
| 1614 | if (mem.eql(u8, name, "ndots")) { |
| 1615 | rc.ndots = @min(value, 15); |
| 1616 | } else if (mem.eql(u8, name, "attempts")) { |
| 1617 | rc.attempts = @min(value, 10); |
| 1618 | } else if (mem.eql(u8, name, "timeout")) { |
| 1619 | rc.timeout = @min(value, 60); |
| 1620 | } |
| 1579 | 1621 | } |
| 1622 | } else if (mem.eql(u8, token, "nameserver")) { |
| 1623 | const ip_txt = line_it.next() orelse continue; |
| 1624 | try linuxLookupNameFromNumericUnspec(gpa, &rc.ns, ip_txt, 53); |
| 1625 | } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) { |
| 1626 | rc.search.items.len = 0; |
| 1627 | try rc.search.appendSlice(gpa, line_it.rest()); |
| 1580 | 1628 | } |
| 1581 | | } else if (mem.eql(u8, token, "nameserver")) { |
| 1582 | | const ip_txt = line_it.next() orelse continue; |
| 1583 | | try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53); |
| 1584 | | } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) { |
| 1585 | | rc.search.items.len = 0; |
| 1586 | | try rc.search.appendSlice(line_it.rest()); |
| 1587 | | } |
| 1588 | | } else |err| return err; |
| 1629 | } else |err| return err; |
| 1589 | 1630 | |
| 1590 | | if (rc.ns.items.len == 0) { |
| 1591 | | return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53); |
| 1631 | if (rc.ns.items.len == 0) { |
| 1632 | return linuxLookupNameFromNumericUnspec(gpa, &rc.ns, "127.0.0.1", 53); |
| 1633 | } |
| 1592 | 1634 | } |
| 1593 | | } |
| 1594 | 1635 | |
| 1595 | | fn linuxLookupNameFromNumericUnspec( |
| 1596 | | addrs: *std.ArrayList(LookupAddr), |
| 1597 | | name: []const u8, |
| 1598 | | port: u16, |
| 1599 | | ) !void { |
| 1600 | | const addr = try Address.resolveIp(name, port); |
| 1601 | | (try addrs.addOne()).* = LookupAddr{ .addr = addr }; |
| 1602 | | } |
| 1603 | | |
| 1604 | | fn resMSendRc( |
| 1605 | | queries: []const []const u8, |
| 1606 | | answers: [][]u8, |
| 1607 | | answer_bufs: []const []u8, |
| 1608 | | rc: ResolvConf, |
| 1609 | | ) !void { |
| 1610 | | const timeout = 1000 * rc.timeout; |
| 1611 | | const attempts = rc.attempts; |
| 1636 | fn resMSendRc( |
| 1637 | rc: ResolvConf, |
| 1638 | queries: []const []const u8, |
| 1639 | answers: [][]u8, |
| 1640 | answer_bufs: []const []u8, |
| 1641 | ) !void { |
| 1642 | const gpa = rc.gpa; |
| 1643 | const timeout = 1000 * rc.timeout; |
| 1644 | const attempts = rc.attempts; |
| 1612 | 1645 | |
| 1613 | | var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in); |
| 1614 | | var family: posix.sa_family_t = posix.AF.INET; |
| 1646 | var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in); |
| 1647 | var family: posix.sa_family_t = posix.AF.INET; |
| 1615 | 1648 | |
| 1616 | | var ns_list = std.ArrayList(Address).init(rc.ns.allocator); |
| 1617 | | defer ns_list.deinit(); |
| 1649 | var ns_list: ArrayList(Address) = .empty; |
| 1650 | defer ns_list.deinit(gpa); |
| 1618 | 1651 | |
| 1619 | | try ns_list.resize(rc.ns.items.len); |
| 1620 | | const ns = ns_list.items; |
| 1652 | try ns_list.resize(gpa, rc.ns.items.len); |
| 1621 | 1653 | |
| 1622 | | for (rc.ns.items, 0..) |iplit, i| { |
| 1623 | | ns[i] = iplit.addr; |
| 1624 | | assert(ns[i].getPort() == 53); |
| 1625 | | if (iplit.addr.any.family != posix.AF.INET) { |
| 1626 | | family = posix.AF.INET6; |
| 1654 | for (ns_list.items, rc.ns.items) |*ns, iplit| { |
| 1655 | ns.* = iplit.addr; |
| 1656 | assert(ns.getPort() == 53); |
| 1657 | if (iplit.addr.any.family != posix.AF.INET) { |
| 1658 | family = posix.AF.INET6; |
| 1659 | } |
| 1627 | 1660 | } |
| 1628 | | } |
| 1629 | 1661 | |
| 1630 | | const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK; |
| 1631 | | const fd = posix.socket(family, flags, 0) catch |err| switch (err) { |
| 1632 | | error.AddressFamilyNotSupported => blk: { |
| 1633 | | // Handle case where system lacks IPv6 support |
| 1634 | | if (family == posix.AF.INET6) { |
| 1635 | | family = posix.AF.INET; |
| 1636 | | break :blk try posix.socket(posix.AF.INET, flags, 0); |
| 1662 | const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK; |
| 1663 | const fd = posix.socket(family, flags, 0) catch |err| switch (err) { |
| 1664 | error.AddressFamilyNotSupported => blk: { |
| 1665 | // Handle case where system lacks IPv6 support |
| 1666 | if (family == posix.AF.INET6) { |
| 1667 | family = posix.AF.INET; |
| 1668 | break :blk try posix.socket(posix.AF.INET, flags, 0); |
| 1669 | } |
| 1670 | return err; |
| 1671 | }, |
| 1672 | else => |e| return e, |
| 1673 | }; |
| 1674 | defer Stream.close(.{ .handle = fd }); |
| 1675 | |
| 1676 | // Past this point, there are no errors. Each individual query will |
| 1677 | // yield either no reply (indicated by zero length) or an answer |
| 1678 | // packet which is up to the caller to interpret. |
| 1679 | |
| 1680 | // Convert any IPv4 addresses in a mixed environment to v4-mapped |
| 1681 | if (family == posix.AF.INET6) { |
| 1682 | try posix.setsockopt( |
| 1683 | fd, |
| 1684 | posix.SOL.IPV6, |
| 1685 | std.os.linux.IPV6.V6ONLY, |
| 1686 | &mem.toBytes(@as(c_int, 0)), |
| 1687 | ); |
| 1688 | for (ns_list.items) |*ns| { |
| 1689 | if (ns.any.family != posix.AF.INET) continue; |
| 1690 | mem.writeInt(u32, ns.in6.sa.addr[12..], ns.in.sa.addr, native_endian); |
| 1691 | ns.in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*; |
| 1692 | ns.any.family = posix.AF.INET6; |
| 1693 | ns.in6.sa.flowinfo = 0; |
| 1694 | ns.in6.sa.scope_id = 0; |
| 1637 | 1695 | } |
| 1638 | | return err; |
| 1639 | | }, |
| 1640 | | else => |e| return e, |
| 1641 | | }; |
| 1642 | | defer Stream.close(.{ .handle = fd }); |
| 1643 | | |
| 1644 | | // Past this point, there are no errors. Each individual query will |
| 1645 | | // yield either no reply (indicated by zero length) or an answer |
| 1646 | | // packet which is up to the caller to interpret. |
| 1647 | | |
| 1648 | | // Convert any IPv4 addresses in a mixed environment to v4-mapped |
| 1649 | | if (family == posix.AF.INET6) { |
| 1650 | | try posix.setsockopt( |
| 1651 | | fd, |
| 1652 | | posix.SOL.IPV6, |
| 1653 | | std.os.linux.IPV6.V6ONLY, |
| 1654 | | &mem.toBytes(@as(c_int, 0)), |
| 1655 | | ); |
| 1656 | | for (0..ns.len) |i| { |
| 1657 | | if (ns[i].any.family != posix.AF.INET) continue; |
| 1658 | | mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian); |
| 1659 | | ns[i].in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*; |
| 1660 | | ns[i].any.family = posix.AF.INET6; |
| 1661 | | ns[i].in6.sa.flowinfo = 0; |
| 1662 | | ns[i].in6.sa.scope_id = 0; |
| 1696 | sl = @sizeOf(posix.sockaddr.in6); |
| 1663 | 1697 | } |
| 1664 | | sl = @sizeOf(posix.sockaddr.in6); |
| 1665 | | } |
| 1666 | 1698 | |
| 1667 | | // Get local address and open/bind a socket |
| 1668 | | var sa: Address = undefined; |
| 1669 | | @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0); |
| 1670 | | sa.any.family = family; |
| 1671 | | try posix.bind(fd, &sa.any, sl); |
| 1672 | | |
| 1673 | | var pfd = [1]posix.pollfd{posix.pollfd{ |
| 1674 | | .fd = fd, |
| 1675 | | .events = posix.POLL.IN, |
| 1676 | | .revents = undefined, |
| 1677 | | }}; |
| 1678 | | const retry_interval = timeout / attempts; |
| 1679 | | var next: u32 = 0; |
| 1680 | | var t2: u64 = @bitCast(std.time.milliTimestamp()); |
| 1681 | | const t0 = t2; |
| 1682 | | var t1 = t2 - retry_interval; |
| 1683 | | |
| 1684 | | var servfail_retry: usize = undefined; |
| 1685 | | |
| 1686 | | outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) { |
| 1687 | | if (t2 - t1 >= retry_interval) { |
| 1688 | | // Query all configured nameservers in parallel |
| 1689 | | var i: usize = 0; |
| 1690 | | while (i < queries.len) : (i += 1) { |
| 1691 | | if (answers[i].len == 0) { |
| 1692 | | var j: usize = 0; |
| 1693 | | while (j < ns.len) : (j += 1) { |
| 1694 | | _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined; |
| 1699 | // Get local address and open/bind a socket |
| 1700 | var sa: Address = undefined; |
| 1701 | @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0); |
| 1702 | sa.any.family = family; |
| 1703 | try posix.bind(fd, &sa.any, sl); |
| 1704 | |
| 1705 | var pfd = [1]posix.pollfd{posix.pollfd{ |
| 1706 | .fd = fd, |
| 1707 | .events = posix.POLL.IN, |
| 1708 | .revents = undefined, |
| 1709 | }}; |
| 1710 | const retry_interval = timeout / attempts; |
| 1711 | var next: u32 = 0; |
| 1712 | var t2: u64 = @bitCast(std.time.milliTimestamp()); |
| 1713 | const t0 = t2; |
| 1714 | var t1 = t2 - retry_interval; |
| 1715 | |
| 1716 | var servfail_retry: usize = undefined; |
| 1717 | |
| 1718 | outer: while (t2 - t0 < timeout) : (t2 = @as(u64, @bitCast(std.time.milliTimestamp()))) { |
| 1719 | if (t2 - t1 >= retry_interval) { |
| 1720 | // Query all configured nameservers in parallel |
| 1721 | var i: usize = 0; |
| 1722 | while (i < queries.len) : (i += 1) { |
| 1723 | if (answers[i].len == 0) { |
| 1724 | for (ns_list.items) |*ns| { |
| 1725 | _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined; |
| 1726 | } |
| 1695 | 1727 | } |
| 1696 | 1728 | } |
| 1729 | t1 = t2; |
| 1730 | servfail_retry = 2 * queries.len; |
| 1697 | 1731 | } |
| 1698 | | t1 = t2; |
| 1699 | | servfail_retry = 2 * queries.len; |
| 1700 | | } |
| 1701 | 1732 | |
| 1702 | | // Wait for a response, or until time to retry |
| 1703 | | const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2); |
| 1704 | | const nevents = posix.poll(&pfd, clamped_timeout) catch 0; |
| 1705 | | if (nevents == 0) continue; |
| 1733 | // Wait for a response, or until time to retry |
| 1734 | const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2); |
| 1735 | const nevents = posix.poll(&pfd, clamped_timeout) catch 0; |
| 1736 | if (nevents == 0) continue; |
| 1737 | |
| 1738 | while (true) { |
| 1739 | var sl_copy = sl; |
| 1740 | const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; |
| 1741 | |
| 1742 | // Ignore non-identifiable packets |
| 1743 | if (rlen < 4) continue; |
| 1744 | |
| 1745 | // Ignore replies from addresses we didn't send to |
| 1746 | const ns = for (ns_list.items) |*ns| { |
| 1747 | if (ns.eql(sa)) break ns; |
| 1748 | } else continue; |
| 1749 | |
| 1750 | // Find which query this answer goes with, if any |
| 1751 | var i: usize = next; |
| 1752 | while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or |
| 1753 | answer_bufs[next][1] != queries[i][1])) : (i += 1) |
| 1754 | {} |
| 1755 | |
| 1756 | if (i == queries.len) continue; |
| 1757 | if (answers[i].len != 0) continue; |
| 1758 | |
| 1759 | // Only accept positive or negative responses; |
| 1760 | // retry immediately on server failure, and ignore |
| 1761 | // all other codes such as refusal. |
| 1762 | switch (answer_bufs[next][3] & 15) { |
| 1763 | 0, 3 => {}, |
| 1764 | 2 => if (servfail_retry != 0) { |
| 1765 | servfail_retry -= 1; |
| 1766 | _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns.any, sl) catch undefined; |
| 1767 | }, |
| 1768 | else => continue, |
| 1769 | } |
| 1706 | 1770 | |
| 1707 | | while (true) { |
| 1708 | | var sl_copy = sl; |
| 1709 | | const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; |
| 1710 | | |
| 1711 | | // Ignore non-identifiable packets |
| 1712 | | if (rlen < 4) continue; |
| 1713 | | |
| 1714 | | // Ignore replies from addresses we didn't send to |
| 1715 | | var j: usize = 0; |
| 1716 | | while (j < ns.len and !ns[j].eql(sa)) : (j += 1) {} |
| 1717 | | if (j == ns.len) continue; |
| 1718 | | |
| 1719 | | // Find which query this answer goes with, if any |
| 1720 | | var i: usize = next; |
| 1721 | | while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or |
| 1722 | | answer_bufs[next][1] != queries[i][1])) : (i += 1) |
| 1723 | | {} |
| 1724 | | |
| 1725 | | if (i == queries.len) continue; |
| 1726 | | if (answers[i].len != 0) continue; |
| 1727 | | |
| 1728 | | // Only accept positive or negative responses; |
| 1729 | | // retry immediately on server failure, and ignore |
| 1730 | | // all other codes such as refusal. |
| 1731 | | switch (answer_bufs[next][3] & 15) { |
| 1732 | | 0, 3 => {}, |
| 1733 | | 2 => if (servfail_retry != 0) { |
| 1734 | | servfail_retry -= 1; |
| 1735 | | _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined; |
| 1736 | | }, |
| 1737 | | else => continue, |
| 1738 | | } |
| 1771 | // Store answer in the right slot, or update next |
| 1772 | // available temp slot if it's already in place. |
| 1773 | answers[i].len = rlen; |
| 1774 | if (i == next) { |
| 1775 | while (next < queries.len and answers[next].len != 0) : (next += 1) {} |
| 1776 | } else { |
| 1777 | @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]); |
| 1778 | } |
| 1739 | 1779 | |
| 1740 | | // Store answer in the right slot, or update next |
| 1741 | | // available temp slot if it's already in place. |
| 1742 | | answers[i].len = rlen; |
| 1743 | | if (i == next) { |
| 1744 | | while (next < queries.len and answers[next].len != 0) : (next += 1) {} |
| 1745 | | } else { |
| 1746 | | @memcpy(answer_bufs[i][0..rlen], answer_bufs[next][0..rlen]); |
| 1780 | if (next == queries.len) break :outer; |
| 1747 | 1781 | } |
| 1748 | | |
| 1749 | | if (next == queries.len) break :outer; |
| 1750 | 1782 | } |
| 1751 | 1783 | } |
| 1784 | |
| 1785 | fn deinit(rc: *ResolvConf) void { |
| 1786 | const gpa = rc.gpa; |
| 1787 | rc.ns.deinit(gpa); |
| 1788 | rc.search.deinit(gpa); |
| 1789 | rc.* = undefined; |
| 1790 | } |
| 1791 | }; |
| 1792 | |
| 1793 | fn linuxLookupNameFromNumericUnspec( |
| 1794 | gpa: Allocator, |
| 1795 | addrs: *ArrayList(LookupAddr), |
| 1796 | name: []const u8, |
| 1797 | port: u16, |
| 1798 | ) !void { |
| 1799 | const addr = try Address.resolveIp(name, port); |
| 1800 | try addrs.append(gpa, .{ .addr = addr }); |
| 1752 | 1801 | } |
| 1753 | 1802 | |
| 1754 | 1803 | fn dnsParse( |
| ... | ... | @@ -1785,20 +1834,19 @@ fn dnsParse( |
| 1785 | 1834 | } |
| 1786 | 1835 | |
| 1787 | 1836 | fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void { |
| 1837 | const gpa = ctx.gpa; |
| 1788 | 1838 | switch (rr) { |
| 1789 | 1839 | posix.RR.A => { |
| 1790 | 1840 | if (data.len != 4) return error.InvalidDnsARecord; |
| 1791 | | const new_addr = try ctx.addrs.addOne(); |
| 1792 | | new_addr.* = LookupAddr{ |
| 1841 | try ctx.addrs.append(gpa, .{ |
| 1793 | 1842 | .addr = Address.initIp4(data[0..4].*, ctx.port), |
| 1794 | | }; |
| 1843 | }); |
| 1795 | 1844 | }, |
| 1796 | 1845 | posix.RR.AAAA => { |
| 1797 | 1846 | if (data.len != 16) return error.InvalidDnsAAAARecord; |
| 1798 | | const new_addr = try ctx.addrs.addOne(); |
| 1799 | | new_addr.* = LookupAddr{ |
| 1847 | try ctx.addrs.append(gpa, .{ |
| 1800 | 1848 | .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0), |
| 1801 | | }; |
| 1849 | }); |
| 1802 | 1850 | }, |
| 1803 | 1851 | posix.RR.CNAME => { |
| 1804 | 1852 | var tmp: [256]u8 = undefined; |
| ... | ... | @@ -1807,7 +1855,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) |
| 1807 | 1855 | const canon_name = mem.sliceTo(&tmp, 0); |
| 1808 | 1856 | if (isValidHostName(canon_name)) { |
| 1809 | 1857 | ctx.canon.items.len = 0; |
| 1810 | | try ctx.canon.appendSlice(canon_name); |
| 1858 | try ctx.canon.appendSlice(gpa, canon_name); |
| 1811 | 1859 | } |
| 1812 | 1860 | }, |
| 1813 | 1861 | else => return, |