From: Oliver Kurth Date: Mon, 3 Jun 2019 20:39:44 +0000 (-0700) Subject: Update to common source code; does not affect open-vm-tools. X-Git-Tag: stable-11.0.0~65 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c209a5619fa74f6b76171420c57fd65303331a0f;p=thirdparty%2Fopen-vm-tools.git Update to common source code; does not affect open-vm-tools. Allow an asyncWebSocket to connect through an existing socket descriptor Because Windows does not allow unprivileged processes to specify DSCP (formerly ToS) values on TCP traffic, in order to support DSCP for Blast WebSockets we must obtain a preconfigured socket descriptor from a privileged process and then build the WebSocket connection over that socket. This changeset extends the asyncWebSocket API by adding a Connect function that can accept and use an existing socket descriptor in addition to the usual collection of WebSocket Connect parameters. Because the asyncWebSocket is built over an asyncTCPSocket, this change ripples down into the asyncTCPSocket API which also gets a new Connect variant with a socket descriptor parameter. To avoid duplicating existing logic, the old Connect variants are modified to do their work by invoking the new API with a distinguished socket descriptor value of -1. This value indicates that no existing socket descriptor is provided and that a new socket should be created and used for the connection. In this changeset, passing in an existing socket is supported only on the Windows platform. The feature is not required on other platforms, where unprivileged processes are permitted to define DSCP values for their connections. Attempting to create a connection over an existing socket on other platforms will result in a Connect failure. --- diff --git a/open-vm-tools/lib/asyncsocket/asyncsocket.c b/open-vm-tools/lib/asyncsocket/asyncsocket.c index 49524396a..102638cc9 100644 --- a/open-vm-tools/lib/asyncsocket/asyncsocket.c +++ b/open-vm-tools/lib/asyncsocket/asyncsocket.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2003-2018 VMware, Inc. All rights reserved. + * Copyright (C) 2003-2019 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -266,6 +266,7 @@ static unsigned int AsyncTCPSocketGetPortFromAddr( struct sockaddr_storage *addr); static AsyncTCPSocket *AsyncTCPSocketConnect(struct sockaddr_storage *addr, socklen_t addrLen, + int socketFd, AsyncSocketConnectFn connectFn, void *clientData, AsyncSocketConnectFlags flags, @@ -1657,6 +1658,7 @@ static AsyncTCPSocket * AsyncTCPSocketConnectImpl(int socketFamily, // IN const char *hostname, // IN unsigned int port, // IN + int tcpSocketFd, // IN AsyncSocketConnectFn connectFn, // IN void *clientData, // IN AsyncSocketConnectFlags flags, // IN @@ -1688,7 +1690,8 @@ AsyncTCPSocketConnectImpl(int socketFamily, // IN socketFamily == AF_INET ? "IPv4" : "IPv6", ipString, hostname); free(ipString); - asock = AsyncTCPSocketConnect(&addr, addrLen, connectFn, clientData, + asock = AsyncTCPSocketConnect(&addr, addrLen, tcpSocketFd, + connectFn, clientData, flags, pollParams, &error); if (!asock) { Warning(ASOCKPREFIX "%s connection attempt failed: %s\n", @@ -1736,6 +1739,49 @@ AsyncSocket_Connect(const char *hostname, // IN AsyncSocketConnectFlags flags, // IN AsyncSocketPollParams *pollParams, // IN int *outError) // OUT: optional +{ + return AsyncSocket_ConnectWithFd(hostname, + port, + -1, + connectFn, + clientData, + flags, + pollParams, + outError); +} + + +/* + *---------------------------------------------------------------------------- + * + * AsyncSocket_ConnectWithFd -- + * + * AsyncTCPSocket connect using an existing socket descriptor. + * Connection is attempted with AF_INET socket + * family, when that fails AF_INET6 is attempted. + * + * Limitation: The ConnectWithFd functionality is currently Windows only. + * Non-Windows platforms & windows-UWP are not supported. + * + * Results: + * AsyncTCPSocket * on success and NULL on failure. + * On failure, error is returned in *outError. + * + * Side effects: + * Allocates an AsyncTCPSocket, registers a poll callback. + * + *---------------------------------------------------------------------------- + */ + +AsyncSocket * +AsyncSocket_ConnectWithFd(const char *hostname, // IN + unsigned int port, // IN + int tcpSocketFd, // IN + AsyncSocketConnectFn connectFn, // IN + void *clientData, // IN + AsyncSocketConnectFlags flags, // IN + AsyncSocketPollParams *pollParams, // IN + int *outError) // OUT: optional { int error = ASOCKERR_CONNECT; AsyncTCPSocket *asock = NULL; @@ -1746,11 +1792,13 @@ AsyncSocket_Connect(const char *hostname, // IN goto error; } - asock = AsyncTCPSocketConnectImpl(AF_INET, hostname, port, connectFn, - clientData, flags, pollParams, &error); + asock = AsyncTCPSocketConnectImpl(AF_INET, hostname, port, + tcpSocketFd, connectFn, clientData, + flags, pollParams, &error); if (!asock) { - asock = AsyncTCPSocketConnectImpl(AF_INET6, hostname, port, connectFn, - clientData, flags, pollParams, &error); + asock = AsyncTCPSocketConnectImpl(AF_INET6, hostname, port, + tcpSocketFd, connectFn, clientData, + flags, pollParams, &error); } error: @@ -1801,7 +1849,7 @@ AsyncSocket_ConnectVMCI(unsigned int cid, // IN Log(ASOCKPREFIX "creating new socket, connecting to %u:%u\n", cid, port); asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr, - sizeof addr, connectFn, clientData, + sizeof addr, -1, connectFn, clientData, flags, pollParams, outError); VMCISock_ReleaseAFValueFd(vsockDev); @@ -1851,7 +1899,7 @@ AsyncSocket_ConnectUnixDomain(const char *path, // IN Log(ASOCKPREFIX "creating new socket, connecting to %s\n", path); asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr, - sizeof addr, connectFn, clientData, + sizeof addr, -1, connectFn, clientData, flags, pollParams, outError); return BaseSocket(asock); @@ -1920,6 +1968,79 @@ AsyncTCPSocketConnectErrorCheck(void *data) // IN: AsyncTCPSocket * } +/* + *---------------------------------------------------------------------------- + * + * SocketProtocolAndTypeMatches -- + * + * Discover whether a given socket has the specified protocol family + * (PF_INET, PF_INET6, ...) and data transfer type (SOCK_STREAM, + * SOCK_DGRAM, ...). + * + * For now, this is supported only on non-UWP Windows platforms. + * Other platforms always receive a FALSE result. + * + * Results: + * True if the socket has the specified family and type, false + * otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static Bool +SocketProtocolAndTypeMatches(int socketFd, // IN + int protocol, // IN + int type) // IN +{ +#if defined( _WIN32) && !defined(VM_WIN_UWP) + int ret; + WSAPROTOCOL_INFO protocolInfo; + int protocolInfoLen = sizeof protocolInfo; + + ret = getsockopt(socketFd, SOL_SOCKET, SO_PROTOCOL_INFO, + (void*)&protocolInfo, &protocolInfoLen); + if (ret != 0) { + Warning(ASOCKPREFIX "SO_PROTOCOL_INFO failed on sockFd %d, ", + "error 0x%x\n", + socketFd, ASOCK_LASTERROR()); + return FALSE; + } + + /* + * Windows is confused about protocol families (the "domain" of the + * socket, passed as the first argument to the socket() call) and + * address families (specified in the xx_family member of a sockaddr_xx + * argument passed to bind()). The protocol family of the socket is + * reported in the iAddressFamily of the WSAPROTOCOL_INFO structure. + */ + return ((protocol == protocolInfo.iAddressFamily) && + (type == protocolInfo.iSocketType)); +#else + + /* + * If we need to implement this for other platforms then we can use + * getsockopt(SO_TYPE) to retrieve the socket type, and on Linux we can + * use getsockopt(SO_DOMAIN) to retrieve the protocol family, but other + * platforms might not have SO_DOMAIN. On those platforms we might be + * able to infer the protocol family by attempting sockopt calls that + * only work on certain families. + * + * BTW, Linux has thrown in the towel on the distinction between + * protocol families and address families. Its socket() man page shows + * AF_* literals being used for the 'domain' argument instead of PF_* + * literals. This works because AF_XX is defined to have the same + * numeric value as PF_XX for all values of XX. + */ + Warning(ASOCKPREFIX "discovery of socket protocol and type is " + "not implemented on this platform\n"); + NOT_IMPLEMENTED(); +#endif // defined(_WIN32) +} + + /* *---------------------------------------------------------------------------- * @@ -1939,6 +2060,7 @@ AsyncTCPSocketConnectErrorCheck(void *data) // IN: AsyncTCPSocket * static AsyncTCPSocket * AsyncTCPSocketConnect(struct sockaddr_storage *addr, // IN socklen_t addrLen, // IN + int socketFd, // IN: optional AsyncSocketConnectFn connectFn, // IN void *clientData, // IN AsyncSocketConnectFlags flags, // IN @@ -1960,9 +2082,33 @@ AsyncTCPSocketConnect(struct sockaddr_storage *addr, // IN } /* - * Create a new IP socket + * If we were given a socket, verify that it is of the required + * protocol family and type before using it. If no socket was given, + * create a new socket of the appropriate family. (For the sockets + * we care about, the required protocol family is numerically the + * same as the address family provided in the given destination + * sockaddr, so we can use addr->ss_family whenever we need to + * specify a protocol family.) + * + * For now, passing in a socket is supported only on non-UWP Windows + * platforms. The SocketProtocolAndTypeMatches() call will fail on + * other platforms. */ - if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == -1) { + if (-1 != socketFd) { + int protocolFamily = addr->ss_family; + // XXX Logging here is excessive, remove after testing + if (SocketProtocolAndTypeMatches(socketFd, protocolFamily, + SOCK_STREAM)) { + Warning(ASOCKPREFIX "using passed-in socket, family %d\n", + protocolFamily); + fd = socketFd; + } else { + Warning(ASOCKPREFIX "rejecting passed-in socket, wanted family %d\n", + protocolFamily); + error = ASOCKERR_INVAL; + goto error; + } + } else if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == -1) { sysErr = ASOCK_LASTERROR(); Warning(ASOCKPREFIX "failed to create socket, error %d: %s\n", sysErr, Err_Errno2String(sysErr)); diff --git a/open-vm-tools/lib/include/asyncsocket.h b/open-vm-tools/lib/include/asyncsocket.h index fe066045d..95a5e4643 100644 --- a/open-vm-tools/lib/include/asyncsocket.h +++ b/open-vm-tools/lib/include/asyncsocket.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2003-2018 VMware, Inc. All rights reserved. + * Copyright (C) 2003-2019 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -492,6 +492,14 @@ AsyncSocket *AsyncSocket_Connect(const char *hostname, AsyncSocketConnectFlags flags, AsyncSocketPollParams *pollParams, int *error); +AsyncSocket *AsyncSocket_ConnectWithFd(const char *hostname, + unsigned int port, + int tcpSocketFd, + AsyncSocketConnectFn connectFn, + void *clientData, + AsyncSocketConnectFlags flags, + AsyncSocketPollParams *pollParams, + int *error); AsyncSocket *AsyncSocket_ConnectVMCI(unsigned int cid, unsigned int port, AsyncSocketConnectFn connectFn, void *clientData,