Initial commit
This commit is contained in:
21
common/network/CMakeLists.txt
Normal file
21
common/network/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common)
|
||||
|
||||
set(NETWORK_SOURCES
|
||||
Socket.cxx
|
||||
TcpSocket.cxx
|
||||
websocket.c
|
||||
websockify.c)
|
||||
|
||||
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()
|
||||
|
||||
if(UNIX)
|
||||
libtool_create_control_file(network)
|
||||
endif()
|
||||
183
common/network/Socket.cxx
Normal file
183
common/network/Socket.cxx
Normal file
@@ -0,0 +1,183 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. 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
|
||||
|
||||
#ifdef WIN32
|
||||
//#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define errorNumber WSAGetLastError()
|
||||
#else
|
||||
#define errorNumber errno
|
||||
#define closesocket close
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <network/Socket.h>
|
||||
|
||||
using namespace network;
|
||||
|
||||
// -=- Socket initialisation
|
||||
static bool socketsInitialised = false;
|
||||
void network::initSockets() {
|
||||
if (socketsInitialised)
|
||||
return;
|
||||
#ifdef WIN32
|
||||
WORD requiredVersion = MAKEWORD(2,0);
|
||||
WSADATA initResult;
|
||||
|
||||
if (WSAStartup(requiredVersion, &initResult) != 0)
|
||||
throw SocketException("unable to initialise Winsock2", errorNumber);
|
||||
#else
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
socketsInitialised = true;
|
||||
}
|
||||
|
||||
bool network::isSocketListening(int sock)
|
||||
{
|
||||
int listening = 0;
|
||||
socklen_t listening_size = sizeof(listening);
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN,
|
||||
(char *)&listening, &listening_size) < 0)
|
||||
return false;
|
||||
return listening != 0;
|
||||
}
|
||||
|
||||
Socket::Socket(int fd)
|
||||
: instream(0), outstream(0),
|
||||
isShutdown_(false), queryConnection(false)
|
||||
{
|
||||
initSockets();
|
||||
setFd(fd);
|
||||
}
|
||||
|
||||
Socket::Socket()
|
||||
: instream(0), outstream(0),
|
||||
isShutdown_(false), queryConnection(false)
|
||||
{
|
||||
initSockets();
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
if (instream && outstream)
|
||||
closesocket(getFd());
|
||||
delete instream;
|
||||
delete outstream;
|
||||
}
|
||||
|
||||
// if shutdown() is overridden then the override MUST call on to here
|
||||
void Socket::shutdown()
|
||||
{
|
||||
isShutdown_ = true;
|
||||
::shutdown(getFd(), 2);
|
||||
}
|
||||
|
||||
bool Socket::isShutdown() const
|
||||
{
|
||||
return isShutdown_;
|
||||
}
|
||||
|
||||
// Was there a "?" in the ConnectionFilter used to accept this Socket?
|
||||
void Socket::setRequiresQuery()
|
||||
{
|
||||
queryConnection = true;
|
||||
}
|
||||
|
||||
bool Socket::requiresQuery() const
|
||||
{
|
||||
return queryConnection;
|
||||
}
|
||||
|
||||
void Socket::setFd(int fd)
|
||||
{
|
||||
#ifndef WIN32
|
||||
// - By default, close the socket on exec()
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
instream = new rdr::FdInStream(fd);
|
||||
outstream = new rdr::FdOutStream(fd);
|
||||
isShutdown_ = false;
|
||||
}
|
||||
|
||||
SocketListener::SocketListener(int fd)
|
||||
: fd(fd), filter(0)
|
||||
{
|
||||
initSockets();
|
||||
}
|
||||
|
||||
SocketListener::SocketListener()
|
||||
: fd(-1), filter(0)
|
||||
{
|
||||
initSockets();
|
||||
}
|
||||
|
||||
SocketListener::~SocketListener()
|
||||
{
|
||||
if (fd != -1)
|
||||
closesocket(fd);
|
||||
}
|
||||
|
||||
void SocketListener::shutdown()
|
||||
{
|
||||
#ifdef WIN32
|
||||
closesocket(fd);
|
||||
fd = -1;
|
||||
#else
|
||||
::shutdown(fd, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
Socket* SocketListener::accept() {
|
||||
int new_sock = -1;
|
||||
|
||||
// Accept an incoming connection
|
||||
if ((new_sock = ::accept(fd, 0, 0)) < 0)
|
||||
throw SocketException("unable to accept new connection", errorNumber);
|
||||
|
||||
// Create the socket object & check connection is allowed
|
||||
Socket* s = createSocket(new_sock);
|
||||
if (filter && !filter->verifyConnection(s)) {
|
||||
delete s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void SocketListener::listen(int sock)
|
||||
{
|
||||
// - Set it to be a listening socket
|
||||
if (::listen(sock, 5) < 0) {
|
||||
int e = errorNumber;
|
||||
closesocket(sock);
|
||||
throw SocketException("unable to set socket to listening mode", e);
|
||||
}
|
||||
|
||||
fd = sock;
|
||||
}
|
||||
160
common/network/Socket.h
Normal file
160
common/network/Socket.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
|
||||
*/
|
||||
|
||||
// -=- Socket.h - abstract base-class for any kind of network stream/socket
|
||||
|
||||
#ifndef __NETWORK_SOCKET_H__
|
||||
#define __NETWORK_SOCKET_H__
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <limits.h>
|
||||
#include <rdr/FdInStream.h>
|
||||
#include <rdr/FdOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
namespace network {
|
||||
|
||||
void initSockets();
|
||||
|
||||
bool isSocketListening(int sock);
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
Socket(int fd);
|
||||
virtual ~Socket();
|
||||
|
||||
rdr::FdInStream &inStream() {return *instream;}
|
||||
rdr::FdOutStream &outStream() {return *outstream;}
|
||||
int getFd() {return outstream->getFd();}
|
||||
|
||||
void shutdown();
|
||||
bool isShutdown() const;
|
||||
|
||||
virtual bool cork(bool enable) = 0;
|
||||
|
||||
// information about the remote end of the socket
|
||||
virtual char* getPeerAddress() = 0; // a string e.g. "192.168.0.1"
|
||||
virtual char* getPeerEndpoint() = 0; // <address>::<port>
|
||||
|
||||
// Was there a "?" in the ConnectionFilter used to accept this Socket?
|
||||
void setRequiresQuery();
|
||||
bool requiresQuery() const;
|
||||
|
||||
protected:
|
||||
Socket();
|
||||
|
||||
void setFd(int fd);
|
||||
|
||||
private:
|
||||
rdr::FdInStream* instream;
|
||||
rdr::FdOutStream* outstream;
|
||||
bool isShutdown_;
|
||||
bool queryConnection;
|
||||
};
|
||||
|
||||
class ConnectionFilter {
|
||||
public:
|
||||
virtual bool verifyConnection(Socket* s) = 0;
|
||||
virtual ~ConnectionFilter() {}
|
||||
};
|
||||
|
||||
class SocketListener {
|
||||
public:
|
||||
SocketListener(int fd);
|
||||
virtual ~SocketListener();
|
||||
|
||||
// shutdown() stops the socket from accepting further connections
|
||||
void shutdown();
|
||||
|
||||
// accept() returns a new Socket object if there is a connection
|
||||
// attempt in progress AND if the connection passes the filter
|
||||
// if one is installed. Otherwise, returns 0.
|
||||
Socket* accept();
|
||||
|
||||
virtual int getMyPort() = 0;
|
||||
|
||||
// setFilter() applies the specified filter to all new connections
|
||||
void setFilter(ConnectionFilter* f) {filter = f;}
|
||||
int getFd() {return fd;}
|
||||
|
||||
protected:
|
||||
SocketListener();
|
||||
|
||||
void listen(int fd);
|
||||
|
||||
// createSocket() should create a new socket of the correct class
|
||||
// for the given file descriptor
|
||||
virtual Socket* createSocket(int fd) = 0;
|
||||
|
||||
protected:
|
||||
int fd;
|
||||
ConnectionFilter* filter;
|
||||
};
|
||||
|
||||
struct SocketException : public rdr::SystemException {
|
||||
SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {}
|
||||
};
|
||||
|
||||
class SocketServer {
|
||||
public:
|
||||
virtual ~SocketServer() {}
|
||||
|
||||
// addSocket() tells the server to serve the Socket. The caller
|
||||
// retains ownership of the Socket - the only way for the server
|
||||
// to discard a Socket is by calling shutdown() on it.
|
||||
// outgoing is set to true if the socket was created by connecting out
|
||||
// to another host, or false if the socket was created by accept()ing
|
||||
// an incoming connection.
|
||||
virtual void addSocket(network::Socket* sock, bool outgoing=false) = 0;
|
||||
|
||||
// removeSocket() tells the server to stop serving the Socket. The
|
||||
// caller retains ownership of the Socket - the server must NOT
|
||||
// delete the Socket! This call is used mainly to cause per-Socket
|
||||
// resources to be freed.
|
||||
virtual void removeSocket(network::Socket* sock) = 0;
|
||||
|
||||
// getSockets() gets a list of sockets. This can be used to generate an
|
||||
// fd_set for calling select().
|
||||
virtual void getSockets(std::list<network::Socket*>* sockets) = 0;
|
||||
|
||||
// processSocketReadEvent() tells the server there is a Socket read event.
|
||||
// The implementation can indicate that the Socket is no longer active
|
||||
// by calling shutdown() on it. The caller will then call removeSocket()
|
||||
// soon after processSocketEvent returns, to allow any pre-Socket
|
||||
// resources to be tidied up.
|
||||
virtual void processSocketReadEvent(network::Socket* sock) = 0;
|
||||
|
||||
// processSocketReadEvent() tells the server there is a Socket write event.
|
||||
// This is only necessary if the Socket has been put in non-blocking
|
||||
// mode and needs this callback to flush the buffer.
|
||||
virtual void processSocketWriteEvent(network::Socket* sock) = 0;
|
||||
|
||||
// checkTimeouts() allows the server to check socket timeouts, etc. The
|
||||
// return value is the number of milliseconds to wait before
|
||||
// checkTimeouts() should be called again. If this number is zero then
|
||||
// there is no timeout and checkTimeouts() should be called the next time
|
||||
// an event occurs.
|
||||
virtual int checkTimeouts() = 0;
|
||||
|
||||
virtual bool getDisable() {return false;};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __NETWORK_SOCKET_H__
|
||||
1029
common/network/TcpSocket.cxx
Normal file
1029
common/network/TcpSocket.cxx
Normal file
File diff suppressed because it is too large
Load Diff
158
common/network/TcpSocket.h
Normal file
158
common/network/TcpSocket.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
|
||||
*/
|
||||
|
||||
// -=- TcpSocket.h - base-class for TCP stream sockets.
|
||||
// This header also defines the TcpListener class, used
|
||||
// to listen for incoming socket connections over TCP
|
||||
//
|
||||
// NB: Any file descriptors created by the TcpSocket or
|
||||
// TcpListener classes are close-on-exec if the OS supports
|
||||
// it. TcpSockets initialised with a caller-supplied fd
|
||||
// are NOT set to close-on-exec.
|
||||
|
||||
#ifndef __NETWORK_TCP_SOCKET_H__
|
||||
#define __NETWORK_TCP_SOCKET_H__
|
||||
|
||||
#include <network/Socket.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h> /* for socklen_t */
|
||||
#include <netinet/in.h> /* for struct sockaddr_in */
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
|
||||
/* Tunnelling support. */
|
||||
#define TUNNEL_PORT_OFFSET 5500
|
||||
|
||||
namespace network {
|
||||
|
||||
/* Tunnelling support. */
|
||||
int findFreeTcpPort (void);
|
||||
|
||||
int getSockPort(int sock);
|
||||
|
||||
class TcpSocket : public Socket {
|
||||
public:
|
||||
TcpSocket(int sock);
|
||||
TcpSocket(const char *name, int port);
|
||||
|
||||
virtual char* getPeerAddress();
|
||||
virtual char* getPeerEndpoint();
|
||||
|
||||
virtual bool cork(bool enable);
|
||||
|
||||
protected:
|
||||
bool enableNagles(bool enable);
|
||||
};
|
||||
|
||||
class WebSocket : public Socket {
|
||||
public:
|
||||
WebSocket(int sock);
|
||||
|
||||
virtual char* getPeerAddress();
|
||||
virtual char* getPeerEndpoint();
|
||||
|
||||
virtual bool cork(bool enable) { return true; }
|
||||
};
|
||||
|
||||
class TcpListener : public SocketListener {
|
||||
public:
|
||||
TcpListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen);
|
||||
TcpListener(int sock);
|
||||
|
||||
virtual int getMyPort();
|
||||
|
||||
static void getMyAddresses(std::list<char*>* result);
|
||||
|
||||
protected:
|
||||
virtual Socket* createSocket(int fd);
|
||||
};
|
||||
|
||||
class WebsocketListener : public SocketListener {
|
||||
public:
|
||||
WebsocketListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen,
|
||||
bool sslonly, const char *cert, const char *basicauth,
|
||||
const char *httpdir);
|
||||
|
||||
virtual int getMyPort();
|
||||
|
||||
static void getMyAddresses(std::list<char*>* result);
|
||||
|
||||
int internalSocket;
|
||||
|
||||
protected:
|
||||
virtual Socket* createSocket(int fd);
|
||||
};
|
||||
|
||||
void createLocalTcpListeners(std::list<SocketListener*> *listeners,
|
||||
int port);
|
||||
void createWebsocketListeners(std::list<SocketListener*> *listeners,
|
||||
int port,
|
||||
const char *addr,
|
||||
bool sslonly,
|
||||
const char *cert,
|
||||
const char *basicauth,
|
||||
const char *httpdir);
|
||||
void createTcpListeners(std::list<SocketListener*> *listeners,
|
||||
const char *addr,
|
||||
int port);
|
||||
void createTcpListeners(std::list<SocketListener*> *listeners,
|
||||
const struct addrinfo *ai);
|
||||
void createWebsocketListeners(std::list<SocketListener*> *listeners,
|
||||
const struct addrinfo *ai,
|
||||
bool sslonly,
|
||||
const char *cert,
|
||||
const char *basicauth,
|
||||
const char *httpdir);
|
||||
|
||||
typedef struct vnc_sockaddr {
|
||||
union {
|
||||
sockaddr sa;
|
||||
sockaddr_in sin;
|
||||
sockaddr_in6 sin6;
|
||||
} u;
|
||||
} vnc_sockaddr_t;
|
||||
|
||||
class TcpFilter : public ConnectionFilter {
|
||||
public:
|
||||
TcpFilter(const char* filter);
|
||||
virtual ~TcpFilter();
|
||||
|
||||
virtual bool verifyConnection(Socket* s);
|
||||
|
||||
typedef enum {Accept, Reject, Query} Action;
|
||||
struct Pattern {
|
||||
Action action;
|
||||
vnc_sockaddr_t address;
|
||||
unsigned int prefixlen;
|
||||
|
||||
vnc_sockaddr_t mask; // computed from address and prefix
|
||||
};
|
||||
static Pattern parsePattern(const char* s);
|
||||
static char* patternToStr(const Pattern& p);
|
||||
protected:
|
||||
std::list<Pattern> filter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __NETWORK_TCP_SOCKET_H__
|
||||
171
common/network/UnixSocket.cxx
Normal file
171
common/network/UnixSocket.cxx
Normal file
@@ -0,0 +1,171 @@
|
||||
/* 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/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.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");
|
||||
|
||||
// -=- UnixSocket
|
||||
|
||||
UnixSocket::UnixSocket(int sock) : Socket(sock)
|
||||
{
|
||||
}
|
||||
|
||||
UnixSocket::UnixSocket(const char *path)
|
||||
{
|
||||
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
|
||||
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);
|
||||
|
||||
setFd(sock);
|
||||
}
|
||||
|
||||
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)");
|
||||
}
|
||||
|
||||
char* UnixSocket::getPeerEndpoint() {
|
||||
return getPeerAddress();
|
||||
}
|
||||
|
||||
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
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||
throw SocketException("unable to create listening socket", errno);
|
||||
|
||||
// - 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);
|
||||
}
|
||||
|
||||
listen(fd);
|
||||
}
|
||||
|
||||
UnixListener::~UnixListener()
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t salen = sizeof(addr);
|
||||
|
||||
if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) == 0)
|
||||
unlink(addr.sun_path);
|
||||
}
|
||||
|
||||
Socket* UnixListener::createSocket(int fd) {
|
||||
return new UnixSocket(fd);
|
||||
}
|
||||
|
||||
int UnixListener::getMyPort() {
|
||||
return 0;
|
||||
}
|
||||
60
common/network/UnixSocket.h
Normal file
60
common/network/UnixSocket.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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);
|
||||
UnixSocket(const char *name);
|
||||
|
||||
virtual char* getPeerAddress();
|
||||
virtual char* getPeerEndpoint();
|
||||
|
||||
virtual bool cork(bool enable);
|
||||
};
|
||||
|
||||
class UnixListener : public SocketListener {
|
||||
public:
|
||||
UnixListener(const char *listenaddr, int mode);
|
||||
virtual ~UnixListener();
|
||||
|
||||
int getMyPort();
|
||||
|
||||
protected:
|
||||
virtual Socket* createSocket(int fd);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __NETWORK_UNIX_SOCKET_H__
|
||||
1035
common/network/websocket.c
Normal file
1035
common/network/websocket.c
Normal file
File diff suppressed because it is too large
Load Diff
122
common/network/websocket.h
Normal file
122
common/network/websocket.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#define BUFSIZE 65536
|
||||
#define DBUFSIZE (BUFSIZE * 3) / 4 - 20
|
||||
|
||||
#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
|
||||
Upgrade: WebSocket\r\n\
|
||||
Connection: Upgrade\r\n\
|
||||
%sWebSocket-Origin: %s\r\n\
|
||||
%sWebSocket-Location: %s://%s%s\r\n\
|
||||
%sWebSocket-Protocol: %s\r\n\
|
||||
\r\n%s"
|
||||
|
||||
#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\
|
||||
Upgrade: websocket\r\n\
|
||||
Connection: Upgrade\r\n\
|
||||
Sec-WebSocket-Accept: %s\r\n\
|
||||
Sec-WebSocket-Protocol: %s\r\n\
|
||||
\r\n"
|
||||
|
||||
#define HYBI_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
|
||||
#define HYBI10_ACCEPTHDRLEN 29
|
||||
|
||||
#define HIXIE_MD5_DIGEST_LENGTH 16
|
||||
|
||||
#define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
|
||||
|
||||
#define OPCODE_TEXT 0x01
|
||||
#define OPCODE_BINARY 0x02
|
||||
|
||||
typedef struct {
|
||||
char path[1024+1];
|
||||
char host[1024+1];
|
||||
char origin[1024+1];
|
||||
char version[1024+1];
|
||||
char connection[1024+1];
|
||||
char protocols[1024+1];
|
||||
char key1[1024+1];
|
||||
char key2[1024+1];
|
||||
char key3[8+1];
|
||||
} headers_t;
|
||||
|
||||
typedef struct {
|
||||
int sockfd;
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
int hixie;
|
||||
int hybi;
|
||||
int opcode;
|
||||
headers_t *headers;
|
||||
char *cin_buf;
|
||||
char *cout_buf;
|
||||
char *tin_buf;
|
||||
char *tout_buf;
|
||||
} ws_ctx_t;
|
||||
|
||||
struct wspass_t {
|
||||
int csock;
|
||||
unsigned id;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int verbose;
|
||||
int listen_sock;
|
||||
unsigned int handler_id;
|
||||
const char *cert;
|
||||
const char *key;
|
||||
const char *basicauth;
|
||||
int ssl_only;
|
||||
const char *httpdir;
|
||||
} settings_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int resolve_host(struct in_addr *sin_addr, const char *hostname);
|
||||
|
||||
ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len);
|
||||
|
||||
ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len);
|
||||
|
||||
/* base64.c declarations */
|
||||
//int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize);
|
||||
//int b64_pton(char const *src, u_char *target, size_t targsize);
|
||||
|
||||
extern __thread unsigned wsthread_handler_id;
|
||||
|
||||
#define gen_handler_msg(stream, ...) \
|
||||
if (settings.verbose) { \
|
||||
fprintf(stream, " websocket %d: ", wsthread_handler_id); \
|
||||
fprintf(stream, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define wserr(...) \
|
||||
{ \
|
||||
fprintf(stderr, " websocket %d: ", wsthread_handler_id); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define handler_msg(...) gen_handler_msg(stderr, __VA_ARGS__);
|
||||
#define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__);
|
||||
|
||||
void traffic(const char * token);
|
||||
|
||||
int encode_hixie(u_char const *src, size_t srclength,
|
||||
char *target, size_t targsize);
|
||||
int decode_hixie(char *src, size_t srclength,
|
||||
u_char *target, size_t targsize,
|
||||
unsigned int *opcode, unsigned int *left);
|
||||
int encode_hybi(u_char const *src, size_t srclength,
|
||||
char *target, size_t targsize, unsigned int opcode);
|
||||
int decode_hybi(unsigned char *src, size_t srclength,
|
||||
u_char *target, size_t targsize,
|
||||
unsigned int *opcode, unsigned int *left);
|
||||
|
||||
void *start_server(void *unused);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern C
|
||||
#endif
|
||||
361
common/network/websockify.c
Normal file
361
common/network/websockify.c
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* A WebSocket to TCP socket proxy with support for "wss://" encryption.
|
||||
* Copyright 2010 Joel Martin
|
||||
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||
*
|
||||
* You can make a cert/key with openssl using:
|
||||
* openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
||||
* as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "websocket.h"
|
||||
|
||||
/*
|
||||
char USAGE[] = "Usage: [options] " \
|
||||
"[source_addr:]source_port target_addr:target_port\n\n" \
|
||||
" --verbose|-v verbose messages and per frame traffic\n" \
|
||||
" --daemon|-D become a daemon (background process)\n" \
|
||||
" --run-once handle a single WebSocket connection and exit\n" \
|
||||
" --cert CERT SSL certificate file\n" \
|
||||
" --key KEY SSL key file (if separate from cert)\n" \
|
||||
" --ssl-only disallow non-encrypted connections";
|
||||
*/
|
||||
|
||||
extern int pipe_error;
|
||||
extern settings_t settings;
|
||||
|
||||
static void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
||||
fd_set rlist, wlist, elist;
|
||||
struct timeval tv;
|
||||
int maxfd, client = ws_ctx->sockfd;
|
||||
unsigned int opcode, left, ret;
|
||||
unsigned int tout_start, tout_end, cout_start, cout_end;
|
||||
unsigned int tin_start, tin_end;
|
||||
ssize_t len, bytes;
|
||||
|
||||
tout_start = tout_end = cout_start = cout_end =
|
||||
tin_start = tin_end = 0;
|
||||
maxfd = client > target ? client+1 : target+1;
|
||||
|
||||
while (1) {
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&rlist);
|
||||
FD_ZERO(&wlist);
|
||||
FD_ZERO(&elist);
|
||||
|
||||
FD_SET(client, &elist);
|
||||
FD_SET(target, &elist);
|
||||
|
||||
if (tout_end == tout_start) {
|
||||
// Nothing queued for target, so read from client
|
||||
FD_SET(client, &rlist);
|
||||
} else {
|
||||
// Data queued for target, so write to it
|
||||
FD_SET(target, &wlist);
|
||||
}
|
||||
if (cout_end == cout_start) {
|
||||
// Nothing queued for client, so read from target
|
||||
FD_SET(target, &rlist);
|
||||
} else {
|
||||
// Data queued for client, so write to it
|
||||
FD_SET(client, &wlist);
|
||||
}
|
||||
|
||||
do {
|
||||
ret = select(maxfd, &rlist, &wlist, &elist, &tv);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
if (pipe_error) { break; }
|
||||
|
||||
if (FD_ISSET(target, &elist)) {
|
||||
handler_emsg("target exception\n");
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(client, &elist)) {
|
||||
handler_emsg("client exception\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
handler_emsg("select(): %s\n", strerror(errno));
|
||||
break;
|
||||
} else if (ret == 0) {
|
||||
//handler_emsg("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FD_ISSET(target, &wlist)) {
|
||||
len = tout_end-tout_start;
|
||||
bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0);
|
||||
if (pipe_error) { break; }
|
||||
if (bytes < 0) {
|
||||
handler_emsg("target connection error: %s\n",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
tout_start += bytes;
|
||||
if (tout_start >= tout_end) {
|
||||
tout_start = tout_end = 0;
|
||||
traffic(">");
|
||||
} else {
|
||||
traffic(">.");
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(client, &wlist)) {
|
||||
len = cout_end-cout_start;
|
||||
bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len);
|
||||
if (pipe_error) { break; }
|
||||
if (len < 3) {
|
||||
handler_emsg("len: %d, bytes: %d: %d\n",
|
||||
(int) len, (int) bytes,
|
||||
(int) *(ws_ctx->cout_buf + cout_start));
|
||||
}
|
||||
cout_start += bytes;
|
||||
if (cout_start >= cout_end) {
|
||||
cout_start = cout_end = 0;
|
||||
traffic("<");
|
||||
} else {
|
||||
traffic("<.");
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(target, &rlist)) {
|
||||
bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0);
|
||||
if (pipe_error) { break; }
|
||||
if (bytes <= 0) {
|
||||
handler_emsg("target closed connection\n");
|
||||
break;
|
||||
}
|
||||
cout_start = 0;
|
||||
if (ws_ctx->hybi) {
|
||||
cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
|
||||
ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode);
|
||||
} else {
|
||||
cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
|
||||
ws_ctx->cout_buf, BUFSIZE);
|
||||
}
|
||||
/*
|
||||
printf("encoded: ");
|
||||
for (i=0; i< cout_end; i++) {
|
||||
printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i));
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
if (cout_end < 0) {
|
||||
handler_emsg("encoding error\n");
|
||||
break;
|
||||
}
|
||||
traffic("{");
|
||||
}
|
||||
|
||||
if (FD_ISSET(client, &rlist)) {
|
||||
bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1);
|
||||
if (pipe_error) { break; }
|
||||
if (bytes <= 0) {
|
||||
handler_emsg("client closed connection\n");
|
||||
break;
|
||||
}
|
||||
tin_end += bytes;
|
||||
/*
|
||||
printf("before decode: ");
|
||||
for (i=0; i< bytes; i++) {
|
||||
printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i));
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
if (ws_ctx->hybi) {
|
||||
len = decode_hybi(ws_ctx->tin_buf + tin_start,
|
||||
tin_end-tin_start,
|
||||
ws_ctx->tout_buf, BUFSIZE-1,
|
||||
&opcode, &left);
|
||||
} else {
|
||||
len = decode_hixie(ws_ctx->tin_buf + tin_start,
|
||||
tin_end-tin_start,
|
||||
ws_ctx->tout_buf, BUFSIZE-1,
|
||||
&opcode, &left);
|
||||
}
|
||||
|
||||
if (opcode == 8) {
|
||||
handler_msg("client sent orderly close frame\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
printf("decoded: ");
|
||||
for (i=0; i< len; i++) {
|
||||
printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i));
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
if (len < 0) {
|
||||
handler_emsg("decoding error\n");
|
||||
break;
|
||||
}
|
||||
if (left) {
|
||||
tin_start = tin_end - left;
|
||||
//printf("partial frame from client");
|
||||
} else {
|
||||
tin_start = 0;
|
||||
tin_end = 0;
|
||||
}
|
||||
|
||||
traffic("}");
|
||||
tout_start = 0;
|
||||
tout_end = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
||||
addr.sun_path[0] = '\0';
|
||||
|
||||
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
handler_msg("connecting to VNC target\n");
|
||||
|
||||
if (connect(tsock, (struct sockaddr *) &addr,
|
||||
sizeof(sa_family_t) + sizeof(".KasmVNCSock")) < 0) {
|
||||
|
||||
handler_emsg("Could not connect to target: %s\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
do_proxy(ws_ctx, tsock);
|
||||
|
||||
shutdown(tsock, SHUT_RDWR);
|
||||
close(tsock);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd, c, option_index = 0;
|
||||
static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0;
|
||||
char *found;
|
||||
static struct option long_options[] = {
|
||||
{"verbose", no_argument, &verbose, 'v'},
|
||||
{"ssl-only", no_argument, &ssl_only, 1 },
|
||||
{"daemon", no_argument, &daemon, 'D'},
|
||||
/* ---- */
|
||||
{"run-once", no_argument, 0, 'r'},
|
||||
{"cert", required_argument, 0, 'c'},
|
||||
{"key", required_argument, 0, 'k'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
settings.cert = realpath("self.pem", NULL);
|
||||
if (!settings.cert) {
|
||||
/* Make sure it's always set to something */
|
||||
settings.cert = "self.pem";
|
||||
}
|
||||
settings.key = "";
|
||||
|
||||
while (1) {
|
||||
c = getopt_long (argc, argv, "vDrc:k:",
|
||||
long_options, &option_index);
|
||||
|
||||
/* Detect the end */
|
||||
if (c == -1) { break; }
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
break; // ignore
|
||||
case 1:
|
||||
break; // ignore
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'D':
|
||||
daemon = 1;
|
||||
break;
|
||||
case 'r':
|
||||
run_once = 1;
|
||||
break;
|
||||
case 'c':
|
||||
settings.cert = realpath(optarg, NULL);
|
||||
if (! settings.cert) {
|
||||
usage("No cert file at %s\n", optarg);
|
||||
}
|
||||
break;
|
||||
case 'k':
|
||||
settings.key = realpath(optarg, NULL);
|
||||
if (! settings.key) {
|
||||
usage("No key file at %s\n", optarg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage("");
|
||||
}
|
||||
}
|
||||
settings.verbose = verbose;
|
||||
settings.ssl_only = ssl_only;
|
||||
settings.daemon = daemon;
|
||||
settings.run_once = run_once;
|
||||
|
||||
if ((argc-optind) != 2) {
|
||||
usage("Invalid number of arguments\n");
|
||||
}
|
||||
|
||||
found = strstr(argv[optind], ":");
|
||||
if (found) {
|
||||
memcpy(settings.listen_host, argv[optind], found-argv[optind]);
|
||||
settings.listen_port = strtol(found+1, NULL, 10);
|
||||
} else {
|
||||
settings.listen_host[0] = '\0';
|
||||
settings.listen_port = strtol(argv[optind], NULL, 10);
|
||||
}
|
||||
optind++;
|
||||
if (settings.listen_port == 0) {
|
||||
usage("Could not parse listen_port\n");
|
||||
}
|
||||
|
||||
found = strstr(argv[optind], ":");
|
||||
if (found) {
|
||||
memcpy(target_host, argv[optind], found-argv[optind]);
|
||||
target_port = strtol(found+1, NULL, 10);
|
||||
} else {
|
||||
usage("Target argument must be host:port\n");
|
||||
}
|
||||
if (target_port == 0) {
|
||||
usage("Could not parse target port\n");
|
||||
}
|
||||
|
||||
if (ssl_only) {
|
||||
if (access(settings.cert, R_OK) != 0) {
|
||||
usage("SSL only and cert file '%s' not found\n", settings.cert);
|
||||
}
|
||||
} else if (access(settings.cert, R_OK) != 0) {
|
||||
fprintf(stderr, "Warning: '%s' not found\n", settings.cert);
|
||||
}
|
||||
|
||||
//printf(" verbose: %d\n", settings.verbose);
|
||||
//printf(" ssl_only: %d\n", settings.ssl_only);
|
||||
//printf(" daemon: %d\n", settings.daemon);
|
||||
//printf(" run_once: %d\n", settings.run_once);
|
||||
//printf(" cert: %s\n", settings.cert);
|
||||
//printf(" key: %s\n", settings.key);
|
||||
|
||||
settings.handler = proxy_handler;
|
||||
start_server();
|
||||
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user