.. 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
+ 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
+ defined on ``socket`` are:
+
+ - ``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 service identifier of the registered service.
+
+ .. 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
.. availability:: Linux >= 3.9
+.. data:: AF_HYPERV
+ HV_PROTOCOL_RAW
+ 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.
+
+ .. availability:: Windows.
+
+ .. versionadded:: 3.12
+
Functions
^^^^^^^^^
import signal
import math
import pickle
+import re
import struct
import random
import shutil
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()
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
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
+ 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):
+ 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.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.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.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.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.assertRaisesRegex(ValueError, re.escape(expected)):
+ s.connect((socket.HV_GUID_ZERO, "00"))
+
+
class BasicTCPTest(SocketConnectedTest):
def __init__(self, methodName='runTest'):
--- /dev/null
+Add support for connecting and binding to Hyper-V sockets on Windows Hyper-V hosts and guests.
# include <fcntl.h>
# endif
+/* Helpers needed for AF_HYPERV */
+# include <Rpc.h>
+
/* Macros based on the IPPROTO enum, see: https://bugs.python.org/issue29515 */
#ifdef MS_WINDOWS
#define IPPROTO_ICMP IPPROTO_ICMP
}
#endif /* HAVE_SOCKADDR_ALG */
+#ifdef AF_HYPERV
+ case AF_HYPERV:
+ {
+ SOCKADDR_HV *a = (SOCKADDR_HV *) addr;
+
+ wchar_t *guidStr;
+ RPC_STATUS res = UuidToStringW(&a->VmId, &guidStr);
+ if (res != RPC_S_OK) {
+ PyErr_SetFromWindowsErr(res);
+ return 0;
+ }
+ PyObject *vmId = PyUnicode_FromWideChar(guidStr, -1);
+ res = RpcStringFreeW(&guidStr);
+ assert(res == RPC_S_OK);
+
+ res = UuidToStringW(&a->ServiceId, &guidStr);
+ if (res != RPC_S_OK) {
+ Py_DECREF(vmId);
+ PyErr_SetFromWindowsErr(res);
+ return 0;
+ }
+ PyObject *serviceId = PyUnicode_FromWideChar(guidStr, -1);
+ res = RpcStringFreeW(&guidStr);
+ assert(res == RPC_S_OK);
+
+ return Py_BuildValue("NN", vmId, serviceId);
+ }
+#endif /* AF_HYPERV */
+
/* More cases here... */
default:
return 1;
}
#endif /* HAVE_SOCKADDR_ALG */
+#ifdef AF_HYPERV
+ case AF_HYPERV:
+ {
+ switch (s->sock_proto) {
+ case HV_PROTOCOL_RAW:
+ {
+ PyObject *vm_id_obj = NULL;
+ PyObject *service_id_obj = NULL;
+
+ 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,
+ "UU;AF_HYPERV address must be a str tuple (vm_id, service_id)",
+ &vm_id_obj, &service_id_obj))
+ {
+ return 0;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ *len_ret = sizeof(*addr);
+ return 1;
+ }
+ default:
+ PyErr_Format(PyExc_OSError,
+ "%s(): unsupported AF_HYPERV protocol: %d",
+ caller, s->sock_proto);
+ return 0;
+ }
+ }
+#endif /* AF_HYPERV */
/* More cases here... */
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... */
/* 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);
+
+ /* 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", "A42E7CDA-D03F-480C-9CC2-A4DE20ABB878");
+#endif /* AF_HYPERV */
#ifdef USE_BLUETOOTH
PyModule_AddIntMacro(m, AF_BLUETOOTH);
# else
typedef int socklen_t;
# endif /* IPPROTO_IPV6 */
+
+/* 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
+# include <hvsocket.h>
#endif /* MS_WINDOWS */
#ifdef HAVE_SYS_UN_H
#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,
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
- <AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>ws2_32.lib;iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>