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