Add support for relaying unix sockets

This commit is contained in:
Lauri Kasanen 2023-02-16 15:51:26 +02:00
parent 61613c4e65
commit 3bae812364
18 changed files with 333 additions and 0 deletions

View File

@ -88,6 +88,9 @@ namespace rfb {
virtual void clearLocalClipboards() {} virtual void clearLocalClipboards() {}
virtual void receivedUnixRelayData(const char name[], const unsigned char *buf,
const unsigned len) {}
protected: protected:
virtual ~SDesktop() {} virtual ~SDesktop() {}
}; };

View File

@ -98,6 +98,9 @@ namespace rfb {
virtual void udpUpgrade(const char *resp) = 0; virtual void udpUpgrade(const char *resp) = 0;
virtual void udpDowngrade(const bool) = 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; ConnParams cp;
}; };
} }

View File

@ -100,6 +100,12 @@ void SMsgReader::readMsg()
case msgTypeUpgradeToUdp: case msgTypeUpgradeToUdp:
readUpgradeToUdp(); readUpgradeToUdp();
break; break;
case msgTypeSubscribeUnixRelay:
readSubscribeUnixRelay();
break;
case msgTypeUnixRelay:
readUnixRelay();
break;
default: default:
fprintf(stderr, "unknown message type %d\n", msgType); fprintf(stderr, "unknown message type %d\n", msgType);
throw Exception("unknown message type"); throw Exception("unknown message type");
@ -357,3 +363,42 @@ void SMsgReader::readUpgradeToUdp()
handler->udpUpgrade(resp); 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);
}

View File

@ -65,6 +65,9 @@ namespace rfb {
void readUpgradeToUdp(); void readUpgradeToUdp();
void readSubscribeUnixRelay();
void readUnixRelay();
SMsgHandler* handler; SMsgHandler* handler;
rdr::InStream* is; rdr::InStream* is;
}; };

View File

@ -749,3 +749,29 @@ void SMsgWriter::writeUdpUpgrade(const char *resp)
endMsg(); 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();
}

View File

@ -129,6 +129,9 @@ namespace rfb {
void writeUdpUpgrade(const char *resp); 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: protected:
void startMsg(int type); void startMsg(int type);
void endMsg(); void endMsg();

View File

@ -51,6 +51,8 @@ static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
extern rfb::BoolParameter disablebasicauth; extern rfb::BoolParameter disablebasicauth;
extern "C" char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN];
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
bool reverse) bool reverse)
: upgradingToUdp(false), sock(s), reverseConnection(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)); memset(bstats_total, 0, sizeof(bstats_total));
gettimeofday(&connStart, NULL); gettimeofday(&connStart, NULL);
unsigned i;
for (i = 0; i < MAX_UNIX_RELAYS; i++)
unixRelaySubscriptions[i][0] = '\0';
// Check their permissions, if applicable // Check their permissions, if applicable
kasmpasswdpath[0] = '\0'; kasmpasswdpath[0] = '\0';
wordexp_t wexp; wordexp_t wexp;
@ -1780,3 +1786,54 @@ void VNCSConnectionST::udpDowngrade(const bool byServer)
vlog.info("Client %s downgrading from udp by %s", sock->getPeerAddress(), vlog.info("Client %s downgrading from udp by %s", sock->getPeerAddress(),
byServer ? "the server" : "its own request"); 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);
}

View File

@ -33,6 +33,7 @@
#include <rfb/EncodeManager.h> #include <rfb/EncodeManager.h>
#include <rfb/SConnection.h> #include <rfb/SConnection.h>
#include <rfb/Timer.h> #include <rfb/Timer.h>
#include <rfb/unixRelayLimits.h>
namespace rfb { namespace rfb {
class VNCServerST; class VNCServerST;
@ -200,6 +201,18 @@ namespace rfb {
bool upgradingToUdp; 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: private:
// SConnection callbacks // SConnection callbacks
@ -222,6 +235,8 @@ namespace rfb {
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void udpUpgrade(const char *resp); 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 supportsLocalCursor();
virtual void supportsFence(); virtual void supportsFence();
virtual void supportsContinuousUpdates(); virtual void supportsContinuousUpdates();
@ -324,6 +339,8 @@ namespace rfb {
bool frameTracking; bool frameTracking;
uint32_t udpFramesSinceFull; uint32_t udpFramesSinceFull;
char unixRelaySubscriptions[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN];
}; };
} }
#endif #endif

View File

@ -1244,3 +1244,15 @@ void VNCServerST::refreshClients()
(*i)->add_changed_all(); (*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<VNCSConnectionST*>::iterator i;
for (i = clients.begin(); i != clients.end(); i++) {
if ((*i)->isSubscribedToUnixRelay(name)) {
(*i)->sendUnixRelayData(name, buf, len);
}
}
}

View File

@ -198,6 +198,7 @@ namespace rfb {
const char mimes[][32]); const char mimes[][32]);
void refreshClients(); void refreshClients();
void sendUnixRelayData(const char name[], const unsigned char *buf, const unsigned len);
protected: protected:

View File

