From de718e442c31ba44858561739d639cef711b3547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Mon, 23 Feb 2026 22:00:27 -0500 Subject: [PATCH 1/4] Only accept numeric address in getnameinfo --- src/core/IronPython.Modules/_socket.cs | 9 ++++++--- src/core/IronPython/Runtime/Operations/StringOps.cs | 1 + tests/suite/modules/network_related/test__socket.py | 4 ++-- tests/suite/test_socket_stdlib.py | 4 ---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/IronPython.Modules/_socket.cs b/src/core/IronPython.Modules/_socket.cs index a28e30843..6d5ae050e 100644 --- a/src/core/IronPython.Modules/_socket.cs +++ b/src/core/IronPython.Modules/_socket.cs @@ -1173,7 +1173,7 @@ public static object getnameinfo(CodeContext/*!*/ context, [NotNone] PythonTuple // Host IList addrs; try { - addrs = HostToAddresses(context, host, AddressFamily.InterNetwork); + addrs = HostToAddresses(context, host, AddressFamily.InterNetwork, getHostEntry: false); } catch (IndexOutOfRangeException) { throw MakeGaiException(context, EAI_NODATA); } @@ -2002,7 +2002,7 @@ private static IPAddress HostToAddress(CodeContext/*!*/ context, string host, Ad /// not the same as the specified family. gaierror is also raised if the hostname cannot be /// converted to an IP address (e.g. through a name lookup failure). /// - private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host, AddressFamily family) { + private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host, AddressFamily family, bool getHostEntry = true) { host = ConvertSpecialAddresses(host); try { bool numeric = true; @@ -2022,6 +2022,9 @@ private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host } // Incorrect family will raise EAI_NODATA exception below } else { + if (!getHostEntry) { + throw MakeGaiException(context, EAI_NONAME); + } IPHostEntry hostEntry = Dns.GetHostEntry(host); List addrs = new List(); foreach (IPAddress ip in hostEntry.AddressList) { @@ -2032,7 +2035,7 @@ private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host if (addrs.Count > 0) return addrs.ToArray(); } throw MakeGaiException(context, EAI_NODATA); - } catch (SocketException ex) { + } catch (SocketException ex) { throw MakeGaiException(context, ex); } } diff --git a/src/core/IronPython/Runtime/Operations/StringOps.cs b/src/core/IronPython/Runtime/Operations/StringOps.cs index fce9ba646..f17a210d3 100644 --- a/src/core/IronPython/Runtime/Operations/StringOps.cs +++ b/src/core/IronPython/Runtime/Operations/StringOps.cs @@ -623,6 +623,7 @@ public static bool isalpha([NotNone] this string self) { return true; } + // new in Python 3.7 public static bool isascii([NotNone] this string self) { foreach (char c in self) { if (c > 0x7f) return false; diff --git a/tests/suite/modules/network_related/test__socket.py b/tests/suite/modules/network_related/test__socket.py index 594eabb58..82f773834 100644 --- a/tests/suite/modules/network_related/test__socket.py +++ b/tests/suite/modules/network_related/test__socket.py @@ -352,8 +352,8 @@ def test_getnameinfo(self): host, service = _socket.getnameinfo( ("127.0.0.1", 80), 0) self.assertEqual(service, "http") #IP gives a TypeError - #self.assertRaises(SystemError, _socket.getnameinfo, ("127.0.0.1"), 8) - #self.assertRaises(SystemError, _socket.getnameinfo, (321), 8) + self.assertRaises(TypeError, _socket.getnameinfo, ("127.0.0.1"), 8) + self.assertRaises(TypeError, _socket.getnameinfo, (321), 8) self.assertRaises(TypeError, _socket.getnameinfo, ("127.0.0.1"), '0') self.assertRaises(TypeError, _socket.getnameinfo, ("127.0.0.1", 80, 0, 0, 0), 8) self.assertRaises(_socket.gaierror, _socket.getnameinfo, ('no such host will ever exist', 80), 8) diff --git a/tests/suite/test_socket_stdlib.py b/tests/suite/test_socket_stdlib.py index fbfe9ff4f..247429f58 100644 --- a/tests/suite/test_socket_stdlib.py +++ b/tests/suite/test_socket_stdlib.py @@ -41,10 +41,6 @@ def load_tests(loader, standard_tests, pattern): failing_tests += [ test.test_socket.NonBlockingTCPTests('testRecv'), # TODO: figure out ] - if not is_mono: - failing_tests += [ - test.test_socket.GeneralModuleTests('test_getnameinfo'), # https://github.com/IronLanguages/ironpython3/issues/1222 - ] if is_linux: failing_tests += [ test.test_socket.GeneralModuleTests('test_idna'), # TODO: figure out From 7700660581d6ce49fc9694ccb5d5312e47a053f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Tue, 24 Feb 2026 09:06:39 -0500 Subject: [PATCH 2/4] Simplify getnameinfo implementation --- src/core/IronPython.Modules/_socket.cs | 67 ++++++++++---------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/src/core/IronPython.Modules/_socket.cs b/src/core/IronPython.Modules/_socket.cs index 6d5ae050e..9f5cde8a3 100644 --- a/src/core/IronPython.Modules/_socket.cs +++ b/src/core/IronPython.Modules/_socket.cs @@ -1151,7 +1151,8 @@ public static object getnameinfo(CodeContext/*!*/ context, [NotNone] PythonTuple string host = Converter.ConvertToString(socketAddr[0]); if (host == null) throw PythonOps.TypeError("argument 1 must be string"); - int port = 0; + + int port; try { port = (int)socketAddr[1]!; } catch (InvalidCastException) { @@ -1167,60 +1168,45 @@ public static object getnameinfo(CodeContext/*!*/ context, [NotNone] PythonTuple } } - string resultHost; - string resultPort; - // Host - IList addrs; - try { - addrs = HostToAddresses(context, host, AddressFamily.InterNetwork, getHostEntry: false); - } catch (IndexOutOfRangeException) { + if (!IPAddress.TryParse(host, out IPAddress? addr)) { throw MakeGaiException(context, EAI_NODATA); } - if (addrs.Count > 1) { - // ignore non-IPV4 addresses - List newAddrs = new List(addrs.Count); - foreach (IPAddress addr in addrs) { - if (addr.AddressFamily == AddressFamily.InterNetwork) { - newAddrs.Add(addr); + string resultHost; + if ((flags & NI_NUMERICHOST) != 0) { + resultHost = addr.ToString(); + } else { + string hostName; + if (addr.Equals(IPAddress.Any) || addr.Equals(IPAddress.IPv6Any)) { + hostName = Dns.GetHostName(); + } else { + try { + hostName = Dns.GetHostEntry(addr).HostName; + } catch (SocketException ex) { + throw MakeGaiException(context, ex); } } - if (newAddrs.Count > 1) { - throw PythonExceptions.CreateThrowable(error, "sockaddr resolved to multiple addresses"); - } - addrs = newAddrs; - } - if (addrs.Count < 1) { - throw MakeGaiException(context, EAI_NODATA); - } - - IPHostEntry hostEntry; - try { - hostEntry = Dns.GetHostEntry(addrs[0]); - } catch (SocketException ex) { - throw MakeGaiException(context, ex); - } - if ((flags & (int)NI_NUMERICHOST) != 0) { - resultHost = addrs[0].ToString(); - } else if ((flags & (int)NI_NOFQDN) != 0) { - resultHost = RemoveLocalDomain(hostEntry.HostName); - } else { - resultHost = hostEntry.HostName; + if ((flags & NI_NOFQDN) != 0) { + resultHost = RemoveLocalDomain(hostName); + } else { + resultHost = hostName; + } } // Port - if ((flags & (int)NI_NUMERICSERV) == 0) { + string resultPort; + if ((flags & NI_NUMERICSERV) == 0) { //call the servbyport to translate port if not just use the port.ToString as the result try { resultPort = getservbyport(context, port); } catch { resultPort = port.ToString(); } - flags = flags | (int)NI_NUMERICSERV; - } else + } else { resultPort = port.ToString(); + } return PythonTuple.MakeTuple(resultHost, resultPort); } @@ -2002,7 +1988,7 @@ private static IPAddress HostToAddress(CodeContext/*!*/ context, string host, Ad /// not the same as the specified family. gaierror is also raised if the hostname cannot be /// converted to an IP address (e.g. through a name lookup failure). /// - private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host, AddressFamily family, bool getHostEntry = true) { + private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host, AddressFamily family) { host = ConvertSpecialAddresses(host); try { bool numeric = true; @@ -2022,9 +2008,6 @@ private static IPAddress[] HostToAddresses(CodeContext/*!*/ context, string host } // Incorrect family will raise EAI_NODATA exception below } else { - if (!getHostEntry) { - throw MakeGaiException(context, EAI_NONAME); - } IPHostEntry hostEntry = Dns.GetHostEntry(host); List addrs = new List(); foreach (IPAddress ip in hostEntry.AddressList) { From 1cd685b40b3c2038d5e8f16a1da6dd9dd16ecdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Wed, 25 Feb 2026 22:10:12 -0500 Subject: [PATCH 3/4] Add some test cases --- .../modules/network_related/test__socket.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/suite/modules/network_related/test__socket.py b/tests/suite/modules/network_related/test__socket.py index 82f773834..0aa811750 100644 --- a/tests/suite/modules/network_related/test__socket.py +++ b/tests/suite/modules/network_related/test__socket.py @@ -358,6 +358,28 @@ def test_getnameinfo(self): self.assertRaises(TypeError, _socket.getnameinfo, ("127.0.0.1", 80, 0, 0, 0), 8) self.assertRaises(_socket.gaierror, _socket.getnameinfo, ('no such host will ever exist', 80), 8) + # try some IPv6 cases + host, service = _socket.getnameinfo(("::1", 80), 0) + self.assertEqual(service, "http") + + ports = { + 80: "http", + 443: "https", + } + + addresses = ["::1", "::", "::0"] + address_num = {"::0": "::"} + + cases = [] + + for address in addresses: + for port, port_name in ports.items(): + cases.append((((address, port), 0), (host, port_name))) + cases.append((((address, port), _socket.NI_NUMERICHOST | _socket.NI_NUMERICSERV), (address_num.get(address, address), str(port)))) + + for args, result in cases: + self.assertEqual(_socket.getnameinfo(*args), result) + def test_gethostbyaddr(self): '''Tests _socket.gethostbyaddr''' _socket.gethostbyaddr("localhost") From 0f1336003df375dbc07061eeb64eebdbb3540aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 26 Feb 2026 19:38:35 -0500 Subject: [PATCH 4/4] Don't check host name --- .../suite/modules/network_related/test__socket.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/suite/modules/network_related/test__socket.py b/tests/suite/modules/network_related/test__socket.py index 0aa811750..f3ff0124f 100644 --- a/tests/suite/modules/network_related/test__socket.py +++ b/tests/suite/modules/network_related/test__socket.py @@ -359,9 +359,6 @@ def test_getnameinfo(self): self.assertRaises(_socket.gaierror, _socket.getnameinfo, ('no such host will ever exist', 80), 8) # try some IPv6 cases - host, service = _socket.getnameinfo(("::1", 80), 0) - self.assertEqual(service, "http") - ports = { 80: "http", 443: "https", @@ -370,15 +367,13 @@ def test_getnameinfo(self): addresses = ["::1", "::", "::0"] address_num = {"::0": "::"} - cases = [] - for address in addresses: for port, port_name in ports.items(): - cases.append((((address, port), 0), (host, port_name))) - cases.append((((address, port), _socket.NI_NUMERICHOST | _socket.NI_NUMERICSERV), (address_num.get(address, address), str(port)))) - - for args, result in cases: - self.assertEqual(_socket.getnameinfo(*args), result) + res = _socket.getnameinfo((address, port), 0) + # don't check the host name, unreliable across platforms + self.assertEqual(res[1], port_name) + res = _socket.getnameinfo((address, port), _socket.NI_NUMERICHOST | _socket.NI_NUMERICSERV) + self.assertEqual(res, (address_num.get(address, address), str(port))) def test_gethostbyaddr(self): '''Tests _socket.gethostbyaddr'''