Browse Source

Add support for Unix sockets

Patch originally by Dag-Erling Smørgrav for University of Oslo.
tags/v1.8.90
Pierre Ossman 6 years ago
parent
commit
5d05546fe4

+ 7
- 1
common/network/CMakeLists.txt View File

@@ -1,8 +1,14 @@
include_directories(${CMAKE_SOURCE_DIR}/common)

add_library(network STATIC
set(NETWORK_SOURCES
TcpSocket.cxx)

if(NOT WIN32)
set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx)
endif()

add_library(network STATIC ${NETWORK_SOURCES})

if(WIN32)
target_link_libraries(network ws2_32)
endif()

+ 250
- 0
common/network/UnixSocket.cxx View File

@@ -0,0 +1,250 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (c) 2012 University of Oslo. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stddef.h>

#include <network/UnixSocket.h>
#include <rfb/LogWriter.h>

using namespace network;
using namespace rdr;

static rfb::LogWriter vlog("UnixSocket");

// -=- Socket initialisation
static bool socketsInitialised = false;
static void initSockets() {
if (socketsInitialised)
return;
signal(SIGPIPE, SIG_IGN);
socketsInitialised = true;
}


// -=- UnixSocket

UnixSocket::UnixSocket(int sock, bool close)
: Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
{
}

UnixSocket::UnixSocket(const char *path)
: closeFd(true)
{
int sock, err, result;
sockaddr_un addr;
socklen_t salen;

if (strlen(path) >= sizeof(addr.sun_path))
throw SocketException("socket path is too long", ENAMETOOLONG);

// - Create a socket
initSockets();
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
throw SocketException("unable to create socket", errno);

// - Attempt to connect
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, path);
salen = sizeof(addr);
while ((result = connect(sock, (sockaddr *)&addr, salen)) == -1) {
err = errno;
close(sock);
break;
}

if (result == -1)
throw SocketException("unable connect to socket", err);

// - By default, close the socket on exec()
fcntl(sock, F_SETFD, FD_CLOEXEC);

// Create the input and output streams
instream = new FdInStream(sock);
outstream = new FdOutStream(sock);
ownStreams = true;
}

UnixSocket::~UnixSocket() {
if (closeFd)
close(getFd());
}

int UnixSocket::getMyPort() {
return 0;
}

char* UnixSocket::getPeerAddress() {
struct sockaddr_un addr;
socklen_t salen;

// AF_UNIX only has a single address (the server side).
// Unfortunately we don't know which end we are, so we'll have to
// test a bit.

salen = sizeof(addr);
if (getpeername(getFd(), (struct sockaddr *)&addr, &salen) != 0) {
vlog.error("unable to get peer name for socket");
return rfb::strDup("");
}

if (salen > offsetof(struct sockaddr_un, sun_path))
return rfb::strDup(addr.sun_path);

salen = sizeof(addr);
if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) != 0) {
vlog.error("unable to get local name for socket");
return rfb::strDup("");
}

if (salen > offsetof(struct sockaddr_un, sun_path))
return rfb::strDup(addr.sun_path);

// socketpair() will create unnamed sockets

return rfb::strDup("(unnamed UNIX socket)");
}

int UnixSocket::getPeerPort() {
return 0;
}

char* UnixSocket::getPeerEndpoint() {
return getPeerAddress();
}

bool UnixSocket::sameMachine() {
return true;
}

void UnixSocket::shutdown()
{
Socket::shutdown();
::shutdown(getFd(), 2);
}

bool UnixSocket::cork(bool enable)
{
return true;
}

UnixListener::UnixListener(const char *path, int mode)
{
struct sockaddr_un addr;
mode_t saved_umask;
int err, result;

if (strlen(path) >= sizeof(addr.sun_path))
throw SocketException("socket path is too long", ENAMETOOLONG);

// - Create a socket
initSockets();
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
throw SocketException("unable to create listening socket", errno);

// - By default, close the socket on exec()
fcntl(fd, F_SETFD, FD_CLOEXEC);

// - Delete existing socket (ignore result)
unlink(path);

// - Attempt to bind to the requested path
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, path);
saved_umask = umask(0777);
result = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
err = errno;
umask(saved_umask);
if (result < 0) {
close(fd);
throw SocketException("unable to bind listening socket", err);
}

// - Set socket mode
if (chmod(path, mode) < 0) {
err = errno;
close(fd);
throw SocketException("unable to set socket mode", err);
}

// - Set it to be a listening socket
if (listen(fd, 5) < 0) {
err = errno;
close(fd);
throw SocketException("unable to set socket to listening mode", err);
}
}

UnixListener::UnixListener(int sock)
{
fd = sock;
}

UnixListener::~UnixListener()
{
struct sockaddr_un addr;
socklen_t salen = sizeof(addr);

close(fd);

if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) == 0)
unlink(addr.sun_path);
}

void UnixListener::shutdown()
{
::shutdown(getFd(), 2);
}