@ -33,6 +33,8 @@ namespace rfb {
const int msgTypeRequestFrameStats = 179; const int msgTypeRequestFrameStats = 179;
const int msgTypeBinaryClipboard = 180; const int msgTypeBinaryClipboard = 180;
const int msgTypeUpgradeToUdp = 181; const int msgTypeUpgradeToUdp = 181;
const int msgTypeSubscribeUnixRelay = 182;
const int msgTypeUnixRelay = 183;
const int msgTypeServerFence = 248; const int msgTypeServerFence = 248;
@ -54,6 +56,8 @@ namespace rfb {
// same as the other direction // same as the other direction
//const int msgTypeBinaryClipboard = 180; //const int msgTypeBinaryClipboard = 180;
//const int msgTypeUpgradeToUdp = 181; //const int msgTypeUpgradeToUdp = 181;
//const int msgTypeSubscribeUnixRelay = 182;
//const int msgTypeUnixRelay = 183;
const int msgTypeClientFence = 248; const int msgTypeClientFence = 248;

View File

@ -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

View File

@ -29,6 +29,8 @@
#include <pwd.h> #include <pwd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/utsname.h> #include <sys/utsname.h>
@ -51,6 +53,7 @@ extern "C" {
void vncSetGlueContext(int screenIndex); void vncSetGlueContext(int screenIndex);
extern int wakeuppipe[2]; extern int wakeuppipe[2];
extern struct sockaddr_un unixrelayclients[MAX_UNIX_RELAYS];
} }
using namespace rfb; using namespace rfb;
@ -323,6 +326,26 @@ void XserverDesktop::handleSocketEvent(int fd, bool read, bool write)
return; 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)) if (handleListenerEvent(fd, &listeners, server))
return; return;
} }
@ -557,3 +580,21 @@ bool XserverDesktop::handleTimeout(Timer* t)
return false; 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;
}
}

View File

@ -110,6 +110,9 @@ public:
const char* userName, const char* userName,
char** reason); char** reason);
virtual void receivedUnixRelayData(const char name[], const unsigned char *buf,
const unsigned len);
protected: protected:
bool handleListenerEvent(int fd, bool handleListenerEvent(int fd,
std::list<network::SocketListener*>* sockets, std::list<network::SocketListener*>* sockets,
@ -138,5 +141,7 @@ private:
rfb::Point oldCursorPos; rfb::Point oldCursorPos;
bool resizing; bool resizing;
uint8_t unixbuf[1024 * 1024];
}; };
#endif #endif

View File

@ -89,6 +89,11 @@ Use IPv4 for incoming and outgoing connections. Default is on.
Use IPv6 for incoming and outgoing connections. Default is on. Use IPv6 for incoming and outgoing connections. Default is on.
. .
.TP .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 .B \-rfbunixpath \fIpath\fP
Specifies the path of a Unix domain socket on which Xvnc listens for Specifies the path of a Unix domain socket on which Xvnc listens for
connections from viewers, instead of listening on a TCP port. connections from viewers, instead of listening on a TCP port.

View File

@ -237,6 +237,14 @@ void vncExtensionInit(void)
fcntl(wakeuppipe[0], F_SETFL, flags | O_NONBLOCK); fcntl(wakeuppipe[0], F_SETFL, flags | O_NONBLOCK);
vncSetNotifyFd(wakeuppipe[0], 0, true, false); 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; initialised = true;
} }

View File

@ -23,6 +23,8 @@
#include <stddef.h> #include <stddef.h>
#include <sys/select.h> #include <sys/select.h>
#include <rfb/unixRelayLimits.h>
// Only from C++ // Only from C++
#ifdef __cplusplus #ifdef __cplusplus
namespace rfb { class StringParameter; }; namespace rfb { class StringParameter; };
@ -106,6 +108,9 @@ void vncRefreshScreenLayout(int scrIdx);
int vncOverrideParam(const char *nameAndValue); int vncOverrideParam(const char *nameAndValue);
extern int unixrelays[MAX_UNIX_RELAYS];
extern char unixrelaynames[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN];
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -61,8 +61,11 @@ from the X Consortium.
#include "input.h" #include "input.h"
#include "mipointer.h" #include "mipointer.h"
#include "micmap.h" #include "micmap.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#ifndef WIN32 #ifndef WIN32
#include <sys/param.h> #include <sys/param.h>
@ -155,6 +158,73 @@ static char displayNumStr[16];
static int vncVerbose = DEFAULT_LOG_VERBOSITY; 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; char *extra_headers = NULL;
unsigned extra_headers_len = 0; unsigned extra_headers_len = 0;
@ -364,6 +434,7 @@ void ddxUseMsg(void)
ErrorF("-inetd has been launched from inetd\n"); ErrorF("-inetd has been launched from inetd\n");
ErrorF("-http-header name=val append this header to all HTTP responses\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("-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("-verbose [n] verbose startup messages\n");
ErrorF("-quiet minimal startup messages\n"); ErrorF("-quiet minimal startup messages\n");
ErrorF("-version show the server version\n"); ErrorF("-version show the server version\n");
@ -416,6 +487,13 @@ ddxProcessArgument(int argc, char *argv[], int i)
vfbInitializeDefaultScreens(); vfbInitializeDefaultScreens();
vfbInitializePixmapDepths(); vfbInitializePixmapDepths();
unsigned r;
for (r = 0; r < MAX_UNIX_RELAYS; r++) {
unixrelays[r] = -1;
unixrelaynames[r][0] = '\0';
}
firstTime = FALSE; firstTime = FALSE;
vncInitRFB(); vncInitRFB();
} }
@ -668,6 +746,16 @@ ddxProcessArgument(int argc, char *argv[], int i)
return 1; 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 (!strcmp(argv[i], "-verbose")) {
if (++i < argc && argv[i]) { if (++i < argc && argv[i]) {
char *end; char *end;