10000 gh-92658: Add Hyper-V socket support by jborean93 · Pull Request #92755 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-92658: Add Hyper-V socket support #92755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use strings for addr information
  • Loading branch information
jborean93 committed May 19, 2022
commit 6c0d1583efedc067fa10eff26bcf56daa35e1140
33 changes: 15 additions & 18 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -619,6 +615,7 @@ Constants
.. data:: AF_HYPERV
HV_PROTOCOL_RAW
HVSOCKET_*
HV_GUID_*

Constants for Windows Hyper-V sockets for host/guest communications.

Expand Down
32 changes: 22 additions & 10 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
106 changes: 71 additions & 35 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ shutdown(how) -- shut down traffic in one or both directions\n\
# 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
Expand Down Expand Up @@ -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... */
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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... */

Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions Modules/socketmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <hvsocket.h>
#endif /* MS_WINDOWS */

Expand Down
2 changes: 1 addition & 1 deletion PCbuild/_socket.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>ws2_32.lib;iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
Expand Down
0