From 3bae812364984d69a0020c60234c979f11da6667 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Thu, 16 Feb 2023 15:51:26 +0200 Subject: [PATCH] Add support for relaying unix sockets --- common/rfb/SDesktop.h | 3 + common/rfb/SMsgHandler.h | 3 + common/rfb/SMsgReader.cxx | 45 ++++++++++++++ common/rfb/SMsgReader.h | 3 + common/rfb/SMsgWriter.cxx | 26 ++++++++ common/rfb/SMsgWriter.h | 3 + common/rfb/VNCSConnectionST.cxx | 57 +++++++++++++++++ common/rfb/VNCSConnectionST.h | 17 ++++++ common/rfb/VNCServerST.cxx | 12 ++++ common/rfb/VNCServerST.h | 1 + common/rfb/msgTypes.h | 4 ++ common/rfb/unixRelayLimits.h | 7 +++ unix/xserver/hw/vnc/XserverDesktop.cc | 41 +++++++++++++ unix/xserver/hw/vnc/XserverDesktop.h | 5 ++ unix/xserver/hw/vnc/Xvnc.man | 5 ++ unix/xserver/hw/vnc/vncExtInit.cc | 8 +++ unix/xserver/hw/vnc/vncExtInit.h | 5 ++ unix/xserver/hw/vnc/xvnc.c | 88 +++++++++++++++++++++++++++ 18 files changed, 333 insertions(+) create mode 100644 common/rfb/unixRelayLimits.h diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index 7afc14c..c6e267c 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -88,6 +88,9 @@ namespace rfb { virtual void clearLocalClipboards() {} + virtual void receivedUnixRelayData(const char name[], const unsigned char *buf, + const unsigned len) {} + protected: virtual ~SDesktop() {} }; diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index 7cf8b71..8856b9f 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -98,6 +98,9 @@ namespace rfb { virtual void udpUpgrade(const char *resp) = 0; virtual void udpDowngrade(const bool) = 0; + virtual void subscribeUnixRelay(const char *name) = 0; + virtual void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len) = 0; + ConnParams cp; }; } diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index c615dc2..d5b8c6f 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -100,6 +100,12 @@ void SMsgReader::readMsg() case msgTypeUpgradeToUdp: readUpgradeToUdp(); break; + case msgTypeSubscribeUnixRelay: + readSubscribeUnixRelay(); + break; + case msgTypeUnixRelay: + readUnixRelay(); + break; default: fprintf(stderr, "unknown message type %d\n", msgType); throw Exception("unknown message type"); @@ -357,3 +363,42 @@ void SMsgReader::readUpgradeToUdp() handler->udpUpgrade(resp); } + +void SMsgReader::readSubscribeUnixRelay() +{ + const rdr::U8 namelen = is->readU8(); + char name[64]; + if (namelen >= sizeof(name)) { + vlog.error("Ignoring subscribe with too large name"); + is->skip(namelen); + return; + } + is->readBytes(name, namelen); + name[namelen] = '\0'; + + handler->subscribeUnixRelay(name); +} + +void SMsgReader::readUnixRelay() +{ + const rdr::U8 namelen = is->readU8(); + char name[64]; + if (namelen >= sizeof(name)) { + vlog.error("Ignoring relay packet with too large name"); + is->skip(namelen); + return; + } + is->readBytes(name, namelen); + name[namelen] = '\0'; + + const rdr::U32 len = is->readU32(); + rdr::U8 buf[1024 * 1024]; + if (len >= sizeof(buf)) { + vlog.error("Ignoring relay packet with too large data"); + is->skip(len); + return ; + } + is->readBytes(buf, len); + + handler->unixRelay(name, buf, len); +} diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h index 7e9f5b0..60d49c0 100644 --- a/common/rfb/SMsgReader.h +++ b/common/rfb/SMsgReader.h @@ -65,6 +65,9 @@ namespace rfb { void readUpgradeToUdp(); + void readSubscribeUnixRelay(); + void readUnixRelay(); + SMsgHandler* handler; rdr::InStream* is; }; diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index ea77081..aba2cdd 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -749,3 +749,29 @@ void SMsgWriter::writeUdpUpgrade(const char *resp) endMsg(); } + +void SMsgWriter::writeSubscribeUnixRelay(const bool success, const char *msg) +{ + startMsg(msgTypeSubscribeUnixRelay); + + const rdr::U8 len = strlen(msg); + os->writeU8(success); + os->writeU8(len); + os->writeBytes(msg, len); + + endMsg(); +} + +void SMsgWriter::writeUnixRelay(const char *name, const rdr::U8 *buf, const unsigned len) +{ + startMsg(msgTypeUnixRelay); + + const rdr::U8 namelen = strlen(name); + os->writeU8(namelen); + os->writeBytes(name, namelen); + + os->writeU32(len); + os->writeBytes(buf, len); + + endMsg(); +} diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 71b6fcd..f16b03b 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -129,6 +129,9 @@ namespace rfb { void writeUdpUpgrade(const char *resp); + void writeSubscribeUnixRelay(const bool success, const char *msg); + void writeUnixRelay(const char *name, const rdr::U8 *buf, const unsigned len); + protected: void startMsg(int type); void endMsg(); diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 5629ed6..49a039a 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -51,6 +51,8 @@ static Cursor emptyCursor(0, 0, Point(0, 0), NULL); extern rfb::BoolParameter disablebasicauth; +extern "C" char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN]; + VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, bool reverse) : upgradingToUdp(false), sock(s), reverseConnection(reverse), @@ -73,6 +75,10 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, memset(bstats_total, 0, sizeof(bstats_total)); gettimeofday(&connStart, NULL); + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) + unixRelaySubscriptions[i][0] = '\0'; + // Check their permissions, if applicable kasmpasswdpath[0] = '\0'; wordexp_t wexp; @@ -1780,3 +1786,54 @@ void VNCSConnectionST::udpDowngrade(const bool byServer) vlog.info("Client %s downgrading from udp by %s", sock->getPeerAddress(), byServer ? "the server" : "its own request"); } + +void VNCSConnectionST::subscribeUnixRelay(const char *name) +{ + bool read, write, owner; + if (!getPerms(read, write, owner) || !write) { + // Need write permissions to subscribe + writer()->writeSubscribeUnixRelay(false, "No permissions"); + vlog.info("Client tried to subscribe to unix channel %s without permissions", name); + return; + } + + unsigned i; + bool found = false; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (!strcmp(name, unixrelaynames[i])) { + found = true; + break; + } + } + + if (!found) { + writer()->writeSubscribeUnixRelay(false, "No such unix channel"); + vlog.info("Client tried to subscribe to nonexistent unix channel %s", name); + return; + } + + writer()->writeSubscribeUnixRelay(true, "Ok"); + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (!unixRelaySubscriptions[i][0]) { + strcpy(unixRelaySubscriptions[i], name); + break; + } + } +} + +void VNCSConnectionST::unixRelay(const char *name, const rdr::U8 *buf, const unsigned len) +{ + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (!strcmp(unixRelaySubscriptions[i], name)) { + server->desktop->receivedUnixRelayData(name, buf, len); + return; + } + } +} + +void VNCSConnectionST::sendUnixRelayData(const char name[], const unsigned char *buf, + const unsigned len) +{ + writer()->writeUnixRelay(name, buf, len); +} diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 51826b4..55c20cd 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace rfb { class VNCServerST; @@ -200,6 +201,18 @@ namespace rfb { bool upgradingToUdp; + bool isSubscribedToUnixRelay(const char *name) const { + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (!strcmp(unixRelaySubscriptions[i], name)) + return true; + } + return false; + } + + virtual void sendUnixRelayData(const char name[], const unsigned char *buf, + const unsigned len); + private: // SConnection callbacks @@ -222,6 +235,8 @@ namespace rfb { virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); virtual void udpUpgrade(const char *resp); + virtual void subscribeUnixRelay(const char *name); + virtual void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len); virtual void supportsLocalCursor(); virtual void supportsFence(); virtual void supportsContinuousUpdates(); @@ -324,6 +339,8 @@ namespace rfb { bool frameTracking; uint32_t udpFramesSinceFull; + + char unixRelaySubscriptions[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN]; }; } #endif diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 7abd972..198b695 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -1244,3 +1244,15 @@ void VNCServerST::refreshClients() (*i)->add_changed_all(); } } + +void VNCServerST::sendUnixRelayData(const char name[], + const unsigned char *buf, const unsigned len) +{ + // For each client subscribed to this channel, send the data to them + std::list::iterator i; + for (i = clients.begin(); i != clients.end(); i++) { + if ((*i)->isSubscribedToUnixRelay(name)) { + (*i)->sendUnixRelayData(name, buf, len); + } + } +} diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index bdd43b0..805a07f 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -198,6 +198,7 @@ namespace rfb { const char mimes[][32]); void refreshClients(); + void sendUnixRelayData(const char name[], const unsigned char *buf, const unsigned len); protected: diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h index 11805e3..836e4d8 100644 --- a/common/rfb/msgTypes.h +++ b/common/rfb/msgTypes.h @@ -33,6 +33,8 @@ namespace rfb { const int msgTypeRequestFrameStats = 179; const int msgTypeBinaryClipboard = 180; const int msgTypeUpgradeToUdp = 181; + const int msgTypeSubscribeUnixRelay = 182; + const int msgTypeUnixRelay = 183; const int msgTypeServerFence = 248; @@ -54,6 +56,8 @@ namespace rfb { // same as the other direction //const int msgTypeBinaryClipboard = 180; //const int msgTypeUpgradeToUdp = 181; + //const int msgTypeSubscribeUnixRelay = 182; + //const int msgTypeUnixRelay = 183; const int msgTypeClientFence = 248; diff --git a/common/rfb/unixRelayLimits.h b/common/rfb/unixRelayLimits.h new file mode 100644 index 0000000..f692808 --- /dev/null +++ b/common/rfb/unixRelayLimits.h @@ -0,0 +1,7 @@ +#ifndef UNIX_RELAY_LIMITS_H +#define UNIX_RELAY_LIMITS_H + +#define MAX_UNIX_RELAYS 4 +#define MAX_UNIX_RELAY_NAME_LEN 64 + +#endif diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 605e228..031dcd4 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -51,6 +53,7 @@ extern "C" { void vncSetGlueContext(int screenIndex); extern int wakeuppipe[2]; +extern struct sockaddr_un unixrelayclients[MAX_UNIX_RELAYS]; } using namespace rfb; @@ -323,6 +326,26 @@ void XserverDesktop::handleSocketEvent(int fd, bool read, bool write) return; } + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (unixrelays[i] == -1) + break; + if (fd == unixrelays[i]) { + do { + struct sockaddr_un client; + socklen_t addrlen = sizeof(struct sockaddr_un); + const ssize_t len = recvfrom(unixrelays[i], unixbuf, sizeof(unixbuf), + MSG_DONTWAIT, + (struct sockaddr *) &client, &addrlen); + if (len <= 0) + break; + memcpy(&unixrelayclients[i], &client, addrlen); + server->sendUnixRelayData(unixrelaynames[i], unixbuf, len); + } while (1); + return; + } + } + if (handleListenerEvent(fd, &listeners, server)) return; } @@ -557,3 +580,21 @@ bool XserverDesktop::handleTimeout(Timer* t) return false; } + +void XserverDesktop::receivedUnixRelayData(const char name[], const unsigned char *buf, + const unsigned len) +{ + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (unixrelays[i] == -1) + break; + if (strcmp(name, unixrelaynames[i])) + continue; + + if (sendto(unixrelays[i], buf, len, 0, + (struct sockaddr *) &unixrelayclients[i], sizeof(struct sockaddr_un)) == -1) + vlog.error("Error writing unix relay data to %s", name); + + break; + } +} diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index de9baa7..03e221d 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -110,6 +110,9 @@ public: const char* userName, char** reason); + virtual void receivedUnixRelayData(const char name[], const unsigned char *buf, + const unsigned len); + protected: bool handleListenerEvent(int fd, std::list* sockets, @@ -138,5 +141,7 @@ private: rfb::Point oldCursorPos; bool resizing; + + uint8_t unixbuf[1024 * 1024]; }; #endif diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index 16ba11d..798481a 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -89,6 +89,11 @@ Use IPv4 for incoming and outgoing connections. Default is on. Use IPv6 for incoming and outgoing connections. Default is on. . .TP +.B \-UnixRelay \fIname:path\fP +Create a local named unix socket, for relaying data. May be given multiple times. +Example: -UnixRelay audio:/tmp/audiosock +. +.TP .B \-rfbunixpath \fIpath\fP Specifies the path of a Unix domain socket on which Xvnc listens for connections from viewers, instead of listening on a TCP port. diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 38cbf87..b676d32 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -237,6 +237,14 @@ void vncExtensionInit(void) fcntl(wakeuppipe[0], F_SETFL, flags | O_NONBLOCK); vncSetNotifyFd(wakeuppipe[0], 0, true, false); + unsigned i; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (unixrelays[i] == -1) + break; + vncSetNotifyFd(unixrelays[i], 0, true, false); + vlog.info("Listening to unix relay socket %s", unixrelaynames[i]); + } + initialised = true; } diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 0b4abfd..3dfb44a 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -23,6 +23,8 @@ #include #include +#include + // Only from C++ #ifdef __cplusplus namespace rfb { class StringParameter; }; @@ -106,6 +108,9 @@ void vncRefreshScreenLayout(int scrIdx); int vncOverrideParam(const char *nameAndValue); +extern int unixrelays[MAX_UNIX_RELAYS]; +extern char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN]; + #ifdef __cplusplus } #endif diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c index 5f293f9..473ef81 100644 --- a/unix/xserver/hw/vnc/xvnc.c +++ b/unix/xserver/hw/vnc/xvnc.c @@ -61,8 +61,11 @@ from the X Consortium. #include "input.h" #include "mipointer.h" #include "micmap.h" +#include +#include #include #include +#include #include #ifndef WIN32 #include @@ -155,6 +158,73 @@ static char displayNumStr[16]; static int vncVerbose = DEFAULT_LOG_VERBOSITY; +int unixrelays[MAX_UNIX_RELAYS]; +char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN]; +struct sockaddr_un unixrelayclients[MAX_UNIX_RELAYS]; + +static unsigned addrelay(const char * const arg) +{ + const char *ptr = strchr(arg, ':'); + if (!ptr) { + ErrorF("Invalid unixrelay\n"); + return 1; + } + + const unsigned namelen = ptr - arg; + if (namelen >= MAX_UNIX_RELAY_NAME_LEN) { + ErrorF("Unix relay name too long\n"); + return 1; + } + + unsigned i; + unsigned char found = 0; + for (i = 0; i < MAX_UNIX_RELAYS; i++) { + if (unixrelays[i] == -1) { + found = 1; + break; + } + } + if (!found) { + ErrorF("Too many unix relays\n"); + return 1; + } + + memcpy(unixrelaynames[i], arg, namelen); + unixrelaynames[i][namelen] = '\0'; + + unixrelays[i] = socket(AF_UNIX, SOCK_DGRAM, 0); + if (unixrelays[i] < 0) { + ErrorF("Failed to create unix sock\n"); + return 1; + } + + ptr++; + struct sockaddr_un sa; + if (strlen(ptr) >= sizeof(sa.sun_path)) { + ErrorF("Unix relay path too long\n"); + return 1; + } + + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, ptr); + + // SO_REUSEADDR doesn't exist for unix sockets, if the socket exists + // (from our previous run), we need to delete it first. Check it's a + // socket so we don't delete wrong files + struct stat st; + if (stat(ptr, &st) == 0) { + if (S_ISSOCK(st.st_mode)) + unlink(ptr); + } + + if (bind(unixrelays[i], (struct sockaddr *) &sa, sizeof(struct sockaddr_un))) { + ErrorF("Failed to bind unix sock\n"); + return 1; + } + + return 0; +} + char *extra_headers = NULL; unsigned extra_headers_len = 0; @@ -364,6 +434,7 @@ void ddxUseMsg(void) ErrorF("-inetd has been launched from inetd\n"); ErrorF("-http-header name=val append this header to all HTTP responses\n"); ErrorF("-noclipboard disable clipboard settings modification via vncconfig utility\n"); + ErrorF("-unixrelay name:path create a local named unix relay socket\n"); ErrorF("-verbose [n] verbose startup messages\n"); ErrorF("-quiet minimal startup messages\n"); ErrorF("-version show the server version\n"); @@ -416,6 +487,13 @@ ddxProcessArgument(int argc, char *argv[], int i) vfbInitializeDefaultScreens(); vfbInitializePixmapDepths(); + + unsigned r; + for (r = 0; r < MAX_UNIX_RELAYS; r++) { + unixrelays[r] = -1; + unixrelaynames[r][0] = '\0'; + } + firstTime = FALSE; vncInitRFB(); } @@ -668,6 +746,16 @@ ddxProcessArgument(int argc, char *argv[], int i) return 1; } + if (strcasecmp(argv[i], "-unixrelay") == 0) + { + fail_unless_args(argc, i, 1); + ++i; + + if (addrelay(argv[i])) + return 0; + return 2; + } + if (!strcmp(argv[i], "-verbose")) { if (++i < argc && argv[i]) { char *end;