Patch originally by Dag-Erling Smørgrav for University of Oslo.tags/v1.8.90
@@ -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() |
@@ -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; | |||
} |
@@ -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__ |
@@ -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 |
@@ -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; |
@@ -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) |