Socket*
UnixListener::accept() {
int new_sock = -1;

// Accept an incoming connection
if ((new_sock = ::accept(fd, 0, 0)) < 0)
throw SocketException("unable to accept new connection", errno);

// - By default, close the socket on exec()
fcntl(new_sock, F_SETFD, FD_CLOEXEC);

// - Create the socket object
return new UnixSocket(new_sock);
}

int UnixListener::getMyPort() {
return 0;
}

+ 70
- 0
common/network/UnixSocket.h View File

@@ -0,0 +1,70 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (c) 2012 University of Oslo. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

// -=- UnixSocket.h - base-class for UNIX stream sockets.
// This header also defines the UnixListener class, used
// to listen for incoming socket connections over UNIX
//
// NB: Any file descriptors created by the UnixSocket or
// UnixListener classes are close-on-exec if the OS supports
// it. UnixSockets initialised with a caller-supplied fd
// are NOT set to close-on-exec.

#ifndef __NETWORK_UNIX_SOCKET_H__
#define __NETWORK_UNIX_SOCKET_H__

#include <network/Socket.h>

namespace network {

class UnixSocket : public Socket {
public:
UnixSocket(int sock, bool close=true);
UnixSocket(const char *name);
virtual ~UnixSocket();

virtual int getMyPort();

virtual char* getPeerAddress();
virtual int getPeerPort();
virtual char* getPeerEndpoint();
virtual bool sameMachine();

virtual void shutdown();
virtual bool cork(bool enable);

private:
bool closeFd;
};

class UnixListener : public SocketListener {
public:
UnixListener(const char *listenaddr, int mode);
UnixListener(int sock);
virtual ~UnixListener();

virtual void shutdown();
virtual Socket* accept();

int getMyPort();
};

}

#endif // __NETWORK_UNIX_SOCKET_H__

+ 9
- 0
unix/xserver/hw/vnc/Xvnc.man View File

@@ -91,6 +91,15 @@ Use IPv4 for incoming and outgoing connections. Default is on.
Use IPv6 for incoming and outgoing connections. Default is on.
.
.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.
.
.TP
.B \-rfbunixmode \fImode\fP
Specifies the mode of the Unix domain socket. The default is 0600.
.
.TP
.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP
Time in milliseconds to wait for a viewer which is blocking the server. This is
necessary because the server is single-threaded and sometimes blocks until the

+ 18
- 0
unix/xserver/hw/vnc/vncExtInit.cc View File

@@ -34,6 +34,7 @@
#include <rfb/Region.h>
#include <rfb/ledStates.h>
#include <network/TcpSocket.h>
#include <network/UnixSocket.h>

#include "XserverDesktop.h"
#include "vncExtInit.h"
@@ -79,6 +80,8 @@ rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0);
rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
&rfb::Server::clientWaitTimeMillis);
rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
rfb::StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
rfb::IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
rfb::BoolParameter localhostOnly("localhost",
"Only allow connections from localhost",
@@ -181,6 +184,21 @@ void vncExtensionInit(void)
listeners.push_back(new network::TcpListener(vncInetdSock));
vlog.info("inetd wait");
}
} else if (rfbunixpath.getValueStr()[0] != '\0') {
char path[PATH_MAX];
int mode = (int)rfbunixmode;

if (scr == 0)
strncpy(path, rfbunixpath, sizeof(path));
else
snprintf(path, sizeof(path), "%s.%d",
rfbunixpath.getValueStr(), scr);
path[sizeof(path)-1] = '\0';

listeners.push_back(new network::UnixListener(path, mode));

vlog.info("Listening for VNC connections on %s (mode %04o)",
path, mode);
} else {
const char *addr = interface;
int port = rfbport;

+ 15
- 3
vncviewer/CConn.cxx View File

@@ -39,6 +39,9 @@
#include <rdr/MemInStream.h>
#include <rdr/MemOutStream.h>
#include <network/TcpSocket.h>
#ifndef WIN32
#include <network/UnixSocket.h>
#endif

#include <FL/Fl.H>
#include <FL/fl_ask.H>
@@ -106,10 +109,19 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)

if(sock == NULL) {
try {
getHostAndPort(vncServerName, &serverHost, &serverPort);
#ifndef WIN32
if (strchr(vncServerName, '/') != NULL) {
sock = new network::UnixSocket(vncServerName);
serverHost = sock->getPeerAddress();
vlog.info(_("connected to socket %s"), serverHost);
} else
#endif
{
getHostAndPort(vncServerName, &serverHost, &serverPort);

sock = new network::TcpSocket(serverHost, serverPort);
vlog.info(_("connected to host %s port %d"), serverHost, serverPort);
sock = new network::TcpSocket(serverHost, serverPort);
vlog.info(_("connected to host %s port %d"), serverHost, serverPort);
}
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
if (alertOnFatalError)

Loading…
Cancel
Save