From 47699cdf5138fa9dbaf8718e14b6397145c6a40c Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Fri, 13 May 2022 10:47:29 +1000 Subject: [PATCH 1/7] Add Hyper-V socket support --- Doc/library/socket.rst | 37 ++++++++ Lib/test/test_socket.py | 48 ++++++++++ ...2-05-13-00-57-18.gh-issue-92658.YdhFE2.rst | 1 + Modules/socketmodule.c | 93 +++++++++++++++++++ Modules/socketmodule.h | 11 +++ 5 files changed, 190 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-05-13-00-57-18.gh-issue-92658.YdhFE2.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 4c193b892bda8f..2f4ee43acd058f 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -225,6 +225,33 @@ created. Socket addresses are represented as follows: .. versionadded:: 3.9 +- :const:`AF_HYPERV` is a Windows-only socket based interface for communicating + with Hyper-V hosts and guests. The address family is represented as a + ``(vm_id, service_id)`` tuple where the ``vm_id`` and ``service_id`` are the + little endian byte representation of a ``uuid.UUID`` object. + + The ``vm_id`` is the virtual machine identifier or a set of known VMID values + if the target is not a specific virtual machine. Known VMID values are: + + - ``HV_GUID_ZERO 00000000-0000-0000-0000-000000000000`` - Used to bind on + itself and accept connections from all partitions. + - ``HV_GUID_BROADCAST FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF`` + - ``HV_GUID_CHILDREN 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd`` - Used to bind on + itself and accept connection from child partitions. + - ``HV_GUID_LOOPBACK e0e16197-dd56-4a10-9195-5ee7a155a838`` - Used as a + target to itself. + - ``HV_GUID_PARENT a42e7cda-d03f-480c-9cc2-a4de20abb878`` - When used as a + bind accepts connection from the parent partition. When used as an address + target it will connect to the parent parition. + + The ``service_id`` is the registered service identifier of the registered + service. + + The easily get the byte value do + ``uuid.UUID("eee5f691-5210-47e8-bbc9-7198bed79b77").bytes_le``. + + .. versionadded:: 3.12 + If you use a hostname in the *host* portion of IPv4/v6 socket address, the program may show a nondeterministic behavior, as Python uses the first address returned from the DNS resolution. The socket address will be resolved @@ -589,6 +616,16 @@ Constants .. availability:: Linux >= 3.9 +.. data:: AF_HYPERV + HV_PROTOCOL_RAW + HVSOCKET_* + + Constants for Windows Hyper-V sockets for host/guest communications. + + .. availability:: Windows. + + .. versionadded:: 3.12 + Functions ^^^^^^^^^ diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1aaa9e44f90c65..6b71f3249897e1 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -143,6 +143,17 @@ def _have_socket_bluetooth(): return True +def _have_socket_hyperv(): + """Check whether AF_HYPERV sockets are supported on this host.""" + try: + s = socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) + except (AttributeError, OSError): + return False + else: + s.close() + return True + + @contextlib.contextmanager def socket_setdefaulttimeout(timeout): old_timeout = socket.getdefaulttimeout() @@ -171,6 +182,8 @@ def socket_setdefaulttimeout(timeout): HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth() +HAVE_SOCKET_HYPERV = _have_socket_hyperv() + # Size in bytes of the int type SIZEOF_INT = array.array("i").itemsize @@ -2459,6 +2472,41 @@ def testCreateScoSocket(self): pass +@unittest.skipUnless(HAVE_SOCKET_HYPERV, + 'Hyper-V sockets required for this test.') +class BasicHyperVTest(unittest.TestCase): + + def testHyperVConstants(self): + socket.HVSOCKET_CONNECT_TIMEOUT + socket.HVSOCKET_CONNECT_TIMEOUT_MAX + socket.HVSOCKET_CONTAINER_PASSTHRU + socket.HVSOCKET_CONNECTED_SUSPEND + socket.HVSOCKET_ADDRESS_FLAG_PASSTHRU + + def testCreateHyperVSocketWithUnknownProtoFailure(self): + self.assertRaises(OSError, socket.socket, socket.AF_HYPERV, socket.SOCK_STREAM) + + def testCreateHyperVSocketAddrNotTupleFailure(self): + with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: + self.assertRaises(TypeError, s.connect, b"\x00" * 16) + + def testCreateHyperVSocketAddrNotTupleOf2BytesFailure(self): + with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: + self.assertRaises(TypeError, s.connect, (b"\x00" * 16,)) + + def testCreateHyperVSocketAddrNotTupleOfBytesFailure(self): + with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: + self.assertRaises(TypeError, s.connect, (1, 2)) + + def testCreateHyperVSocketAddrVmIdNotCorrectLengthFailure(self): + with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: + self.assertRaises(TypeError, s.connect, (b"\x00", b"\x00" * 16)) + + def testCreateHyperVSocketAddrServiceIdNotCorrectLengthFailure(self): + with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: + self.assertRaises(TypeError, s.connect, (b"\x00" * 16, b"\x00")) + + class BasicTCPTest(SocketConnectedTest): def __init__(self, methodName='runTest'): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-13-00-57-18.gh-issue-92658.YdhFE2.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-13-00-57-18.gh-issue-92658.YdhFE2.rst new file mode 100644 index 00000000000000..887b3d61596609 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-05-13-00-57-18.gh-issue-92658.YdhFE2.rst @@ -0,0 +1 @@ +Add support for connecting and binding to Hyper-V sockets on Windows Hyper-V hosts and guests. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index f376513fead1b8..cb7fa331c3c69d 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1579,6 +1579,18 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) } #endif /* HAVE_SOCKADDR_ALG */ +#ifdef AF_HYPERV + case AF_HYPERV: + { + SOCKADDR_HV *a = (SOCKADDR_HV *) addr; + return Py_BuildValue("y#y#", + a->VmId, + sizeof(GUID), + a->ServiceId, + sizeof(GUID)); + } +#endif /* AF_HYPERV */ + /* More cases here... */ default: @@ -2375,6 +2387,66 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 1; } #endif /* HAVE_SOCKADDR_ALG */ +#ifdef AF_HYPERV + case AF_HYPERV: + { + switch (s->sock_proto) { + case HV_PROTOCOL_RAW: + { + GUID *vm_id; + Py_ssize_t vm_id_len = 0; + + GUID *service_id; + Py_ssize_t service_id_len = 0; + + SOCKADDR_HV *addr = &addrbuf->hv; + + memset(addr, 0, sizeof(*addr)); + addr->Family = AF_HYPERV; + + if (!PyTuple_Check(args)) { + PyErr_Format(PyExc_TypeError, + "%s(): AF_HYPERV address must be tuple, " + "not %.500s", + caller, Py_TYPE(args)->tp_name); + return 0; + } + if (!PyArg_ParseTuple(args, + "y#y#;AF_HYPERV address must be a tuple " + "(vm_id, service_id)", + &vm_id, &vm_id_len, &service_id, + &service_id_len)) + { + return 0; + } + if (vm_id_len != sizeof(GUID)) { + PyErr_Format(PyExc_TypeError, + "%s(): AF_HYPERV address vm_id must have a " + "length of %d", + caller, sizeof(GUID)); + return 0; + } + if (service_id_len != sizeof(GUID)) { + PyErr_Format(PyExc_TypeError, + "%s(): AF_HYPERV address service_id must have a " + "length of %d", + caller, sizeof(GUID)); + return 0; + } + + addr->VmId = *vm_id; + addr->ServiceId = *service_id; + + *len_ret = sizeof(*addr); + return 1; + } + default: + PyErr_Format(PyExc_OSError, + "%s(): unsupported AF_HYPERV protocol", caller); + return 0; + } + } +#endif /* AF_HYPERV */ /* More cases here... */ @@ -2524,6 +2596,13 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) return 1; } #endif /* HAVE_SOCKADDR_ALG */ +#ifdef AF_HYPERV + case AF_HYPERV: + { + *len_ret = sizeof (SOCKADDR_HV); + return 1; + } +#endif /* AF_HYPERV /* More cases here... */ @@ -7351,6 +7430,20 @@ PyInit__socket(void) /* Linux LLC */ PyModule_AddIntMacro(m, AF_LLC); #endif +#ifdef AF_HYPERV + /* Hyper-V sockets */ + PyModule_AddIntMacro(m, AF_HYPERV); + + /* for proto */ + PyModule_AddIntMacro(m, HV_PROTOCOL_RAW); + + /* for setsockopt() */ + PyModule_AddIntMacro(m, HVSOCKET_CONNECT_TIMEOUT); + PyModule_AddIntMacro(m, HVSOCKET_CONNECT_TIMEOUT_MAX); + PyModule_AddIntMacro(m, HVSOCKET_CONTAINER_PASSTHRU); + PyModule_AddIntMacro(m, HVSOCKET_CONNECTED_SUSPEND); + PyModule_AddIntMacro(m, HVSOCKET_ADDRESS_FLAG_PASSTHRU); +#endif /* AF_HYPERV */ #ifdef USE_BLUETOOTH PyModule_AddIntMacro(m, AF_BLUETOOTH); diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 1b35b11cdee6af..4f872845ba62f1 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -76,6 +76,14 @@ struct SOCKADDR_BTH_REDEF { # else typedef int socklen_t; # endif /* IPPROTO_IPV6 */ + +/* Future remove once Py_WINVER has been bumped to >=0x0604 */ +# ifndef AF_HYPERV +# define AF_HYPERV 34 +# endif + +/* FIXME: Should this have some sort of safe guard? */ +# include #endif /* MS_WINDOWS */ #ifdef HAVE_SYS_UN_H @@ -288,6 +296,9 @@ typedef union sock_addr { #ifdef HAVE_LINUX_TIPC_H struct sockaddr_tipc tipc; #endif +#ifdef AF_HYPERV + SOCKADDR_HV hv; +#endif } sock_addr_t; /* The object holding a socket. It holds some extra information, From 6c0d1583efedc067fa10eff26bcf56daa35e1140 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Fri, 20 May 2022 00:50:12 +1000 Subject: [PATCH 2/7] Use strings for addr information --- Doc/library/socket.rst | 33 ++++++------- Lib/test/test_socket.py | 32 ++++++++---- Modules/socketmodule.c | 106 +++++++++++++++++++++++++++------------- Modules/socketmodule.h | 2 - PCbuild/_socket.vcxproj | 2 +- 5 files changed, 109 insertions(+), 66 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 2f4ee43acd058f..d532075831ca28 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -227,28 +227,24 @@ created. Socket addresses are represented as follows: - :const:`AF_HYPERV` is a Windows-only socket based interface for communicating with Hyper-V hosts and guests. The address family is represented as a - ``(vm_id, service_id)`` tuple where the ``vm_id`` and ``service_id`` are the - little endian byte representation of a ``uuid.UUID`` object. + ``(vm_id, service_id)`` tuple where the ``vm_id`` and ``service_id`` are + UUID strings. The ``vm_id`` is the virtual machine identifier or a set of known VMID values - if the target is not a specific virtual machine. Known VMID values are: + if the target is not a specific virtual machine. Known VMID constants are + defined on ``socket`` are: - - ``HV_GUID_ZERO 00000000-0000-0000-0000-000000000000`` - Used to bind on - itself and accept connections from all partitions. - - ``HV_GUID_BROADCAST FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF`` - - ``HV_GUID_CHILDREN 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd`` - Used to bind on - itself and accept connection from child partitions. - - ``HV_GUID_LOOPBACK e0e16197-dd56-4a10-9195-5ee7a155a838`` - Used as a - target to itself. - - ``HV_GUID_PARENT a42e7cda-d03f-480c-9cc2-a4de20abb878`` - When used as a - bind accepts connection from the parent partition. When used as an address - target it will connect to the parent parition. + - ``HV_GUID_ZERO`` + - ``HV_GUID_BROADCAST`` + - ``HV_GUID_WILDCARD `` - Used to bind on itself and accept connections from + all partitions. + - ``HV_GUID_CHILDREN`` - Used to bind on itself and accept connection from + child partitions. + - ``HV_GUID_LOOPBACK`` - Used as a target to itself. + - ``HV_GUID_PARENT`` - When used as a bind accepts connection from the parent + partition. When used as an address target it will connect to the parent parition. - The ``service_id`` is the registered service identifier of the registered - service. - - The easily get the byte value do - ``uuid.UUID("eee5f691-5210-47e8-bbc9-7198bed79b77").bytes_le``. + The ``service_id`` is the service identifier of the registered service. .. versionadded:: 3.12 @@ -619,6 +615,7 @@ Constants .. data:: AF_HYPERV HV_PROTOCOL_RAW HVSOCKET_* + HV_GUID_* Constants for Windows Hyper-V sockets for host/guest communications. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 6b71f3249897e1..7997804f07dd57 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -2482,29 +2482,41 @@ def testHyperVConstants(self): socket.HVSOCKET_CONTAINER_PASSTHRU socket.HVSOCKET_CONNECTED_SUSPEND socket.HVSOCKET_ADDRESS_FLAG_PASSTHRU + socket.HV_GUID_ZERO + socket.HV_GUID_WILDCARD + socket.HV_GUID_BROADCAST + socket.HV_GUID_CHILDREN + socket.HV_GUID_LOOPBACK + socket.HV_GUID_LOOPBACK def testCreateHyperVSocketWithUnknownProtoFailure(self): - self.assertRaises(OSError, socket.socket, socket.AF_HYPERV, socket.SOCK_STREAM) + with self.assertRaises(OSError): + socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM) def testCreateHyperVSocketAddrNotTupleFailure(self): with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - self.assertRaises(TypeError, s.connect, b"\x00" * 16) + with self.assertRaises(TypeError): + s.connect(socket.HV_GUID_ZERO) - def testCreateHyperVSocketAddrNotTupleOf2BytesFailure(self): + def testCreateHyperVSocketAddrNotTupleOf2StrsFailure(self): with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - self.assertRaises(TypeError, s.connect, (b"\x00" * 16,)) + with self.assertRaises(TypeError): + s.connect((socket.HV_GUID_ZERO,)) - def testCreateHyperVSocketAddrNotTupleOfBytesFailure(self): + def testCreateHyperVSocketAddrNotTupleOfStrsFailure(self): with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - self.assertRaises(TypeError, s.connect, (1, 2)) + with self.assertRaises(TypeError): + s.connect((1, 2)) - def testCreateHyperVSocketAddrVmIdNotCorrectLengthFailure(self): + def testCreateHyperVSocketAddrVmIdNotValidUUIDFailure(self): with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - self.assertRaises(TypeError, s.connect, (b"\x00", b"\x00" * 16)) + with self.assertRaises(ValueError): + s.connect(("00", socket.HV_GUID_ZERO)) - def testCreateHyperVSocketAddrServiceIdNotCorrectLengthFailure(self): + def testCreateHyperVSocketAddrServiceIdNotValidUUIDFailure(self): with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - self.assertRaises(TypeError, s.connect, (b"\x00" * 16, b"\x00")) + with self.assertRaises(ValueError): + s.connect((socket.HV_GUID_ZERO, "00")) class BasicTCPTest(SocketConnectedTest): diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index cb7fa331c3c69d..de0ca26defc545 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -271,6 +271,9 @@ shutdown(how) -- shut down traffic in one or both directions\n\ # include # endif +/* Helpers needed for AF_HYPERV */ +# include + /* Macros based on the IPPROTO enum, see: https://bugs.python.org/issue29515 */ #ifdef MS_WINDOWS #define IPPROTO_ICMP IPPROTO_ICMP @@ -1580,15 +1583,32 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) #endif /* HAVE_SOCKADDR_ALG */ #ifdef AF_HYPERV - case AF_HYPERV: - { - SOCKADDR_HV *a = (SOCKADDR_HV *) addr; - return Py_BuildValue("y#y#", - a->VmId, - sizeof(GUID), - a->ServiceId, - sizeof(GUID)); - } + case AF_HYPERV: + { + SOCKADDR_HV *a = (SOCKADDR_HV *) addr; + + wchar_t *guidStr; + PyObject *vmId; + PyObject *serviceId; + + if (UuidToStringW(&a->VmId, &guidStr) == RPC_S_OUT_OF_MEMORY) { + PyErr_SetString(PyExc_MemoryError, + "out of memory in makesockaddr"); + return NULL; + } + vmId = PyUnicode_FromWideChar(guidStr, -1); + RpcStringFreeW(&guidStr); + + if (UuidToStringW(&a->ServiceId, &guidStr) == RPC_S_OUT_OF_MEMORY) { + PyErr_SetString(PyExc_MemoryError, + "out of memory in makesockaddr"); + return NULL; + } + serviceId = PyUnicode_FromWideChar(guidStr, -1); + RpcStringFreeW(&guidStr); + + return Py_BuildValue("NN", vmId, serviceId); + } #endif /* AF_HYPERV */ /* More cases here... */ @@ -2393,11 +2413,9 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, switch (s->sock_proto) { case HV_PROTOCOL_RAW: { - GUID *vm_id; - Py_ssize_t vm_id_len = 0; - - GUID *service_id; - Py_ssize_t service_id_len = 0; + PyObject *vm_id_obj = NULL; + PyObject *service_id_obj = NULL; + wchar_t *guid_str; SOCKADDR_HV *addr = &addrbuf->hv; @@ -2406,43 +2424,53 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, if (!PyTuple_Check(args)) { PyErr_Format(PyExc_TypeError, - "%s(): AF_HYPERV address must be tuple, " - "not %.500s", - caller, Py_TYPE(args)->tp_name); + "%s(): AF_HYPERV address must be tuple, not %.500s", + caller, Py_TYPE(args)->tp_name); return 0; } if (!PyArg_ParseTuple(args, - "y#y#;AF_HYPERV address must be a tuple " - "(vm_id, service_id)", - &vm_id, &vm_id_len, &service_id, - &service_id_len)) + "UU;AF_HYPERV address must be a str tuple (vm_id, service_id)", + &vm_id_obj, &service_id_obj)) { return 0; } - if (vm_id_len != sizeof(GUID)) { - PyErr_Format(PyExc_TypeError, - "%s(): AF_HYPERV address vm_id must have a " - "length of %d", - caller, sizeof(GUID)); + + guid_str = PyUnicode_AsWideCharString(vm_id_obj, NULL); + if (guid_str == NULL) { + PyErr_Format(PyExc_ValueError, + "%s(): AF_HYPERV address vm_id is not a valid UUID string", + caller); return 0; } - if (service_id_len != sizeof(GUID)) { - PyErr_Format(PyExc_TypeError, - "%s(): AF_HYPERV address service_id must have a " - "length of %d", - caller, sizeof(GUID)); + if (UuidFromStringW(guid_str, &addr->VmId) != RPC_S_OK) { + PyErr_Format(PyExc_ValueError, + "%s(): AF_HYPERV address vm_id is not a valid UUID string", + caller); return 0; } + PyMem_Free(guid_str); - addr->VmId = *vm_id; - addr->ServiceId = *service_id; + guid_str = PyUnicode_AsWideCharString(service_id_obj, NULL); + if (guid_str == NULL) { + PyErr_Format(PyExc_ValueError, + "%s(): AF_HYPERV address service_id is not a valid UUID string", + caller); + return 0; + } + if (UuidFromStringW(guid_str, &addr->ServiceId) != RPC_S_OK) { + PyErr_Format(PyExc_ValueError, + "%s(): AF_HYPERV address service_id is not a valid UUID string", + caller); + return 0; + } + PyMem_Free(guid_str); *len_ret = sizeof(*addr); return 1; } default: PyErr_Format(PyExc_OSError, - "%s(): unsupported AF_HYPERV protocol", caller); + "%s(): unsupported AF_HYPERV protocol", caller); return 0; } } @@ -2602,7 +2630,7 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) *len_ret = sizeof (SOCKADDR_HV); return 1; } -#endif /* AF_HYPERV +#endif /* AF_HYPERV */ /* More cases here... */ @@ -7443,6 +7471,14 @@ PyInit__socket(void) PyModule_AddIntMacro(m, HVSOCKET_CONTAINER_PASSTHRU); PyModule_AddIntMacro(m, HVSOCKET_CONNECTED_SUSPEND); PyModule_AddIntMacro(m, HVSOCKET_ADDRESS_FLAG_PASSTHRU); + + /* for bind() or connect() */ + PyModule_AddStringConstant(m, "HV_GUID_ZERO", "00000000-0000-0000-0000-000000000000"); + PyModule_AddStringConstant(m, "HV_GUID_WILDCARD", "00000000-0000-0000-0000-000000000000"); + PyModule_AddStringConstant(m, "HV_GUID_BROADCAST", "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"); + PyModule_AddStringConstant(m, "HV_GUID_CHILDREN", "90DB8B89-0D35-4F79-8CE9-49EA0AC8B7CD"); + PyModule_AddStringConstant(m, "HV_GUID_LOOPBACK", "E0E16197-DD56-4A10-9195-5EE7A155A838"); + PyModule_AddStringConstant(m, "HV_GUID_PARENT", "E0E16197-DD56-4A10-9195-5EE7A155A838"); #endif /* AF_HYPERV */ #ifdef USE_BLUETOOTH diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 4f872845ba62f1..96ab86024afe5d 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -81,8 +81,6 @@ typedef int socklen_t; # ifndef AF_HYPERV # define AF_HYPERV 34 # endif - -/* FIXME: Should this have some sort of safe guard? */ # include #endif /* MS_WINDOWS */ diff --git a/PCbuild/_socket.vcxproj b/PCbuild/_socket.vcxproj index 8fd75f90e7ee1e..78fa4d6729abb9 100644 --- a/PCbuild/_socket.vcxproj +++ b/PCbuild/_socket.vcxproj @@ -93,7 +93,7 @@ - ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) From 931031f6416352b1663d54e52272110a80ae6124 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 19 May 2022 16:54:59 +1000 Subject: [PATCH 3/7] Fix doc problem --- Doc/library/socket.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index d532075831ca28..614bf5566fc24e 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -236,7 +236,7 @@ created. Socket addresses are represented as follows: - ``HV_GUID_ZERO`` - ``HV_GUID_BROADCAST`` - - ``HV_GUID_WILDCARD `` - Used to bind on itself and accept connections from + - ``HV_GUID_WILDCARD`` - Used to bind on itself and accept connections from all partitions. - ``HV_GUID_CHILDREN`` - Used to bind on itself and accept connection from child partitions. From f39731c3acb10e2655f07dd7c0ac431cb07cee4a Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Fri, 20 May 2022 06:49:15 +1000 Subject: [PATCH 4/7] Fixes for review --- Doc/library/socket.rst | 13 +++++++++++-- Lib/test/test_socket.py | 20 ++++++++++++++------ Modules/socketmodule.c | 40 +++++++++++++++++++--------------------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 614bf5566fc24e..e4bd339e45dc6e 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -614,8 +614,17 @@ Constants .. data:: AF_HYPERV HV_PROTOCOL_RAW - HVSOCKET_* - HV_GUID_* + HVSOCKET_CONNECT_TIMEOUT + HVSOCKET_CONNECT_TIMEOUT_MAX + HVSOCKET_CONTAINER_PASSTHRU + HVSOCKET_CONNECTED_SUSPEND + HVSOCKET_ADDRESS_FLAG_PASSTHRU + HV_GUID_ZERO + HV_GUID_WILDCARD + HV_GUID_BROADCAST + HV_GUID_CHILDREN + HV_GUID_LOOPBACK + HV_GUID_LOOPBACK Constants for Windows Hyper-V sockets for host/guest communications. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 7997804f07dd57..c98190382416ca 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -22,6 +22,7 @@ import signal import math import pickle +import re import struct import random import shutil @@ -2490,32 +2491,39 @@ def testHyperVConstants(self): socket.HV_GUID_LOOPBACK def testCreateHyperVSocketWithUnknownProtoFailure(self): - with self.assertRaises(OSError): + expected = "A protocol was specified in the socket function call " \ + "that does not support the semantics of the socket type requested" + with self.assertRaisesRegex(OSError, expected): socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM) def testCreateHyperVSocketAddrNotTupleFailure(self): + expected = "connect(): AF_HYPERV address must be tuple, not str" with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - with self.assertRaises(TypeError): + with self.assertRaisesRegex(TypeError, re.escape(expected)): s.connect(socket.HV_GUID_ZERO) def testCreateHyperVSocketAddrNotTupleOf2StrsFailure(self): + expected = "AF_HYPERV address must be a str tuple (vm_id, service_id)" with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - with self.assertRaises(TypeError): + with self.assertRaisesRegex(TypeError, re.escape(expected)): s.connect((socket.HV_GUID_ZERO,)) def testCreateHyperVSocketAddrNotTupleOfStrsFailure(self): + expected = "AF_HYPERV address must be a str tuple (vm_id, service_id)" with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - with self.assertRaises(TypeError): + with self.assertRaisesRegex(TypeError, re.escape(expected)): s.connect((1, 2)) def testCreateHyperVSocketAddrVmIdNotValidUUIDFailure(self): + expected = "connect(): AF_HYPERV address vm_id is not a valid UUID string" with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - with self.assertRaises(ValueError): + with self.assertRaisesRegex(ValueError, re.escape(expected)): s.connect(("00", socket.HV_GUID_ZERO)) def testCreateHyperVSocketAddrServiceIdNotValidUUIDFailure(self): + expected = "connect(): AF_HYPERV address service_id is not a valid UUID string" with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s: - with self.assertRaises(ValueError): + with self.assertRaisesRegex(ValueError, re.escape(expected)): s.connect((socket.HV_GUID_ZERO, "00")) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index de0ca26defc545..c80b2205340808 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1588,24 +1588,20 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) SOCKADDR_HV *a = (SOCKADDR_HV *) addr; wchar_t *guidStr; - PyObject *vmId; - PyObject *serviceId; - if (UuidToStringW(&a->VmId, &guidStr) == RPC_S_OUT_OF_MEMORY) { - PyErr_SetString(PyExc_MemoryError, - "out of memory in makesockaddr"); - return NULL; + return PyErr_NoMemory(); } - vmId = PyUnicode_FromWideChar(guidStr, -1); - RpcStringFreeW(&guidStr); + PyObject *vmId = PyUnicode_FromWideChar(guidStr, -1); + RPC_STATUS res = RpcStringFreeW(&guidStr); + assert(res == RPC_S_OK); if (UuidToStringW(&a->ServiceId, &guidStr) == RPC_S_OUT_OF_MEMORY) { - PyErr_SetString(PyExc_MemoryError, - "out of memory in makesockaddr"); - return NULL; + Py_DECREF(vmId); + return PyErr_NoMemory(); } - serviceId = PyUnicode_FromWideChar(guidStr, -1); - RpcStringFreeW(&guidStr); + PyObject *serviceId = PyUnicode_FromWideChar(guidStr, -1); + res = RpcStringFreeW(&guidStr); + assert(res == RPC_S_OK); return Py_BuildValue("NN", vmId, serviceId); } @@ -2415,7 +2411,6 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, { PyObject *vm_id_obj = NULL; PyObject *service_id_obj = NULL; - wchar_t *guid_str; SOCKADDR_HV *addr = &addrbuf->hv; @@ -2435,20 +2430,21 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, return 0; } - guid_str = PyUnicode_AsWideCharString(vm_id_obj, NULL); + wchar_t *guid_str = PyUnicode_AsWideCharString(vm_id_obj, NULL); if (guid_str == NULL) { PyErr_Format(PyExc_ValueError, "%s(): AF_HYPERV address vm_id is not a valid UUID string", caller); return 0; } - if (UuidFromStringW(guid_str, &addr->VmId) != RPC_S_OK) { + RPC_STATUS rc = UuidFromStringW(guid_str, &addr->VmId); + PyMem_Free(guid_str); + if (rc != RPC_S_OK) { PyErr_Format(PyExc_ValueError, "%s(): AF_HYPERV address vm_id is not a valid UUID string", caller); return 0; } - PyMem_Free(guid_str); guid_str = PyUnicode_AsWideCharString(service_id_obj, NULL); if (guid_str == NULL) { @@ -2457,20 +2453,22 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, caller); return 0; } - if (UuidFromStringW(guid_str, &addr->ServiceId) != RPC_S_OK) { + rc = UuidFromStringW(guid_str, &addr->ServiceId); + PyMem_Free(guid_str); + if (rc != RPC_S_OK) { PyErr_Format(PyExc_ValueError, "%s(): AF_HYPERV address service_id is not a valid UUID string", caller); return 0; } - PyMem_Free(guid_str); *len_ret = sizeof(*addr); return 1; } default: PyErr_Format(PyExc_OSError, - "%s(): unsupported AF_HYPERV protocol", caller); + "%s(): unsupported AF_HYPERV protocol: %d", + caller, s->sock_proto); return 0; } } @@ -7478,7 +7476,7 @@ PyInit__socket(void) PyModule_AddStringConstant(m, "HV_GUID_BROADCAST", "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"); PyModule_AddStringConstant(m, "HV_GUID_CHILDREN", "90DB8B89-0D35-4F79-8CE9-49EA0AC8B7CD"); PyModule_AddStringConstant(m, "HV_GUID_LOOPBACK", "E0E16197-DD56-4A10-9195-5EE7A155A838"); - PyModule_AddStringConstant(m, "HV_GUID_PARENT", "E0E16197-DD56-4A10-9195-5EE7A155A838"); + PyModule_AddStringConstant(m, "HV_GUID_PARENT", "A42E7CDA-D03F-480C-9CC2-A4DE20ABB878"); #endif /* AF_HYPERV */ #ifdef USE_BLUETOOTH From a0b06f3c6b2b95bf73af2e6228be14c312d66e20 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Fri, 20 May 2022 07:37:53 +1000 Subject: [PATCH 5/7] Update Doc/library/socket.rst Co-authored-by: Steve Dower --- Doc/library/socket.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index e4bd339e45dc6e..b52d0d2f646618 100755 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -231,7 +231,7 @@ created. Socket addresses are represented as follows: UUID strings. The ``vm_id`` is the virtual machine identifier or a set of known VMID values - if the target is not a specific virtual machine. Known VMID constants are + if the target is not a specific virtual machine. Known VMID constants defined on ``socket`` are: - ``HV_GUID_ZERO`` From d7af175a53373ddb65e3689ca724f7ac34aabc50 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Tue, 24 May 2022 09:52:28 +1000 Subject: [PATCH 6/7] Add comment for AF_HYPERV def --- Modules/socketmodule.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 96ab86024afe5d..66d9ccf24e3fde 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -77,7 +77,10 @@ struct SOCKADDR_BTH_REDEF { typedef int socklen_t; # endif /* IPPROTO_IPV6 */ -/* Future remove once Py_WINVER has been bumped to >=0x0604 */ +/* Remove ifdef once Py_WINVER >= 0x0604 + * socket.h only defines AF_HYPERV if _WIN32_WINNT is at that level or higher + * so for now it's just manually defined. + */ # ifndef AF_HYPERV # define AF_HYPERV 34 # endif From 025efb066c2a195dedd74b99fd6ffca48626a625 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Tue, 24 May 2022 11:49:21 +1000 Subject: [PATCH 7/7] Better error handling for UUID conversions --- Modules/socketmodule.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c80b2205340808..0bc99011b2c4e2 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1588,16 +1588,20 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) SOCKADDR_HV *a = (SOCKADDR_HV *) addr; wchar_t *guidStr; - if (UuidToStringW(&a->VmId, &guidStr) == RPC_S_OUT_OF_MEMORY) { - return PyErr_NoMemory(); + RPC_STATUS res = UuidToStringW(&a->VmId, &guidStr); + if (res != RPC_S_OK) { + PyErr_SetFromWindowsErr(res); + return 0; } PyObject *vmId = PyUnicode_FromWideChar(guidStr, -1); - RPC_STATUS res = RpcStringFreeW(&guidStr); + res = RpcStringFreeW(&guidStr); assert(res == RPC_S_OK); - if (UuidToStringW(&a->ServiceId, &guidStr) == RPC_S_OUT_OF_MEMORY) { + res = UuidToStringW(&a->ServiceId, &guidStr); + if (res != RPC_S_OK) { Py_DECREF(vmId); - return PyErr_NoMemory(); + PyErr_SetFromWindowsErr(res); + return 0; } PyObject *serviceId = PyUnicode_FromWideChar(guidStr, -1); res = RpcStringFreeW(&guidStr);