RFC: adding fd-passing to win32

Simon McVittie smcv at collabora.com
Thu Aug 4 19:36:09 UTC 2022


On Wed, 03 Aug 2022 at 14:54:21 +0400, Marc-André Lureau wrote:
> The array elements have
> the handle type & value or details for SOCKET. The body 'H' arguments index the
> array, similar to 'h' FDs values.

Is there a reason why the wire protocol can't use the same 'h' type as
on Unix? In the wire protocol, the 'h' type is just a special sort of
integer, with the conventional semantics being that it is an index into an
array of out-of-band file descriptors. Can the Windows equivalent be making
it an index into an array of HANDLEs?

This would work very well for GDBus, which literally represents the 'h'
type as an integer index into the GUnixFdList, but might be a bit weird
in libdbus, which transparently replaces the integer index with the actual
file descriptor (which of course will usually be numerically unequal to
either the index, or the fd in the sender) while iterating a received
message's arguments, or transparently replaces a fd with an index into
the array of fds to be sent while building a new message to be sent.
HANDLEs are not the same size as int, so the ABI for dealing with handles
would be different on Unix and Windows.

I can see why the authors of fd-passing in libdbus did it this way,
but with hindsight, GDBus' approach is probably better.

If the Windows HANDLEs or SOCKETs have to be sent in-band as ordinary
bytes in the message stream, rather than out-of-band as a special
kernel-mediated capability (which seems odd to me, but I'm not a
Windows expert), then they should probably be in a new header field of
an appropriate type (perhaps 'at' if a bare 64-bit integer is enough,
or 'a(t...)' if you need a 64-bit integer plus metadata, or something
along those lines).

Something like this (in GVariant-ish pseudocode):

header = (
    byte endianness,
    byte message type,
    byte flags,
    byte major_version,
    uint32 body_length,
    uint32 serial,
    @a(yv) {
        PATH: <'/foo'>,
        INTERFACE: <'com.example.ArbitraryCodeExecution'>,
        MEMBER: <'ExecCommand'>,
        ...,
        WINDOWS_HANDLES: @a(t...) [                  # a new header field
            (0x12345678, 'this one is stdin', ...),
            (0x87654321, 'this one is stdout', ...),
            (0x12121212, 'this one is stderr', ...),
        ],
    }
)

# 0-7 bytes of padding here, to reach next 8-byte boundary

body = (
    bytestring b'dir c:\',
    handle 0,  # stdin is the 0'th item in WINDOWS_HANDLES, i.e. 0x12345678
    handle 1,  # stdout is the 1st item
    handle 2,  # stderr is the 2nd item
)   # for a total of body_length bytes

so that where Unix would have the number of out-of-band fds in the
existing UNIX_FDS header field, Windows would instead have the in-band
HANDLEs in the WINDOWS_HANDLES header field.

> It seems the D-Bus protocol was designed so that the message size can be
> computed from the header alone, with header + body + fields array size. The
> reference implementation relies on that (_dbus_header_have_message_untrusted
> for ex).

Yes, the framing layer of the wire protocol is intended to make it possible
to know how long the whole message is going to be by reading its first 16
bytes. This should remain true.

    smcv


More information about the dbus mailing list