671 lines
14 KiB
C++
Executable File
671 lines
14 KiB
C++
Executable File
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
// Copyright (C) 2001 HorizonLive.com, Inc. All Rights Reserved.
|
|
//
|
|
// This file is part of the VNC system.
|
|
//
|
|
// The VNC system 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 program 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 program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
|
|
//
|
|
// If the source code for the VNC system is not available from the place
|
|
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
|
|
// the authors on vnc@uk.research.att.com for information on obtaining it.
|
|
|
|
|
|
// VSocket.cpp
|
|
|
|
// The VSocket class provides a platform-independent socket abstraction
|
|
// with the simple functionality required for an RFB server.
|
|
|
|
class VSocket;
|
|
|
|
////////////////////////////////////////////////////////
|
|
// System includes
|
|
|
|
#include "stdhdrs.h"
|
|
|
|
// Visual C++ .NET 2003 compatibility
|
|
#if (_MSC_VER>= 1300)
|
|
#include <iostream>
|
|
#else
|
|
#include <iostream.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#ifdef __WIN32__
|
|
#include <io.h>
|
|
#include <winsock.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
|
|
////////////////////////////////////////////////////////
|
|
// Custom includes
|
|
|
|
#include "VTypes.h"
|
|
|
|
////////////////////////////////////////////////////////
|
|
// *** Lovely hacks to make Win32 work. Hurrah!
|
|
|
|
#if defined(__WIN32__) && !defined(EWOULDBLOCK)
|
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////
|
|
// Socket implementation
|
|
|
|
#include "VSocket.h"
|
|
|
|
// The socket timeout value (currently 5 seconds, for no reason...)
|
|
// *** THIS IS NOT CURRENTLY USED ANYWHERE
|
|
const VInt rfbMaxClientWait = 5000;
|
|
|
|
////////////////////////////
|
|
// Socket implementation initialisation
|
|
|
|
static WORD winsockVersion = 0;
|
|
|
|
VSocketSystem::VSocketSystem()
|
|
{
|
|
// Initialise the socket subsystem
|
|
// This is only provided for compatibility with Windows.
|
|
|
|
#ifdef __WIN32__
|
|
// Initialise WinPoxySockets on Win32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
if (WSAStartup(wVersionRequested, &wsaData) != 0)
|
|
{
|
|
m_status = VFalse;
|
|
return;
|
|
}
|
|
|
|
winsockVersion = wsaData.wVersion;
|
|
|
|
#else
|
|
// Disable the nasty read/write failure signals on UNIX
|
|
signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
|
|
// If successful, or if not required, then continue!
|
|
m_status = VTrue;
|
|
}
|
|
|
|
VSocketSystem::~VSocketSystem()
|
|
{
|
|
/*if (m_status)
|
|
{
|
|
WSACleanup();
|
|
}*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VSocket::VSocket()
|
|
{
|
|
// Clear out the internal socket fields
|
|
sock = -1;
|
|
hCloseEvent = NULL;
|
|
out_queue = NULL;
|
|
}
|
|
|
|
VSocket::VSocket( WSAPROTOCOL_INFO * pSocketInfo, HANDLE hClose )
|
|
{
|
|
sock = (int)WSASocket( AF_INET, SOCK_STREAM, 0, pSocketInfo, 0, 0 );
|
|
if( sock == INVALID_SOCKET )
|
|
sock = -1;
|
|
//BREAK_ON_WSAERROR( "[VNCDLL] vncdll_run. WSASocketA failed" );
|
|
|
|
// Clear out the internal socket fields
|
|
//sock = (int)socket;
|
|
hCloseEvent = hClose;
|
|
out_queue = NULL;
|
|
}
|
|
////////////////////////////
|
|
|
|
VSocket::~VSocket()
|
|
{
|
|
// Close the socket
|
|
Close();
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::Create()
|
|
{
|
|
const int one = 1;
|
|
|
|
// Check that the old socket was closed
|
|
if (sock >= 0)
|
|
Close();
|
|
|
|
// Create the socket
|
|
if ((sock = (int)socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
return VFalse;
|
|
}
|
|
|
|
// Set the socket options:
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)))
|
|
{
|
|
return VFalse;
|
|
}
|
|
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)))
|
|
{
|
|
return VFalse;
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
extern HANDLE hCloseEvent;
|
|
|
|
VBool VSocket::Close()
|
|
{
|
|
if( sock >= 0 )
|
|
{
|
|
//shutdown(sock, SD_BOTH);
|
|
//closesocket(sock);
|
|
//CloseHandle( (HANDLE)sock );
|
|
sock = -1;
|
|
SetEvent( hCloseEvent );
|
|
}
|
|
|
|
while (out_queue)
|
|
{
|
|
AIOBlock *next = out_queue->next;
|
|
delete out_queue;
|
|
out_queue = next;
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::Shutdown()
|
|
{
|
|
/*if (sock >= 0)
|
|
{
|
|
shutdown(sock, SD_BOTH);
|
|
}*/
|
|
while (out_queue)
|
|
{
|
|
AIOBlock *next = out_queue->next;
|
|
delete out_queue;
|
|
out_queue = next;
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::Bind(const VCard port, const VBool localOnly,
|
|
const VBool checkIfInUse)
|
|
{
|
|
return VFalse;
|
|
/*struct sockaddr_in addr;
|
|
|
|
// Check that the socket is open!
|
|
if (sock < 0)
|
|
return VFalse;
|
|
|
|
// If a specific port is being set then check it's not already used!
|
|
if (port != 0 && checkIfInUse)
|
|
{
|
|
VSocket dummy;
|
|
|
|
if (dummy.Create())
|
|
{
|
|
// If we're able to connect then the port number is in use...
|
|
if (dummy.Connect("localhost", port))
|
|
return VFalse;
|
|
}
|
|
}
|
|
|
|
// Set up the address to bind the socket to
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
if (localOnly)
|
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
else
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
// And do the binding
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
return VFalse;
|
|
|
|
return VTrue;*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::Connect(VStringConst address, const VCard port)
|
|
{
|
|
return VFalse;
|
|
/*
|
|
// Check the socket
|
|
if (sock < 0)
|
|
return VFalse;
|
|
|
|
// Create an address structure and clear it
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
// Fill in the address if possible
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = inet_addr(address);
|
|
|
|
// Was the string a valid IP address?
|
|
if (addr.sin_addr.s_addr == -1)
|
|
{
|
|
// No, so get the actual IP address of the host name specified
|
|
struct hostent *pHost;
|
|
pHost = gethostbyname(address);
|
|
if (pHost != NULL)
|
|
{
|
|
if (pHost->h_addr == NULL)
|
|
return VFalse;
|
|
addr.sin_addr.s_addr = ((struct in_addr *)pHost->h_addr)->s_addr;
|
|
}
|
|
else
|
|
return VFalse;
|
|
}
|
|
|
|
// Set the port number in the correct format
|
|
addr.sin_port = htons(port);
|
|
|
|
// Actually connect the socket
|
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
|
|
return VFalse;
|
|
|
|
// Put the socket into non-blocking mode
|
|
#ifdef __WIN32__
|
|
u_long arg = 1;
|
|
if (ioctlsocket(sock, FIONBIO, &arg) != 0)
|
|
return VFalse;
|
|
#else
|
|
if (fcntl(sock, F_SETFL, O_NDELAY) != 0)
|
|
return VFalse;
|
|
#endif
|
|
|
|
return VTrue;*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::Listen()
|
|
{
|
|
return VFalse;
|
|
/*
|
|
// Check socket
|
|
if (sock < 0)
|
|
return VFalse;
|
|
|
|
// Set it to listen
|
|
if (listen(sock, 5) < 0)
|
|
return VFalse;
|
|
|
|
return VTrue;*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VSocket *
|
|
VSocket::Accept()
|
|
{
|
|
return VFalse;
|
|
/*
|
|
const int one = 1;
|
|
|
|
int new_socket_id;
|
|
VSocket * new_socket;
|
|
|
|
// Check this socket
|
|
if (sock < 0)
|
|
return NULL;
|
|
|
|
// Accept an incoming connection
|
|
if ((new_socket_id = (int)accept(sock, NULL, 0)) < 0)
|
|
return NULL;
|
|
|
|
// Create a new VSocket and return it
|
|
new_socket = new VSocket;
|
|
if (new_socket != NULL)
|
|
{
|
|
new_socket->sock = new_socket_id;
|
|
}
|
|
else
|
|
{
|
|
shutdown(new_socket_id, SD_BOTH);
|
|
closesocket(new_socket_id);
|
|
return NULL;
|
|
}
|
|
|
|
// Attempt to set the new socket's options
|
|
setsockopt(new_socket->sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one));
|
|
|
|
// Put the socket into non-blocking mode
|
|
#ifdef __WIN32__
|
|
u_long arg = 1;
|
|
if (ioctlsocket(new_socket->sock, FIONBIO, &arg) != 0) {
|
|
delete new_socket;
|
|
new_socket = NULL;
|
|
}
|
|
#else
|
|
if (fcntl(new_socket->sock, F_SETFL, O_NDELAY) != 0) {
|
|
delete new_socket;
|
|
new_socket = NULL;
|
|
}
|
|
#endif
|
|
|
|
return new_socket;*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::TryAccept(VSocket **new_socket, long ms)
|
|
{
|
|
return VFalse;
|
|
/*
|
|
// Check this socket
|
|
if (sock < 0)
|
|
return NULL;
|
|
|
|
struct fd_set fds;
|
|
struct timeval tm;
|
|
FD_ZERO(&fds);
|
|
FD_SET((unsigned int)sock, &fds);
|
|
tm.tv_sec = ms / 1000;
|
|
tm.tv_usec = (ms % 1000) * 1000;
|
|
int ready = select(sock + 1, &fds, NULL, NULL, &tm);
|
|
if (ready == 0) {
|
|
// Timeout
|
|
*new_socket = NULL;
|
|
return VTrue;
|
|
} else if (ready != 1) {
|
|
// Error
|
|
return VFalse;
|
|
}
|
|
// Ready to accept new connection
|
|
VSocket *s = Accept();
|
|
if (s == NULL)
|
|
return VFalse;
|
|
// Success
|
|
*new_socket = s;
|
|
return VTrue;*/
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VString VSocket::GetPeerName()
|
|
{
|
|
return "<unavailable>";
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VString VSocket::GetSockName()
|
|
{
|
|
return "<unavailable>";
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VCard32
|
|
VSocket::Resolve(VStringConst address)
|
|
{
|
|
VCard32 addr;
|
|
|
|
// Try converting the address as IP
|
|
addr = inet_addr(address);
|
|
|
|
// Was it a valid IP address?
|
|
if (addr == 0xffffffff)
|
|
{
|
|
// No, so get the actual IP address of the host name specified
|
|
struct hostent *pHost;
|
|
pHost = gethostbyname(address);
|
|
if (pHost != NULL)
|
|
{
|
|
if (pHost->h_addr == NULL)
|
|
return 0;
|
|
addr = ((struct in_addr *)pHost->h_addr)->s_addr;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Return the resolved IP address as an integer
|
|
return addr;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::SetTimeout(VCard32 secs)
|
|
{
|
|
//if (LOBYTE(winsockVersion) < 2)
|
|
// return VFalse;
|
|
int timeout=secs;
|
|
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) == SOCKET_ERROR)
|
|
{
|
|
return VFalse;
|
|
}
|
|
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) == SOCKET_ERROR)
|
|
{
|
|
return VFalse;
|
|
}
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VInt VSocket::Send(const char *buff, const VCard bufflen)
|
|
{
|
|
errno = 0;
|
|
|
|
VInt bytes = send(sock, buff, bufflen, 0);
|
|
if (bytes < 0)
|
|
{
|
|
int wsa_error = WSAGetLastError();
|
|
#ifdef __WIN32__
|
|
if (wsa_error == WSAEWOULDBLOCK)
|
|
errno = EWOULDBLOCK;
|
|
#endif
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::SendExact(const char *buff, const VCard bufflen)
|
|
{
|
|
struct fd_set write_fds;
|
|
struct timeval tm;
|
|
int count;
|
|
|
|
// Put the data into the queue
|
|
SendQueued(buff, bufflen);
|
|
|
|
while (out_queue) {
|
|
// Wait until some data can be sent
|
|
do {
|
|
FD_ZERO(&write_fds);
|
|
FD_SET((unsigned int)sock, &write_fds);
|
|
tm.tv_sec = 1;
|
|
tm.tv_usec = 0;
|
|
count = select(sock + 1, NULL, &write_fds, NULL, &tm);
|
|
} while (count == 0);
|
|
if (count < 0 || count > 1) {
|
|
return VFalse;
|
|
}
|
|
// Actually send some data
|
|
if (FD_ISSET((unsigned int)sock, &write_fds)) {
|
|
if (!SendFromQueue())
|
|
return VFalse;
|
|
}
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::SendQueued(const char *buff, const VCard bufflen)
|
|
{
|
|
omni_mutex_lock l(queue_lock);
|
|
|
|
// Just append new bytes to the output queue
|
|
if (!out_queue) {
|
|
out_queue = new AIOBlock(bufflen, buff);
|
|
bytes_sent = 0;
|
|
} else {
|
|
AIOBlock *last = out_queue;
|
|
while (last->next)
|
|
last = last->next;
|
|
last->next = new AIOBlock(bufflen, buff);
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::SendFromQueue()
|
|
{
|
|
omni_mutex_lock l(queue_lock);
|
|
|
|
// Is there something to send?
|
|
if (!out_queue)
|
|
return VTrue;
|
|
|
|
// Maximum data size to send at once
|
|
size_t portion_size = out_queue->data_size - bytes_sent;
|
|
if (portion_size > 32768)
|
|
portion_size = 32768;
|
|
|
|
// Try to send some data
|
|
int bytes = Send(out_queue->data_ptr + bytes_sent, (VCard)portion_size);
|
|
if (bytes > 0) {
|
|
bytes_sent += bytes;
|
|
} else if (bytes < 0 && errno != EWOULDBLOCK) {
|
|
return VFalse;
|
|
}
|
|
|
|
// Remove block if all its data has been sent
|
|
if (bytes_sent == out_queue->data_size) {
|
|
AIOBlock *sent = out_queue;
|
|
out_queue = sent->next;
|
|
bytes_sent = 0;
|
|
delete sent;
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VInt
|
|
VSocket::Read(char *buff, const VCard bufflen)
|
|
{
|
|
errno = 0;
|
|
|
|
VInt bytes = recv(sock, buff, bufflen, 0);
|
|
|
|
#ifdef __WIN32__
|
|
if (bytes < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
|
|
errno = EWOULDBLOCK;
|
|
#endif
|
|
|
|
return bytes;
|
|
}
|
|
|
|
////////////////////////////
|
|
|
|
VBool
|
|
VSocket::ReadExact(char *buff, const VCard bufflen)
|
|
{
|
|
int bytes;
|
|
VCard currlen = bufflen;
|
|
struct fd_set read_fds, write_fds;
|
|
struct timeval tm;
|
|
int count;
|
|
|
|
while (currlen > 0) {
|
|
// Wait until some data can be read or sent
|
|
do {
|
|
FD_ZERO(&read_fds);
|
|
FD_SET((unsigned int)sock, &read_fds);
|
|
FD_ZERO(&write_fds);
|
|
if (out_queue)
|
|
FD_SET((unsigned int)sock, &write_fds);
|
|
tm.tv_sec = 0;
|
|
tm.tv_usec = 50;
|
|
count = select(sock + 1, &read_fds, &write_fds, NULL, &tm);
|
|
} while (count == 0);
|
|
if (count < 0 || count > 2) {
|
|
return VFalse;
|
|
}
|
|
if (FD_ISSET((unsigned int)sock, &write_fds)) {
|
|
// Try to send some data
|
|
if (!SendFromQueue())
|
|
return VFalse;
|
|
}
|
|
if (FD_ISSET((unsigned int)sock, &read_fds)) {
|
|
// Try to read some data in
|
|
bytes = Read(buff, currlen);
|
|
if (bytes > 0) {
|
|
// Adjust the buffer position and size
|
|
buff += bytes;
|
|
currlen -= bytes;
|
|
} else if (bytes < 0 && errno != EWOULDBLOCK) {
|
|
return VFalse;
|
|
} else if (bytes == 0) {
|
|
return VFalse;
|
|
}
|
|
}
|
|
}
|
|
|
|
return VTrue;
|
|
}
|
|
|