You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/libvncserver/sockets.c

1110 lines
28 KiB

/*
* sockets.c - deal with TCP & UDP sockets.
*
* This code should be independent of any changes in the RFB protocol. It just
* deals with the X server scheduling stuff, calling rfbNewClientConnection and
* rfbProcessClientMessage to actually deal with the protocol. If a socket
* needs to be closed for any reason then rfbCloseClient should be called. In turn,
* rfbClientConnectionGone will be called by rfbProcessEvents (non-threaded case)
* or clientInput (threaded case) in main.c. To make an active
* connection out, call rfbConnect - note that this does _not_ call
* rfbNewClientConnection.
*
* This file is divided into two types of function. Those beginning with
* "rfb" are specific to sockets using the RFB protocol. Those without the
* "rfb" prefix are more general socket routines (which are used by the http
* code).
*
* Thanks to Karl Hakimian for pointing out that some platforms return EAGAIN
* not EWOULDBLOCK.
*/
/*
* Copyright (C) 2011-2012 Christian Beier <dontmind@freeshell.org>
* Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
* OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
* Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
* 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 __STRICT_ANSI__
#define _BSD_SOURCE
#ifdef __linux__
/* Setting this on other systems hides definitions such as INADDR_LOOPBACK.
* The check should be for __GLIBC__ in fact. */
# define _POSIX_SOURCE
#endif
#endif
#include <rfb/rfb.h>
#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#ifdef LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
#include "rfbssl.h"
#endif
#ifdef LIBVNCSERVER_WITH_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#if defined(__linux__) && defined(NEED_TIMEVAL)
struct timeval
{
long int tv_sec,tv_usec;
}
;
#endif
#ifdef LIBVNCSERVER_HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#ifdef USE_LIBWRAP
#include <syslog.h>
#include <tcpd.h>
int allow_severity=LOG_INFO;
int deny_severity=LOG_WARNING;
#endif
#if defined(WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef __MINGW32__
#pragma warning (disable: 4018 4761)
#endif
#define read(sock,buf,len) recv(sock,buf,len,0)
#define EWOULDBLOCK WSAEWOULDBLOCK
#define ETIMEDOUT WSAETIMEDOUT
#define write(sock,buf,len) send(sock,buf,len,0)
#else
#define closesocket close
#endif
#ifdef _MSC_VER
#define SHUT_RD 0x00
#define SHUT_WR 0x01
#define SHUT_RDWR 0x02
#define snprintf _snprintf /* Missing in MSVC */
#endif
int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has
gone away - needed to stop us hanging */
static rfbBool
rfbNewConnectionFromSock(rfbScreenInfoPtr rfbScreen, int sock)
{
const int one = 1;
#ifdef LIBVNCSERVER_IPv6
struct sockaddr_storage addr;
#else
struct sockaddr_in addr;
#endif
socklen_t addrlen = sizeof(addr);
getpeername(sock, (struct sockaddr *)&addr, &addrlen);
if(!rfbSetNonBlocking(sock)) {
rfbLogPerror("rfbCheckFds: setnonblock");
closesocket(sock);
return FALSE;
}
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one)) < 0) {
rfbLogPerror("rfbCheckFds: setsockopt failed: can't set TCP_NODELAY flag, non TCP socket?");
}
#ifdef USE_LIBWRAP
if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr),
STRING_UNKNOWN)) {
rfbLog("Rejected connection from client %s\n",
inet_ntoa(addr.sin_addr));
closesocket(sock);
return FALSE;
}
#endif
#ifdef LIBVNCSERVER_IPv6
char host[1024];
if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) {
rfbLogPerror("rfbProcessNewConnection: error in getnameinfo");
}
rfbLog("Got connection from client %s\n", host);
#else
rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr));
#endif
rfbNewClient(rfbScreen,sock);
return TRUE;
}
/*
* rfbInitSockets sets up the TCP and UDP sockets to listen for RFB
* connections. It does nothing if called again.
*/
void
rfbInitSockets(rfbScreenInfoPtr rfbScreen)
{
in_addr_t iface = rfbScreen->listenInterface;
if (rfbScreen->socketState == RFB_SOCKET_READY) {
return;
}
rfbScreen->socketState = RFB_SOCKET_READY;
#ifdef LIBVNCSERVER_WITH_SYSTEMD
if (sd_listen_fds(0) == 1)
{
int sock = SD_LISTEN_FDS_START + 0;
if (sd_is_socket(sock, AF_UNSPEC, 0, 0))
rfbNewConnectionFromSock(rfbScreen, sock);
else if (sd_is_socket(sock, AF_UNSPEC, 0, 1))
rfbProcessNewConnection(rfbScreen);
return;
}
else
rfbLog("Unable to establish connection with systemd socket\n");
#endif
if (rfbScreen->inetdSock != -1) {
const int one = 1;
if(!rfbSetNonBlocking(rfbScreen->inetdSock))
return;
if (setsockopt(rfbScreen->inetdSock, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one)) < 0) {
rfbLogPerror("setsockopt failed: can't set TCP_NODELAY flag, non TCP socket?");
}
FD_ZERO(&(rfbScreen->allFds));
FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbScreen->inetdSock;
return;
}
FD_ZERO(&(rfbScreen->allFds));
if(rfbScreen->autoPort && rfbScreen->port>0) {
int i;
rfbLog("Autoprobing TCP port \n");
for (i = 5900; i < 6000; i++) {
if ((rfbScreen->listenSock = rfbListenOnTCPPort(i, iface)) >= 0) {
rfbScreen->port = i;
break;
}
}
if (i >= 6000) {
rfbLogPerror("Failure autoprobing");
return;
}
rfbLog("Autoprobing selected TCP port %d\n", rfbScreen->port);
FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbScreen->listenSock;
}
#ifdef LIBVNCSERVER_IPv6
if(rfbScreen->autoPort && rfbScreen->ipv6port>0) {
int i;
rfbLog("Autoprobing TCP6 port \n");
for (i = 5900; i < 6000; i++) {
if ((rfbScreen->listen6Sock = rfbListenOnTCP6Port(i, rfbScreen->listen6Interface)) >= 0) {
rfbScreen->ipv6port = i;
break;
}
}
if (i >= 6000) {
rfbLogPerror("Failure autoprobing");
return;
}
rfbLog("Autoprobing selected TCP6 port %d\n", rfbScreen->ipv6port);
FD_SET(rfbScreen->listen6Sock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbMax((int)rfbScreen->listen6Sock,rfbScreen->maxFd);
}
#endif
if(!rfbScreen->autoPort) {
if(rfbScreen->port>0) {
if ((rfbScreen->listenSock = rfbListenOnTCPPort(rfbScreen->port, iface)) < 0) {
rfbLogPerror("ListenOnTCPPort");
return;
}
rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port);
FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbScreen->listenSock;
}
#ifdef LIBVNCSERVER_IPv6
if (rfbScreen->ipv6port>0) {
if ((rfbScreen->listen6Sock = rfbListenOnTCP6Port(rfbScreen->ipv6port, rfbScreen->listen6Interface)) < 0) {
/* ListenOnTCP6Port has its own detailed error printout */
return;
}
rfbLog("Listening for VNC connections on TCP6 port %d\n", rfbScreen->ipv6port);
FD_SET(rfbScreen->listen6Sock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbMax((int)rfbScreen->listen6Sock,rfbScreen->maxFd);
}
#endif
}
if (rfbScreen->udpPort != 0) {
rfbLog("rfbInitSockets: listening for input on UDP port %d\n",rfbScreen->udpPort);
if ((rfbScreen->udpSock = rfbListenOnUDPPort(rfbScreen->udpPort, iface)) < 0) {
rfbLogPerror("ListenOnUDPPort");
return;
}
rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port);
FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbMax((int)rfbScreen->udpSock,rfbScreen->maxFd);
}
}
void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen)
{
if (rfbScreen->socketState!=RFB_SOCKET_READY)
return;
rfbScreen->socketState = RFB_SOCKET_SHUTDOWN;
if(rfbScreen->inetdSock>-1) {
closesocket(rfbScreen->inetdSock);
FD_CLR(rfbScreen->inetdSock,&rfbScreen->allFds);
rfbScreen->inetdSock=-1;
}
if(rfbScreen->listenSock>-1) {
closesocket(rfbScreen->listenSock);
FD_CLR(rfbScreen->listenSock,&rfbScreen->allFds);
rfbScreen->listenSock=-1;
}
if(rfbScreen->listen6Sock>-1) {
closesocket(rfbScreen->listen6Sock);
FD_CLR(rfbScreen->listen6Sock,&rfbScreen->allFds);
rfbScreen->listen6Sock=-1;
}
if(rfbScreen->udpSock>-1) {
closesocket(rfbScreen->udpSock);
FD_CLR(rfbScreen->udpSock,&rfbScreen->allFds);
rfbScreen->udpSock=-1;
}
}
/*
* rfbCheckFds is called from ProcessInputEvents to check for input on the RFB
* socket(s). If there is input to process, the appropriate function in the
* RFB server code will be called (rfbNewClientConnection,
* rfbProcessClientMessage, etc).
*/
int
rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec)
{
int nfds;
fd_set fds;
struct timeval tv;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
char buf[6];
rfbClientIteratorPtr i;
rfbClientPtr cl;
int result = 0;
if (!rfbScreen->inetdInitDone && rfbScreen->inetdSock != -1) {
rfbNewClientConnection(rfbScreen,rfbScreen->inetdSock);
rfbScreen->inetdInitDone = TRUE;
}
do {
memcpy((char *)&fds, (char *)&(rfbScreen->allFds), sizeof(fd_set));
tv.tv_sec = 0;
tv.tv_usec = usec;
nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv);
if (nfds == 0) {
/* timed out, check for async events */
i = rfbGetClientIterator(rfbScreen);
while((cl = rfbClientIteratorNext(i))) {
if (cl->onHold)
continue;
if (FD_ISSET(cl->sock, &(rfbScreen->allFds)))
rfbSendFileTransferChunk(cl);
}
rfbReleaseClientIterator(i);
return result;
}
if (nfds < 0) {
#ifdef WIN32
errno = WSAGetLastError();
#endif
if (errno != EINTR)
rfbLogPerror("rfbCheckFds: select");
return -1;
}
result += nfds;
if (rfbScreen->listenSock != -1 && FD_ISSET(rfbScreen->listenSock, &fds)) {
if (!rfbProcessNewConnection(rfbScreen))
return -1;
FD_CLR(rfbScreen->listenSock, &fds);
if (--nfds == 0)
return result;
}
if (rfbScreen->listen6Sock != -1 && FD_ISSET(rfbScreen->listen6Sock, &fds)) {
if (!rfbProcessNewConnection(rfbScreen))
return -1;
FD_CLR(rfbScreen->listen6Sock, &fds);
if (--nfds == 0)
return result;
}
if ((rfbScreen->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) {
if(!rfbScreen->udpClient)
rfbNewUDPClient(rfbScreen);
if (recvfrom(rfbScreen->udpSock, buf, 1, MSG_PEEK,
(struct sockaddr *)&addr, &addrlen) < 0) {
rfbLogPerror("rfbCheckFds: UDP: recvfrom");
rfbDisconnectUDPSock(rfbScreen);
rfbScreen->udpSockConnected = FALSE;
} else {
if (!rfbScreen->udpSockConnected ||
(memcmp(&addr, &rfbScreen->udpRemoteAddr, addrlen) != 0))
{
/* new remote end */
rfbLog("rfbCheckFds: UDP: got connection\n");
memcpy(&rfbScreen->udpRemoteAddr, &addr, addrlen);
rfbScreen->udpSockConnected = TRUE;
if (connect(rfbScreen->udpSock,
(struct sockaddr *)&addr, addrlen) < 0) {
rfbLogPerror("rfbCheckFds: UDP: connect");
rfbDisconnectUDPSock(rfbScreen);
return -1;
}
rfbNewUDPConnection(rfbScreen,rfbScreen->udpSock);
}
rfbProcessUDPInput(rfbScreen);
}
FD_CLR(rfbScreen->udpSock, &fds);
if (--nfds == 0)
return result;
}
i = rfbGetClientIterator(rfbScreen);
while((cl = rfbClientIteratorNext(i))) {
if (cl->onHold)
continue;
if (FD_ISSET(cl->sock, &(rfbScreen->allFds)))
{
if (FD_ISSET(cl->sock, &fds))
{
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
do {
rfbProcessClientMessage(cl);
} while (cl->sock > 0 && webSocketsHasDataInBuffer(cl));
#else
rfbProcessClientMessage(cl);
#endif
}
else
rfbSendFileTransferChunk(cl);
}
}
rfbReleaseClientIterator(i);
} while(rfbScreen->handleEventsEagerly);
return result;
}
rfbBool
rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen)
{
int sock = -1;
fd_set listen_fds;
int chosen_listen_sock = -1;
/* Do another select() call to find out which listen socket
has an incoming connection pending. We know that at least
one of them has, so this should not block for too long! */
FD_ZERO(&listen_fds);
if(rfbScreen->listenSock >= 0)
FD_SET(rfbScreen->listenSock, &listen_fds);
if(rfbScreen->listen6Sock >= 0)
FD_SET(rfbScreen->listen6Sock, &listen_fds);
if (select(rfbScreen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) {
rfbLogPerror("rfbProcessNewConnection: error in select");
return FALSE;
}
if (rfbScreen->listenSock >= 0 && FD_ISSET(rfbScreen->listenSock, &listen_fds))
chosen_listen_sock = rfbScreen->listenSock;
if (rfbScreen->listen6Sock >= 0 && FD_ISSET(rfbScreen->listen6Sock, &listen_fds))
chosen_listen_sock = rfbScreen->listen6Sock;
if ((sock = accept(chosen_listen_sock, NULL, NULL)) < 0) {
rfbLogPerror("rfbCheckFds: accept");
return FALSE;
}
return rfbNewConnectionFromSock(rfbScreen, sock);
}
void
rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen)
{
rfbScreen->udpSockConnected = FALSE;
}
void
rfbCloseClient(rfbClientPtr cl)
{
rfbExtensionData* extension;
for(extension=cl->extensions; extension; extension=extension->next)
if(extension->extension->close)
extension->extension->close(cl, extension->data);
LOCK(cl->updateMutex);
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
if (cl->sock != -1)
#endif
{
FD_CLR(cl->sock,&(cl->screen->allFds));
if(cl->sock==cl->screen->maxFd)
while(cl->screen->maxFd>0
&& !FD_ISSET(cl->screen->maxFd,&(cl->screen->allFds)))
cl->screen->maxFd--;
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->sslctx)
rfbssl_destroy(cl);
free(cl->wspath);
#endif
#ifndef __MINGW32__
shutdown(cl->sock,SHUT_RDWR);
#endif
closesocket(cl->sock);
cl->sock = -1;
}
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}
/*
* rfbConnect is called to make a connection out to a given TCP address.
*/
int
rfbConnect(rfbScreenInfoPtr rfbScreen,
char *host,
int port)
{
int sock;
int one = 1;
rfbLog("Making connection to client on host %s port %d\n",
host,port);
if ((sock = rfbConnectToTcpAddr(host, port)) < 0) {
rfbLogPerror("connection failed");
return -1;
}
if(!rfbSetNonBlocking(sock)) {
closesocket(sock);
return -1;
}
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one)) < 0) {
rfbLogPerror("setsockopt failed: can't set TCP_NODELAY flag, non TCP socket?");
}
/* AddEnabledDevice(sock); */
FD_SET(sock, &rfbScreen->allFds);
rfbScreen->maxFd = rfbMax(sock,rfbScreen->maxFd);
return sock;
}
/*
* ReadExact reads an exact number of bytes from a client. Returns 1 if
* those bytes have been read, 0 if the other end has closed, or -1 if an error
* occurred (errno is set to ETIMEDOUT if it timed out).
*/
int
rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
{
int sock = cl->sock;
int n;
fd_set fds;
struct timeval tv;
while (len > 0) {
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->wsctx) {
n = webSocketsDecode(cl, buf, len);
} else if (cl->sslctx) {
n = rfbssl_read(cl, buf, len);
} else {
n = read(sock, buf, len);
}
#else
n = read(sock, buf, len);
#endif
if (n > 0) {
buf += n;
len -= n;
} else if (n == 0) {
return 0;
} else {
#ifdef WIN32
errno = WSAGetLastError();
#endif
if (errno == EINTR)
continue;
#ifdef LIBVNCSERVER_ENOENT_WORKAROUND
if (errno != ENOENT)
#endif
if (errno != EWOULDBLOCK && errno != EAGAIN) {
return n;
}
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->sslctx) {
if (rfbssl_pending(cl))
continue;
}
#endif
FD_ZERO(&fds);
FD_SET(sock, &fds);
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
n = select(sock+1, &fds, NULL, &fds, &tv);
if (n < 0) {
rfbLogPerror("ReadExact: select");
return n;
}
if (n == 0) {
rfbErr("ReadExact: select timeout\n");
errno = ETIMEDOUT;
return -1;
}
}
}
#undef DEBUG_READ_EXACT
#ifdef DEBUG_READ_EXACT
rfbLog("ReadExact %d bytes\n",len);
for(n=0;n<len;n++)
fprintf(stderr,"%02x ",(unsigned char)buf[n]);
fprintf(stderr,"\n");
#endif
return 1;
}
int rfbReadExact(rfbClientPtr cl,char* buf,int len)
{
/* favor the per-screen value if set */
if(cl->screen && cl->screen->maxClientWait)
return(rfbReadExactTimeout(cl,buf,len,cl->screen->maxClientWait));
else
return(rfbReadExactTimeout(cl,buf,len,rfbMaxClientWait));
}
/*
* PeekExact peeks at an exact number of bytes from a client. Returns 1 if
* those bytes have been read, 0 if the other end has closed, or -1 if an
* error occurred (errno is set to ETIMEDOUT if it timed out).
*/
int
rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
{
int sock = cl->sock;
int n;
fd_set fds;
struct timeval tv;
while (len > 0) {
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->sslctx)
n = rfbssl_peek(cl, buf, len);
else
#endif
n = recv(sock, buf, len, MSG_PEEK);
if (n == len) {
break;
} else if (n == 0) {
return 0;
} else {
#ifdef WIN32
errno = WSAGetLastError();
#endif
if (errno == EINTR)
continue;
#ifdef LIBVNCSERVER_ENOENT_WORKAROUND
if (errno != ENOENT)
#endif
if (errno != EWOULDBLOCK && errno != EAGAIN) {
return n;
}
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->sslctx) {
if (rfbssl_pending(cl))
continue;
}
#endif
FD_ZERO(&fds);
FD_SET(sock, &fds);
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
n = select(sock+1, &fds, NULL, &fds, &tv);
if (n < 0) {
rfbLogPerror("PeekExact: select");
return n;
}
if (n == 0) {
errno = ETIMEDOUT;
return -1;
}
}
}
#undef DEBUG_READ_EXACT
#ifdef DEBUG_READ_EXACT
rfbLog("PeekExact %d bytes\n",len);
for(n=0;n<len;n++)
fprintf(stderr,"%02x ",(unsigned char)buf[n]);
fprintf(stderr,"\n");
#endif
return 1;
}
/*
* WriteExact writes an exact number of bytes to a client. Returns 1 if
* those bytes have been written, or -1 if an error occurred (errno is set to
* ETIMEDOUT if it timed out).
*/
int
rfbWriteExact(rfbClientPtr cl,
const char *buf,
int len)
{
int sock = cl->sock;
int n;
fd_set fds;
struct timeval tv;
int totalTimeWaited = 0;
const int timeout = (cl->screen && cl->screen->maxClientWait) ? cl->screen->maxClientWait : rfbMaxClientWait;
#undef DEBUG_WRITE_EXACT
#ifdef DEBUG_WRITE_EXACT
rfbLog("WriteExact %d bytes\n",len);
for(n=0;n<len;n++)
fprintf(stderr,"%02x ",(unsigned char)buf[n]);
fprintf(stderr,"\n");
#endif
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->wsctx) {
char *tmp = NULL;
if ((len = webSocketsEncode(cl, buf, len, &tmp)) < 0) {
rfbErr("WriteExact: WebSockets encode error\n");
return -1;
}
buf = tmp;
}
#endif
LOCK(cl->outputMutex);
while (len > 0) {
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
if (cl->sslctx)
n = rfbssl_write(cl, buf, len);
else
#endif
n = write(sock, buf, len);
if (n > 0) {
buf += n;
len -= n;
} else if (n == 0) {
rfbErr("WriteExact: write returned 0?\n");
return 0;
} else {
#ifdef WIN32
errno = WSAGetLastError();
#endif
if (errno == EINTR)
continue;
if (errno != EWOULDBLOCK && errno != EAGAIN) {
UNLOCK(cl->outputMutex);
return n;
}
/* Retry every 5 seconds until we exceed timeout. We
need to do this because select doesn't necessarily return
immediately when the other end has gone away */
FD_ZERO(&fds);
FD_SET(sock, &fds);
tv.tv_sec = 5;
tv.tv_usec = 0;
n = select(sock+1, NULL, &fds, NULL /* &fds */, &tv);
if (n < 0) {
#ifdef WIN32
errno=WSAGetLastError();
#endif
if(errno==EINTR)
continue;
rfbLogPerror("WriteExact: select");
UNLOCK(cl->outputMutex);
return n;
}
if (n == 0) {
totalTimeWaited += 5000;
if (totalTimeWaited >= timeout) {
errno = ETIMEDOUT;
UNLOCK(cl->outputMutex);
return -1;
}
} else {
totalTimeWaited = 0;
}
}
}
UNLOCK(cl->outputMutex);
return 1;
}
/* currently private, called by rfbProcessArguments() */
int
rfbStringToAddr(char *str, in_addr_t *addr) {
if (str == NULL || *str == '\0' || strcmp(str, "any") == 0) {
*addr = htonl(INADDR_ANY);
} else if (strcmp(str, "localhost") == 0) {
*addr = htonl(INADDR_LOOPBACK);
} else {
struct hostent *hp;
if ((*addr = inet_addr(str)) == htonl(INADDR_NONE)) {
if (!(hp = gethostbyname(str))) {
return 0;
}
*addr = *(unsigned long *)hp->h_addr;
}
}
return 1;
}
int
rfbListenOnTCPPort(int port,
in_addr_t iface)
{
struct sockaddr_in addr;
int sock;
int one = 1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = iface;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)) < 0) {
closesocket(sock);
return -1;
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
closesocket(sock);
return -1;
}
if (listen(sock, 32) < 0) {
closesocket(sock);
return -1;
}
return sock;
}
int
rfbListenOnTCP6Port(int port,
const char* iface)
{
#ifndef LIBVNCSERVER_IPv6
rfbLogPerror("This LibVNCServer does not have IPv6 support");
return -1;
#else
int sock;
int one = 1;
int rv;
struct addrinfo hints, *servinfo, *p;
char port_str[8];
snprintf(port_str, 8, "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; /* fill in wildcard address if iface == NULL */
if ((rv = getaddrinfo(iface, port_str, &hints, &servinfo)) != 0) {
rfbErr("rfbListenOnTCP6Port error in getaddrinfo: %s\n", gai_strerror(rv));
return -1;
}
/* loop through all the results and bind to the first we can */
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
continue;
}
#ifdef IPV6_V6ONLY
/* we have separate IPv4 and IPv6 sockets since some OS's do not support dual binding */
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) {
rfbLogPerror("rfbListenOnTCP6Port error in setsockopt IPV6_V6ONLY");
closesocket(sock);
freeaddrinfo(servinfo);
return -1;
}
#endif
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) {
rfbLogPerror("rfbListenOnTCP6Port: error in setsockopt SO_REUSEADDR");
closesocket(sock);
freeaddrinfo(servinfo);
return -1;
}
if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) {
closesocket(sock);
continue;
}
break;
}
if (p == NULL) {
rfbLogPerror("rfbListenOnTCP6Port: error in bind IPv6 socket");
freeaddrinfo(servinfo);
return -1;
}
/* all done with this structure now */
freeaddrinfo(servinfo);
if (listen(sock, 32) < 0) {
rfbLogPerror("rfbListenOnTCP6Port: error in listen on IPv6 socket");
closesocket(sock);
return -1;
}
return sock;
#endif
}
int
rfbConnectToTcpAddr(char *host,
int port)
{
int sock;
#ifdef LIBVNCSERVER_IPv6
struct addrinfo hints, *servinfo, *p;
int rv;
char port_str[8];
snprintf(port_str, 8, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) {
rfbErr("rfbConnectToTcpAddr: error in getaddrinfo: %s\n", gai_strerror(rv));
return -1;
}
/* loop through all the results and connect to the first we can */
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue;
if (connect(sock, p->ai_addr, p->ai_addrlen) < 0) {
closesocket(sock);
continue;
}
break;
}
/* all failed */
if (p == NULL) {
rfbLogPerror("rfbConnectToTcoAddr: failed to connect\n");
sock = -1; /* set return value */
}
/* all done with this structure now */
freeaddrinfo(servinfo);
#else
struct hostent *hp;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if ((addr.sin_addr.s_addr = inet_addr(host)) == htonl(INADDR_NONE))
{
if (!(hp = gethostbyname(host))) {
errno = EINVAL;
return -1;
}
addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr;
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return -1;
}
if (connect(sock, (struct sockaddr *)&addr, (sizeof(addr))) < 0) {
closesocket(sock);
return -1;
}
#endif
return sock;
}
int
rfbListenOnUDPPort(int port,
in_addr_t iface)
{
struct sockaddr_in addr;
int sock;
int one = 1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = iface;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)) < 0) {
return -1;
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return -1;
}
return sock;
}
/*
* rfbSetNonBlocking sets a socket into non-blocking mode.
*/
rfbBool
rfbSetNonBlocking(int sock)
{
#ifdef WIN32
unsigned long block=1;
if(ioctlsocket(sock, FIONBIO, &block) == SOCKET_ERROR) {
errno=WSAGetLastError();
#else
int flags = fcntl(sock, F_GETFL);
if(flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
#endif
rfbLogPerror("Setting socket to non-blocking failed");
return FALSE;
}
return TRUE;
}