int listen_tcp(int port, in_addr_t iface, int try6) { int fd = -1; int fail4 = noipv4; if (getenv("IPV4_FAILS")) { fail4 = 2; } if (port <= 0 || 65535 < port) { /* for us, invalid port means do not listen. */ return -1; } if (fail4) { if (fail4 > 1) { rfbLog("TESTING: IPV4_FAILS for listen_tcp: port=%d try6=%d\n", port, try6); } } else { fd = rfbListenOnTCPPort(port, iface); } if (fd >= 0) { return fd; } if (fail4 > 1) { rfbLogPerror("listen_tcp: listen failed"); } if (fd < 0 && try6 && ipv6_listen && !noipv6) { #if X11VNC_IPV6 char *save = listen_str6; if (iface == htonl(INADDR_LOOPBACK)) { listen_str6 = "localhost"; rfbLog("listen_tcp: retrying on IPv6 in6addr_loopback ...\n"); fd = listen6(port); } else if (iface == htonl(INADDR_ANY)) { listen_str6 = NULL; rfbLog("listen_tcp: retrying on IPv6 in6addr_any ...\n"); fd = listen6(port); } listen_str6 = save; #endif } return fd; }
//! handle matrix update message rfbBool VncServer::handleMatricesMessage(rfbClientPtr cl, void *data, const rfbClientToServerMsg *message) { if (message->type != rfbMatrices) return FALSE; matricesMsg msg; int n = rfbReadExact(cl, ((char *)&msg)+1, sizeof(msg)-1); if (n <= 0) { if (n!= 0) rfbLogPerror("handleMatricesMessage: read"); rfbCloseClient(cl); return TRUE; } size_t viewNum = msg.viewNum >= 0 ? msg.viewNum : 0; if (viewNum >= plugin->m_viewData.size()) { plugin->m_viewData.resize(viewNum+1); } plugin->resize(viewNum, msg.width, msg.height); ViewData &vd = plugin->m_viewData[viewNum]; vd.nparam.matrixTime = msg.time; vd.nparam.requestNumber = msg.requestNumber; for (int i=0; i<16; ++i) { vd.nparam.proj.data()[i] = msg.proj[i]; vd.nparam.view.data()[i] = msg.view[i]; vd.nparam.model.data()[i] = msg.model[i]; } //std::cerr << "handleMatrices: view " << msg.viewNum << ", proj: " << vd.nparam.proj << std::endl; if (msg.last) { for (int i=0; i<plugin->numViews(); ++i) { plugin->m_viewData[i].param = plugin->m_viewData[i].nparam; } } return TRUE; }
int SkipExact(rfbClientPtr cl, int len) { char *tmpbuf = NULL; int bufLen = min(len, 65536), i, retval = 1; tmpbuf = (char *)malloc(bufLen); if (tmpbuf == NULL) { rfbLogPerror("SkipExact: out of memory"); return -1; } for (i = 0; i < len; i += bufLen) { retval = ReadExact(cl, tmpbuf, min(bufLen, len - i)); if (retval <= 0) break; } free(tmpbuf); return retval; }
static void* listenerRun(void *data) { rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data; int client_fd; struct sockaddr_storage peer; rfbClientPtr cl = NULL; socklen_t len; fd_set listen_fds; /* temp file descriptor list for select() */ /* TODO: this thread wont die by restarting the server */ /* TODO: HTTP is not handled */ while (1) { client_fd = -1; FD_ZERO(&listen_fds); if(screen->listenSock >= 0) FD_SET(screen->listenSock, &listen_fds); if(screen->listen6Sock >= 0) FD_SET(screen->listen6Sock, &listen_fds); if (select(screen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) { rfbLogPerror("listenerRun: error in select"); return NULL; } /* there is something on the listening sockets, handle new connections */ len = sizeof (peer); if (FD_ISSET(screen->listenSock, &listen_fds)) client_fd = accept(screen->listenSock, (struct sockaddr*)&peer, &len); else if (FD_ISSET(screen->listen6Sock, &listen_fds)) client_fd = accept(screen->listen6Sock, (struct sockaddr*)&peer, &len); if(client_fd >= 0) cl = rfbNewClient(screen,client_fd); if (cl && !cl->onHold ) rfbStartOnHoldClient(cl); } return(NULL); }
static char *getsslscript(char *cdir, char *name, char *script) { char *openssl = find_openssl_bin(); char *tmp, *scr, *cdir_use; FILE *out; if (! openssl || openssl[0] == '\0') { exit(1); } if (!name || !script) { exit(1); } cdir_use = get_Cert_dir(cdir, &tmp); if (!cdir_use || !tmp) { exit(1); } scr = (char *) malloc(strlen(tmp) + 1 + strlen(name) + 30); sprintf(scr, "%s/%s.%d.sh", tmp, name, getpid()); out = fopen(scr, "w"); if (! out) { rfbLog("could not open: %s\n", scr); rfbLogPerror("fopen"); exit(1); } fprintf(out, "%s", script); fclose(out); rfbLog("Using openssl: %s\n", openssl); rfbLog("Using certs dir: %s\n", cdir_use); fprintf(stderr, "\n"); set_env("BASE_DIR", cdir_use); set_env("OPENSSL", openssl); return scr; }
int VNCServer::Client::Connect(WebSocket *ws) { Debug(">VNCServer::Client::Connect [ws:%p,this:%p]\n",ws,this); //Store websocekt this->ws = ws; rfbProtocolVersionMsg pv; sprintf(pv,rfbProtocolVersionFormat,cl->screen->protocolMajorVersion,cl->screen->protocolMinorVersion); //Write protocol version if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { rfbLogPerror("rfbNewClient: write"); rfbCloseClient(cl); rfbClientConnectionGone(cl); return Error("-Could not write protocol version"); } //Enable extension for(rfbProtocolExtension* extension = rfbGetExtensionIterator(); extension; extension=extension->next) { void* data = NULL; /* if the extension does not have a newClient method, it wants * to be initialized later. */ if(extension->newClient && extension->newClient(cl, &data)) rfbEnableExtension(cl, extension, data); } rfbReleaseExtensionIterator(); cl->onHold = FALSE; //Start thread createPriorityThread(&thread,run,this,0); Debug("<VNCServer::Client::Connect [ws:%p,this:%p]\n",ws,this); //OK return 1; }
static void rfbSendTunnelingCaps(rfbClientPtr cl) { rfbTunnelingCapsMsg caps; uint32_t nTypes = 0; /* we don't support tunneling yet */ rfbLog("tightvnc-filetransfer/rfbSendTunnelingCaps\n"); caps.nTunnelTypes = Swap32IfLE(nTypes); if (rfbWriteExact(cl, (char *)&caps, sz_rfbTunnelingCapsMsg) < 0) { rfbLogPerror("rfbSendTunnelingCaps: write"); rfbCloseClient(cl); return; } if (nTypes) { /* Dispatch client input to rfbProcessClientTunnelingType(). */ /* The flow should not reach here as tunneling is not implemented. */ rfbProcessClientTunnelingType(cl); } else { rfbSendAuthCaps(cl); } }
void rfbHttpInitSockets(rfbScreenInfoPtr rfbScreen) { if (rfbScreen->httpInitDone) return; rfbScreen->httpInitDone = TRUE; if (!rfbScreen->httpDir) return; if (rfbScreen->httpPort == 0) { rfbScreen->httpPort = rfbScreen->port-100; } if ((rfbScreen->httpListenSock = rfbListenOnTCPPort(rfbScreen->httpPort, rfbScreen->listenInterface)) < 0) { rfbLogPerror("ListenOnTCPPort"); return; } rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); rfbLog(" URL http://%s:%d\n",rfbScreen->thisHost,rfbScreen->httpPort); #ifdef LIBVNCSERVER_IPv6 if (rfbScreen->http6Port == 0) { rfbScreen->http6Port = rfbScreen->ipv6port-100; } if ((rfbScreen->httpListen6Sock = rfbListenOnTCP6Port(rfbScreen->http6Port, rfbScreen->listen6Interface)) < 0) { /* ListenOnTCP6Port has its own detailed error printout */ return; } rfbLog("Listening for HTTP connections on TCP6 port %d\n", rfbScreen->http6Port); rfbLog(" URL http://%s:%d\n",rfbScreen->thisHost,rfbScreen->http6Port); #endif }
static void rfbVncAuthNone(rfbClientPtr cl) { /* The built-in Mac OS X VNC client behaves in a non-conforming fashion * when the server version is 3.7 or later AND the list of security types * sent to the OS X client contains the 'None' authentication type AND * the OS X client sends back the 'None' type as its choice. In this case, * and this case ONLY, the built-in Mac OS X VNC client will NOT send the * ClientInit message and instead will behave as though an implicit * ClientInit message containing a shared-flag of true has been sent. * The special state RFB_INITIALISATION_SHARED represents this case. * The Mac OS X VNC client can be detected by checking protocolMinorVersion * for a value of 889. No other VNC client is known to use this value * for protocolMinorVersion. */ uint32_t authResult; /* The built-in Mac OS X VNC client expects to NOT receive a SecurityResult * message for authentication type 'None'. Since its protocolMinorVersion * is greater than 7 (it is 889) this case must be tested for specially. */ if (cl->protocolMajorVersion==3 && cl->protocolMinorVersion > 7 && cl->protocolMinorVersion != 889) { rfbLog("rfbProcessClientSecurityType: returning securityResult for client rfb version >= 3.8\n"); authResult = Swap32IfLE(rfbVncAuthOK); if (rfbWriteExact(cl, (char *)&authResult, 4) < 0) { rfbLogPerror("rfbAuthProcessClientMessage: write"); rfbCloseClient(cl); return; } } cl->state = cl->protocolMinorVersion == 889 ? RFB_INITIALISATION_SHARED : RFB_INITIALISATION; if (cl->state == RFB_INITIALISATION_SHARED) /* In this case we must call rfbProcessClientMessage now because * otherwise we would hang waiting for data to be received from the * client (the ClientInit message which will never come). */ rfbProcessClientMessage(cl); return; }
//! handle generic application message rfbBool VncServer::handleApplicationMessage(rfbClientPtr cl, void *data, const rfbClientToServerMsg *message) { if (message->type != rfbApplication) return FALSE; if (!cl->clientData) { cl->clientData = new ClientData(); appAnimationTimestep app; app.current = plugin->m_imageParam.timestep; app.total = plugin->m_numTimesteps; sendApplicationMessage(cl, rfbAnimationTimestep, sizeof(app), (char *)&app); } ClientData *cd = static_cast<ClientData *>(cl->clientData); cd->supportsApplication = true; applicationMsg msg; int n = rfbReadExact(cl, ((char *)&msg)+1, sizeof(msg)-1); if (n <= 0) { if (n!= 0) rfbLogPerror("handleApplicationMessage: read"); rfbCloseClient(cl); return TRUE; } std::vector<char> buf(msg.size); n = rfbReadExact(cl, &buf[0], msg.size); if (n <= 0) { if (n!= 0) rfbLogPerror("handleApplicationMessage: read data"); rfbCloseClient(cl); return TRUE; } if (plugin->m_appHandler) { bool handled = plugin->m_appHandler(msg.appType, buf); if (handled) return TRUE; } switch (msg.appType) { case rfbScreenConfig: { appScreenConfig app; memcpy(&app, &buf[0], sizeof(app)); plugin->resize(0, app.width, app.height); plugin->m_screenConfig.hsize = app.hsize; plugin->m_screenConfig.vsize = app.vsize; for (int i=0; i<3; ++i) { plugin->m_screenConfig.pos[i] = app.screenPos[i]; plugin->m_screenConfig.hpr[i] = app.screenRot[i]; } } break; case rfbFeedback: { // not needed: handled via vistle connection } break; case rfbAnimationTimestep: { appAnimationTimestep app; memcpy(&app, &buf[0], sizeof(app)); plugin->m_imageParam.timestep = app.current; } break; } return TRUE; }
void rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen) { int nfds; fd_set fds; struct timeval tv; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); if (!rfbScreen->httpDir) return; if (rfbScreen->httpListenSock < 0) return; FD_ZERO(&fds); FD_SET(rfbScreen->httpListenSock, &fds); if (rfbScreen->httpSock >= 0) { FD_SET(rfbScreen->httpSock, &fds); } tv.tv_sec = 0; tv.tv_usec = 0; nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv); if (nfds == 0) { return; } if (nfds < 0) { #ifdef WIN32 errno = WSAGetLastError(); #endif if (errno != EINTR) rfbLogPerror("httpCheckFds: select"); return; } if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) { httpProcessInput(rfbScreen); } if (FD_ISSET(rfbScreen->httpListenSock, &fds)) { if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("httpCheckFds: accept"); return; } #ifdef USE_LIBWRAP if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), STRING_UNKNOWN)) { rfbLog("Rejected HTTP connection from client %s\n", inet_ntoa(addr.sin_addr)); close(rfbScreen->httpSock); rfbScreen->httpSock=-1; return; } #endif if(!rfbSetNonBlocking(rfbScreen->httpSock)) { close(rfbScreen->httpSock); rfbScreen->httpSock=-1; return; } /*AddEnabledDevice(httpSock);*/ } }
void VncServer::encodeAndSend(int viewNum, int x0, int y0, int w, int h, const VncServer::ViewParameters ¶m, bool lastView) { //std::cerr << "encodeAndSend: view=" << viewNum << ", c=" << (void *)rgba(viewNum) << ", d=" << depth(viewNum) << std::endl; if (!m_resizeBlocked) { m_firstTile = true; } m_resizeBlocked = true; //vistle::StopWatch timer("encodeAndSend"); const int tileWidth = m_tileWidth, tileHeight = m_tileHeight; static int framecount=0; ++framecount; for (int y=y0; y<y0+h; y+=tileHeight) { for (int x=x0; x<x0+w; x+=tileWidth) { // depth auto dt = new(tbb::task::allocate_root()) EncodeTask(m_resultQueue, viewNum, x, y, std::min(tileWidth, x0+w-x), std::min(tileHeight, y0+h-y), depth(viewNum), m_imageParam, param); tbb::task::enqueue(*dt); ++m_queuedTiles; // color auto ct = new(tbb::task::allocate_root()) EncodeTask(m_resultQueue, viewNum, x, y, std::min(tileWidth, x0+w-x), std::min(tileHeight, y0+h-y), rgba(viewNum), m_imageParam, param); tbb::task::enqueue(*ct); ++m_queuedTiles; } } bool tileReady = false; do { VncServer::EncodeResult result; tileReady = false; if (m_resultQueue.try_pop(result)) { --m_queuedTiles; tileReady = true; if (result.message) { tileMsg &msg = *result.message; if (m_firstTile) { msg.flags |= rfbTileFirst; //std::cerr << "first tile: req=" << msg.requestNumber << std::endl; } m_firstTile = false; if (m_queuedTiles == 0 && lastView) { msg.flags |= rfbTileLast; //std::cerr << "last tile: req=" << msg.requestNumber << std::endl; } msg.frameNumber = framecount; rfbCheckFds(m_screen, 0); rfbHttpCheckFds(m_screen); rfbClientIteratorPtr i = rfbGetClientIterator(m_screen); while (rfbClientPtr cl = rfbClientIteratorNext(i)) { if (cl->clientData) { rfbUpdateClient(cl); if (rfbWriteExact(cl, (char *)&msg, sizeof(msg)) < 0) { rfbLogPerror("sendTileMessage: write header"); } if (result.payload && msg.size > 0) { if (rfbWriteExact(cl, result.payload, msg.size) < 0) { rfbLogPerror("sendTileMessage: write paylod"); } } } rfbUpdateClient(cl); } rfbReleaseClientIterator(i); } delete[] result.payload; delete result.message; } } while (m_queuedTiles > 0 && (tileReady || lastView)); if (lastView) { vassert(m_queuedTiles == 0); m_resizeBlocked = false; deferredResize(); } //sleep(1); }
void rfbInitSockets(rfbScreenInfoPtr rfbScreen) { in_addr_t iface = rfbScreen->listenInterface; if (rfbScreen->socketState == RFB_SOCKET_READY) { return; } rfbScreen->socketState = RFB_SOCKET_READY; 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"); return; } FD_ZERO(&(rfbScreen->allFds)); FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->inetdSock; return; } if(rfbScreen->autoPort) { int i; FD_ZERO(&(rfbScreen->allFds)); 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 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 = max((int)rfbScreen->listen6Sock,rfbScreen->maxFd); #endif } else { if(rfbScreen->port>0) { FD_ZERO(&(rfbScreen->allFds)); 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 = max((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 = max((int)rfbScreen->udpSock,rfbScreen->maxFd); } }
static void * clientInput(void *data) { rfbClientPtr cl = (rfbClientPtr)data; pthread_t output_thread; pthread_create(&output_thread, NULL, clientOutput, (void *)cl); while (1) { fd_set rfds, wfds, efds; struct timeval tv; int n; if (cl->sock == -1) { /* Client has disconnected. */ break; } FD_ZERO(&rfds); FD_SET(cl->sock, &rfds); FD_ZERO(&efds); FD_SET(cl->sock, &efds); /* Are we transferring a file in the background? */ FD_ZERO(&wfds); if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1)) FD_SET(cl->sock, &wfds); tv.tv_sec = 60; /* 1 minute */ tv.tv_usec = 0; n = select(cl->sock + 1, &rfds, &wfds, &efds, &tv); if (n < 0) { rfbLogPerror("ReadExact: select"); break; } if (n == 0) /* timeout */ { rfbSendFileTransferChunk(cl); continue; } /* We have some space on the transmit queue, send some data */ if (FD_ISSET(cl->sock, &wfds)) rfbSendFileTransferChunk(cl); if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds)) { #ifdef LIBVNCSERVER_WITH_WEBSOCKETS do { rfbProcessClientMessage(cl); } while (webSocketsHasDataInBuffer(cl)); #else rfbProcessClientMessage(cl); #endif } } /* Get rid of the output thread. */ LOCK(cl->updateMutex); TSIGNAL(cl->updateCond); UNLOCK(cl->updateMutex); IF_PTHREADS(pthread_join(output_thread, NULL)); rfbClientConnectionGone(cl); return NULL; }
static void AuthPAMUserPwdRspFunc(rfbClientPtr cl) { CARD32 userLen; CARD32 pwdLen; char userBuf[MAX_USER_LEN + 1]; char pwdBuf[MAX_PWD_LEN + 1]; int n; const char* emsg; n = ReadExact(cl->sock, (char*) &userLen, sizeof(userLen)); if (n <= 0) { if (n != 0) rfbLogPerror("AuthPAMUserPwdRspFunc: read error"); rfbCloseSock(cl->sock); return; } userLen = Swap32IfLE(userLen); n = ReadExact(cl->sock, (char*) &pwdLen, sizeof(pwdLen)); if (n <= 0) { if (n != 0) rfbLogPerror("AuthPAMUserPwdRspFunc: read error"); rfbCloseSock(cl->sock); return; } pwdLen = Swap32IfLE(pwdLen); if ((userLen > MAX_USER_LEN) || (pwdLen > MAX_PWD_LEN)) { rfbLogPerror("AuthPAMUserPwdRspFunc: excessively large user name or password in response"); rfbCloseSock(cl->sock); return; } n = ReadExact(cl->sock, userBuf, userLen); if (n <= 0) { if (n != 0) rfbLogPerror("AuthPAMUserPwdRspFunc: error reading user name"); rfbCloseSock(cl->sock); return; } userBuf[userLen] = '\0'; n = ReadExact(cl->sock, pwdBuf, pwdLen); if (n <= 0) { if (n != 0) rfbLogPerror("AuthPAMUserPwdRspFunc: error reading password"); rfbCloseSock(cl->sock); return; } pwdBuf[pwdLen] = '\0'; if (rfbAuthUserACL) { UserList* p = userACL; if (p == NULL) rfbLog("WARNING: User ACL is empty. No users will be allowed to log in with Unix login authentication.\n"); while (p != NULL) { if (!strcmp(p->name, userBuf)) break; p = p->next; } if (p == NULL) { rfbLog("User '%s' is not in the ACL and has been denied access\n", userBuf); rfbClientAuthFailed(cl, "User denied access"); return; } cl->viewOnly = p->viewOnly; } if (rfbPAMAuthenticate(pamServiceName, cl->host, userBuf, pwdBuf, &emsg)) { rfbClientAuthSucceeded(cl, rfbAuthUnixLogin); } else { rfbClientAuthFailed(cl, (char*)emsg); } }
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)) rfbProcessClientMessage(cl); else rfbSendFileTransferChunk(cl); } } rfbReleaseClientIterator(i); } while(rfbScreen->handleEventsEagerly); return result; }
int connect_tcp(char *host, int port) { double t0 = dnow(); int fd = -1; int fail4 = noipv4; if (getenv("IPV4_FAILS")) { fail4 = 2; } rfbLog("connect_tcp: trying: %s %d\n", host, port); if (fail4) { if (fail4 > 1) { rfbLog("TESTING: IPV4_FAILS for connect_tcp.\n"); } } else { fd = rfbConnectToTcpAddr(host, port); } if (fd >= 0) { return fd; } rfbLogPerror("connect_tcp: connection failed"); if (dnow() - t0 < 4.0) { rfbLog("connect_tcp: re-trying %s %d\n", host, port); usleep (100 * 1000); if (!fail4) { fd = rfbConnectToTcpAddr(host, port); } if (fd < 0) { rfbLogPerror("connect_tcp: connection failed"); } } if (fd < 0 && !noipv6) { #if X11VNC_IPV6 int err; struct addrinfo *ai; struct addrinfo hints; char service[32], *host2, *q; rfbLog("connect_tcp: trying IPv6 %s %d\n", host, port); memset(&hints, 0, sizeof(hints)); sprintf(service, "%d", port); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif if(ipv6_ip(host)) { #ifdef AI_NUMERICHOST rfbLog("connect_tcp[ipv6]: setting AI_NUMERICHOST for %s\n", host); hints.ai_flags |= AI_NUMERICHOST; #endif } #ifdef AI_NUMERICSERV hints.ai_flags |= AI_NUMERICSERV; #endif if (!strcmp(host, "127.0.0.1")) { host2 = strdup("::1"); } else if (host[0] == '[') { host2 = strdup(host+1); } else { host2 = strdup(host); } q = strrchr(host2, ']'); if (q) { *q = '\0'; } err = getaddrinfo(host2, service, &hints, &ai); if (err != 0) { rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err)); usleep(100 * 1000); err = getaddrinfo(host2, service, &hints, &ai); } free(host2); if (err != 0) { rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err)); } else { struct addrinfo *ap = ai; while (ap != NULL) { int sock; if (fail4) { struct sockaddr_in6 *s6ptr; if (ap->ai_family != AF_INET6) { rfbLog("connect_tcp[ipv6]: skipping AF_INET address under -noipv4\n"); ap = ap->ai_next; continue; } #ifdef IN6_IS_ADDR_V4MAPPED s6ptr = (struct sockaddr_in6 *) ap->ai_addr; if (IN6_IS_ADDR_V4MAPPED(&(s6ptr->sin6_addr))) { rfbLog("connect_tcp[ipv6]: skipping V4MAPPED address under -noipv4\n"); ap = ap->ai_next; continue; } #endif } sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if (sock == -1) { rfbLogPerror("connect_tcp[ipv6]: socket"); if (0) rfbLog("(Ignore the above error if this system is IPv4-only.)\n"); } else { int res = -1, dmsg = 0; char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen); if (!s) s = strdup("unknown"); rfbLog("connect_tcp[ipv6]: trying sock=%d fam=%d proto=%d using %s\n", sock, ap->ai_family, ap->ai_protocol, s); res = connect(sock, ap->ai_addr, ap->ai_addrlen); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if (res != 0) { int zero = 0; rfbLogPerror("connect_tcp[ipv6]: connect"); dmsg = 1; if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, (char *)&zero, sizeof(zero)) == 0) { rfbLog("connect_tcp[ipv6]: trying again with IPV6_V6ONLY=0\n"); res = connect(sock, ap->ai_addr, ap->ai_addrlen); dmsg = 0; } else { rfbLogPerror("connect_tcp[ipv6]: setsockopt IPV6_V6ONLY"); } } #endif if (res == 0) { rfbLog("connect_tcp[ipv6]: connect OK\n"); fd = sock; if (!ipv6_client_ip_str) { ipv6_client_ip_str = strdup(s); } free(s); break; } else { if (!dmsg) rfbLogPerror("connect_tcp[ipv6]: connect"); close(sock); } free(s); } ap = ap->ai_next; } freeaddrinfo(ai); } #endif } if (fd < 0 && !fail4) { /* this is a kludge for IPv4-only machines getting v4mapped string. */ char *q, *host2; if (host[0] == '[') { host2 = strdup(host+1); } else { host2 = strdup(host); } q = strrchr(host2, ']'); if (q) { *q = '\0'; } if (strstr(host2, "::ffff:") == host2 || strstr(host2, "::FFFF:") == host2) { char *host3 = host2 + strlen("::ffff:"); if (dotted_ip(host3, 0)) { rfbLog("connect_tcp[ipv4]: trying fallback to IPv4 for %s\n", host2); fd = rfbConnectToTcpAddr(host3, port); if (fd < 0) { rfbLogPerror("connect_tcp[ipv4]: connection failed"); } } } free(host2); } return fd; }
void rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen) { int nfds; fd_set fds; struct timeval tv; #ifdef LIBVNCSERVER_IPv6 struct sockaddr_storage addr; #else struct sockaddr_in addr; #endif socklen_t addrlen = sizeof(addr); if (!rfbScreen->httpDir) return; if (rfbScreen->httpListenSock < 0) return; FD_ZERO(&fds); FD_SET(rfbScreen->httpListenSock, &fds); if (rfbScreen->httpListen6Sock >= 0) { FD_SET(rfbScreen->httpListen6Sock, &fds); } if (rfbScreen->httpSock >= 0) { FD_SET(rfbScreen->httpSock, &fds); } tv.tv_sec = 0; tv.tv_usec = 0; nfds = select(max(rfbScreen->httpListen6Sock, max(rfbScreen->httpSock,rfbScreen->httpListenSock)) + 1, &fds, NULL, NULL, &tv); if (nfds == 0) { return; } if (nfds < 0) { #ifdef WIN32 errno = WSAGetLastError(); #endif if (errno != EINTR) rfbLogPerror("httpCheckFds: select"); return; } if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) { httpProcessInput(rfbScreen); } if (FD_ISSET(rfbScreen->httpListenSock, &fds) || FD_ISSET(rfbScreen->httpListen6Sock, &fds)) { if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); if(FD_ISSET(rfbScreen->httpListenSock, &fds)) { if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("httpCheckFds: accept"); return; } } else if(FD_ISSET(rfbScreen->httpListen6Sock, &fds)) { if ((rfbScreen->httpSock = accept(rfbScreen->httpListen6Sock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("httpCheckFds: accept"); return; } } #ifdef USE_LIBWRAP char host[1024]; #ifdef LIBVNCSERVER_IPv6 if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { rfbLogPerror("httpCheckFds: error in getnameinfo"); host[0] = '\0'; } #else memcpy(host, inet_ntoa(addr.sin_addr), sizeof(host)); #endif if(!hosts_ctl("vnc",STRING_UNKNOWN, host, STRING_UNKNOWN)) { rfbLog("Rejected HTTP connection from client %s\n", host); close(rfbScreen->httpSock); rfbScreen->httpSock=-1; return; } #endif if(!rfbSetNonBlocking(rfbScreen->httpSock)) { close(rfbScreen->httpSock); rfbScreen->httpSock=-1; return; } /*AddEnabledDevice(httpSock);*/ } }
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 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 seperate 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 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; }
rfbBool rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) { const int one = 1; int sock = -1; #ifdef LIBVNCSERVER_IPv6 struct sockaddr_storage addr; #else struct sockaddr_in addr; #endif socklen_t addrlen = sizeof(addr); 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, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("rfbCheckFds: accept"); return FALSE; } if(!rfbSetNonBlocking(sock)) { closesocket(sock); return FALSE; } if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbCheckFds: setsockopt"); closesocket(sock); return FALSE; } #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; }
static void httpProcessInput(rfbScreenInfoPtr rfbScreen) { #ifdef LIBVNCSERVER_IPv6 struct sockaddr_storage addr; char host[1024]; #else struct sockaddr_in addr; #endif socklen_t addrlen = sizeof(addr); char fullFname[512]; char params[1024]; char *ptr; char *fname; unsigned int maxFnameLen; FILE* fd; rfbBool performSubstitutions = FALSE; char str[256+32]; #ifndef WIN32 char* user=getenv("USER"); #endif cl.sock=rfbScreen->httpSock; if (strlen(rfbScreen->httpDir) > 255) { rfbErr("-httpd directory too long\n"); httpCloseSock(rfbScreen); return; } strcpy(fullFname, rfbScreen->httpDir); fname = &fullFname[strlen(fullFname)]; maxFnameLen = 511 - strlen(fullFname); buf_filled=0; /* Read data from the HTTP client until we get a complete request. */ while (1) { ssize_t got; if (buf_filled > sizeof (buf)) { rfbErr("httpProcessInput: HTTP request is too long\n"); httpCloseSock(rfbScreen); return; } got = read (rfbScreen->httpSock, buf + buf_filled, sizeof (buf) - buf_filled - 1); if (got <= 0) { if (got == 0) { rfbErr("httpd: premature connection close\n"); } else { #ifdef WIN32 errno=WSAGetLastError(); #endif if (errno == EAGAIN) { return; } rfbLogPerror("httpProcessInput: read"); } httpCloseSock(rfbScreen); return; } buf_filled += got; buf[buf_filled] = '\0'; /* Is it complete yet (is there a blank line)? */ if (strstr (buf, "\r\r") || strstr (buf, "\n\n") || strstr (buf, "\r\n\r\n") || strstr (buf, "\n\r\n\r")) break; } /* Process the request. */ if(rfbScreen->httpEnableProxyConnect) { const static char* PROXY_OK_STR = "HTTP/1.0 200 OK\r\nContent-Type: octet-stream\r\nPragma: no-cache\r\n\r\n"; if(!strncmp(buf, "CONNECT ", 8)) { if(atoi(strchr(buf, ':')+1)!=rfbScreen->port) { rfbErr("httpd: CONNECT format invalid.\n"); rfbWriteExact(&cl,INVALID_REQUEST_STR, strlen(INVALID_REQUEST_STR)); httpCloseSock(rfbScreen); return; } /* proxy connection */ rfbLog("httpd: client asked for CONNECT\n"); rfbWriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); rfbScreen->httpSock = -1; return; } if (!strncmp(buf, "GET ",4) && !strncmp(strchr(buf,'/'),"/proxied.connection HTTP/1.", 27)) { /* proxy connection */ rfbLog("httpd: client asked for /proxied.connection\n"); rfbWriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); rfbScreen->httpSock = -1; return; } } if (strncmp(buf, "GET ", 4)) { rfbErr("httpd: no GET line\n"); httpCloseSock(rfbScreen); return; } else { /* Only use the first line. */ buf[strcspn(buf, "\n\r")] = '\0'; } if (strlen(buf) > maxFnameLen) { rfbErr("httpd: GET line too long\n"); httpCloseSock(rfbScreen); return; } if (sscanf(buf, "GET %s HTTP/1.", fname) != 1) { rfbErr("httpd: couldn't parse GET line\n"); httpCloseSock(rfbScreen); return; } if (fname[0] != '/') { rfbErr("httpd: filename didn't begin with '/'\n"); rfbWriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); httpCloseSock(rfbScreen); return; } getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); #ifdef LIBVNCSERVER_IPv6 if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { rfbLogPerror("httpProcessInput: error in getnameinfo"); } rfbLog("httpd: get '%s' for %s\n", fname+1, host); #else rfbLog("httpd: get '%s' for %s\n", fname+1, inet_ntoa(addr.sin_addr)); #endif /* Extract parameters from the URL string if necessary */ params[0] = '\0'; ptr = strchr(fname, '?'); if (ptr != NULL) { *ptr = '\0'; if (!parseParams(&ptr[1], params, 1024)) { params[0] = '\0'; rfbErr("httpd: bad parameters in the URL\n"); } } /* If we were asked for '/', actually read the file index.vnc */ if (strcmp(fname, "/") == 0) { strcpy(fname, "/index.vnc"); rfbLog("httpd: defaulting to '%s'\n", fname+1); } /* Substitutions are performed on files ending .vnc */ if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) { performSubstitutions = TRUE; } /* Open the file */ if ((fd = fopen(fullFname, "r")) == 0) { rfbLogPerror("httpProcessInput: open"); rfbWriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); httpCloseSock(rfbScreen); return; } if(performSubstitutions) /* is the 'index.vnc' file */ rfbWriteExact(&cl, OK_STR_HTML, strlen(OK_STR_HTML)); else rfbWriteExact(&cl, OK_STR, strlen(OK_STR)); while (1) { int n = fread(buf, 1, BUF_SIZE-1, fd); if (n < 0) { rfbLogPerror("httpProcessInput: read"); fclose(fd); httpCloseSock(rfbScreen); return; } if (n == 0) break; if (performSubstitutions) { /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values. This won't quite work properly if the .vnc file is longer than BUF_SIZE, but it's reasonable to assume that .vnc files will always be short. */ char *ptr = buf; char *dollar; buf[n] = 0; /* make sure it's null-terminated */ while ((dollar = strchr(ptr, '$'))!=NULL) { rfbWriteExact(&cl, ptr, (dollar - ptr)); ptr = dollar; if (compareAndSkip(&ptr, "$WIDTH")) { sprintf(str, "%d", rfbScreen->width); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$HEIGHT")) { sprintf(str, "%d", rfbScreen->height); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) { sprintf(str, "%d", rfbScreen->width); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) { sprintf(str, "%d", rfbScreen->height + 32); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$PORT")) { sprintf(str, "%d", rfbScreen->port); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$DESKTOP")) { rfbWriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); } else if (compareAndSkip(&ptr, "$DISPLAY")) { sprintf(str, "%s:%d", rfbScreen->thisHost, rfbScreen->port-5900); rfbWriteExact(&cl, str, strlen(str)); } else if (compareAndSkip(&ptr, "$USER")) { #ifndef WIN32 if (user) { rfbWriteExact(&cl, user, strlen(user)); } else #endif rfbWriteExact(&cl, "?", 1); } else if (compareAndSkip(&ptr, "$PARAMS")) { if (params[0] != '\0') rfbWriteExact(&cl, params, strlen(params)); } else { if (!compareAndSkip(&ptr, "$$")) ptr++; if (rfbWriteExact(&cl, "$", 1) < 0) { fclose(fd); httpCloseSock(rfbScreen); return; } } } if (rfbWriteExact(&cl, ptr, (&buf[n] - ptr)) < 0) break; } else { /* For files not ending .vnc, just write out the buffer */ if (rfbWriteExact(&cl, buf, n) < 0) break; } } fclose(fd); httpCloseSock(rfbScreen); }
void rfbCheckFds() { int nfds; fd_set fds; struct timeval tv; struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); char addrStr[INET6_ADDRSTRLEN]; char buf[6]; const int one = 1; int sock; rfbClientPtr cl; static Bool inetdInitDone = FALSE; if (!inetdInitDone && inetdSock != -1) { rfbNewClientConnection(inetdSock); inetdInitDone = TRUE; } memcpy((char *)&fds, (char *)&allFds, sizeof(fd_set)); tv.tv_sec = 0; tv.tv_usec = 0; nfds = select(maxFd + 1, &fds, NULL, NULL, &tv); if (nfds == 0) { return; } if (nfds < 0) { rfbLogPerror("rfbCheckFds: select"); return; } if (rfbListenSock != -1 && FD_ISSET(rfbListenSock, &fds)) { if ((sock = accept(rfbListenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("rfbCheckFds: accept"); return; } if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { rfbLogPerror("rfbCheckFds: fcntl"); close(sock); return; } if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbCheckFds: setsockopt"); close(sock); return; } fprintf(stderr, "\n"); #if USE_LIBWRAP if (!hosts_ctl("Xvnc", STRING_UNKNOWN, sockaddr_string(&addr, addrStr, INET6_ADDRSTRLEN), STRING_UNKNOWN)) { rfbLog("Rejected connection from client %s\n", sockaddr_string(&addr, addrStr, INET6_ADDRSTRLEN)) close(sock); return; } #endif rfbLog("Got connection from client %s\n", sockaddr_string(&addr, addrStr, INET6_ADDRSTRLEN)); AddEnabledDevice(sock); FD_SET(sock, &allFds); maxFd = max(sock, maxFd); rfbNewClientConnection(sock); FD_CLR(rfbListenSock, &fds); if (--nfds == 0) return; } if ((udpSock != -1) && FD_ISSET(udpSock, &fds)) { if (recvfrom(udpSock, buf, 1, MSG_PEEK, (struct sockaddr *)&addr, &addrlen) < 0) { rfbLogPerror("rfbCheckFds: UDP: recvfrom"); rfbDisconnectUDPSock(); } else { if (!udpSockConnected || (memcmp(&addr, &udpRemoteAddr, addrlen) != 0)) { /* new remote end */ rfbLog("rfbCheckFds: UDP: got connection\n"); memcpy(&udpRemoteAddr, &addr, addrlen); udpSockConnected = TRUE; if (connect(udpSock, (struct sockaddr *)&addr, addrlen) < 0) { rfbLogPerror("rfbCheckFds: UDP: connect"); rfbDisconnectUDPSock(); return; } rfbNewUDPConnection(udpSock); } rfbProcessUDPInput(udpSock); } FD_CLR(udpSock, &fds); if (--nfds == 0) return; } for (cl = rfbClientHead; cl; cl = cl->next) { if (FD_ISSET(cl->sock, &fds) && FD_ISSET(cl->sock, &allFds)) { rfbClientPtr cl2; #if USETLS do { rfbProcessClientMessage(cl); /* Make sure cl hasn't been freed */ for (cl2 = rfbClientHead; cl2; cl2 = cl2->next) { if (cl2 == cl) break; } if (cl2 == NULL) return; } while (cl->sslctx && rfbssl_pending(cl) > 0); #else rfbProcessClientMessage(cl); for (cl2 = rfbClientHead; cl2; cl2 = cl2->next) { if (cl2 == cl) break; } if (cl2 == NULL) return; #endif } } }
int listen6(int port) { #if X11VNC_IPV6 struct sockaddr_in6 sin; int fd = -1, one = 1; if (noipv6) { return -1; } if (port <= 0 || 65535 < port) { /* for us, invalid port means do not listen. */ return -1; } fd = socket(AF_INET6, SOCK_STREAM, 0); if (fd < 0) { rfbLogPerror("listen6: socket"); rfbLog("(Ignore the above error if this system is IPv4-only.)\n"); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("listen6: setsockopt SO_REUSEADDR"); close(fd); return -1; } #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("listen6: setsockopt IPV6_V6ONLY"); close(fd); return -1; } #endif memset((char *)&sin, 0, sizeof(sin)); sin.sin6_family = AF_INET6; sin.sin6_port = htons(port); sin.sin6_addr = in6addr_any; if (listen_str6) { if (!strcmp(listen_str6, "localhost") || !strcmp(listen_str6, "::1")) { sin.sin6_addr = in6addr_loopback; } else { int err; struct addrinfo *ai; struct addrinfo hints; char service[32]; memset(&hints, 0, sizeof(hints)); sprintf(service, "%d", port); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif #ifdef AI_NUMERICHOST if(ipv6_ip(listen_str6)) { hints.ai_flags |= AI_NUMERICHOST; } #endif #ifdef AI_NUMERICSERV hints.ai_flags |= AI_NUMERICSERV; #endif err = getaddrinfo(listen_str6, service, &hints, &ai); if (err == 0) { struct addrinfo *ap = ai; err = 1; while (ap != NULL) { char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen); if (!s) s = strdup("unknown"); rfbLog("listen6: checking: %s family: %d\n", s, ap->ai_family); if (ap->ai_family == AF_INET6) { memcpy((char *)&sin, ap->ai_addr, sizeof(sin)); rfbLog("listen6: using: %s scope_id: %d\n", s, sin.sin6_scope_id); err = 0; free(s); break; } free(s); ap = ap->ai_next; } freeaddrinfo(ai); } if (err != 0) { rfbLog("Invalid or Unsupported -listen6 string: %s\n", listen_str6); close(fd); return -1; } } } else if (allow_list && !strcmp(allow_list, "127.0.0.1")) { sin.sin6_addr = in6addr_loopback; } else if (listen_str) { if (!strcmp(listen_str, "localhost")) { sin.sin6_addr = in6addr_loopback; } } if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) { rfbLogPerror("listen6: bind"); close(fd); return -1; } if (listen(fd, 32) < 0) { rfbLogPerror("listen6: listen"); close(fd); return -1; } return fd; #else if (port) {} return -1; #endif }
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]; const int one = 1; int sock; rfbClientIteratorPtr i; rfbClientPtr cl; struct timeval tv_msg; fd_set fds_msg; int nfds_msg; 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 ((sock = accept(rfbScreen->listenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("rfbCheckFds: accept"); return -1; } #ifndef WIN32 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { rfbLogPerror("rfbCheckFds: fcntl"); closesocket(sock); return -1; } #endif if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbCheckFds: setsockopt"); closesocket(sock); return -1; } #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 -1; } #endif rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr)); rfbNewClient(rfbScreen,sock); FD_CLR(rfbScreen->listenSock, &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)) { tv_msg.tv_sec = 0; tv_msg.tv_usec = 0; FD_ZERO(&fds_msg); FD_SET(cl->sock, &fds_msg); do { /* drain all messages, speed up mouse move processing */ rfbProcessClientMessage(cl); nfds_msg = select(cl->sock + 1, &fds_msg, NULL, NULL, &tv_msg); } while (nfds_msg > 0); } else rfbSendFileTransferChunk(cl); } } rfbReleaseClientIterator(i); } while(rfbScreen->handleEventsEagerly); return result; }
int WriteExact(rfbClientPtr cl, char *buf, int len) { int n, bytesWritten = 0; fd_set fds; struct timeval tv; int totalTimeWaited = 0; int sock = cl->sock; while (len > 0) { do { #if USETLS if (cl->sslctx) n = rfbssl_write(cl, buf, len); else #endif n = write(sock, buf, len); } while (n < 0 && errno == EINTR); if (n > 0) { buf += n; len -= n; bytesWritten += n; sendBytes += n; } else if (n == 0) { rfbLog("WriteExact: write returned 0?\n"); exit(1); } else { if (errno != EWOULDBLOCK && errno != EAGAIN && errno != 0) { return n; } /* Retry every 5 seconds until we exceed rfbMaxClientWait. 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; do { n = select(sock + 1, NULL, &fds, NULL, &tv); } while (n < 0 && errno == EINTR); if (n < 0) { rfbLogPerror("WriteExact: select"); return n; } if (n == 0) { totalTimeWaited += 5000; if (totalTimeWaited >= rfbMaxClientWait) { errno = ETIMEDOUT; return -1; } } else { totalTimeWaited = 0; } } } gettimeofday(&cl->lastWrite, NULL); cl->sockOffset += bytesWritten; return 1; }
int rfbWriteExact(rfbClientPtr cl, const char *buf, int len) { int sock = cl->sock; int n; fd_set fds; struct timeval tv; int totalTimeWaited = 0; #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 LOCK(cl->outputMutex); while (len > 0) { 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 rfbMaxClientWait. 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) { if(errno==EINTR) continue; rfbLogPerror("WriteExact: select"); UNLOCK(cl->outputMutex); return n; } if (n == 0) { totalTimeWaited += 5000; if (totalTimeWaited >= rfbMaxClientWait) { errno = ETIMEDOUT; UNLOCK(cl->outputMutex); return -1; } } else { totalTimeWaited = 0; } } } UNLOCK(cl->outputMutex); return 1; }
void rfbInitSockets(rfbScreenInfoPtr rfbScreen) { in_addr_t iface = rfbScreen->listenInterface; if (rfbScreen->socketState!=RFB_SOCKET_INIT) return; rfbScreen->socketState = RFB_SOCKET_READY; if (rfbScreen->inetdSock != -1) { const int one = 1; #ifndef WIN32 if (fcntl(rfbScreen->inetdSock, F_SETFL, O_NONBLOCK) < 0) { rfbLogPerror("fcntl"); return; } #endif if (setsockopt(rfbScreen->inetdSock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("setsockopt"); return; } FD_ZERO(&(rfbScreen->allFds)); FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->inetdSock; return; } if(rfbScreen->autoPort) { 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 port %d\n", rfbScreen->port); FD_ZERO(&(rfbScreen->allFds)); FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->listenSock; } else if(rfbScreen->port>0) { rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port); if ((rfbScreen->listenSock = rfbListenOnTCPPort(rfbScreen->port, iface)) < 0) { rfbLogPerror("ListenOnTCPPort"); return; } FD_ZERO(&(rfbScreen->allFds)); FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->listenSock; } 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; } FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds)); rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd); } }
void rfbSendInteractionCaps(rfbClientPtr cl) { rfbInteractionCapsMsg intr_caps; rfbCapabilityInfo smsg_list[N_SMSG_CAPS]; rfbCapabilityInfo cmsg_list[N_CMSG_CAPS]; rfbCapabilityInfo enc_list[N_ENC_CAPS]; int i, n_enc_caps = N_ENC_CAPS; /* Fill in the header structure sent prior to capability lists. */ intr_caps.nServerMessageTypes = Swap16IfLE(N_SMSG_CAPS); intr_caps.nClientMessageTypes = Swap16IfLE(N_CMSG_CAPS); intr_caps.nEncodingTypes = Swap16IfLE(N_ENC_CAPS); intr_caps.pad = 0; rfbLog("tightvnc-filetransfer/rfbSendInteractionCaps\n"); /* Supported server->client message types. */ /* For file transfer support: */ i = 0; if((IsFileTransferEnabled() == TRUE) && ( cl->viewOnly == FALSE)) { SetCapInfo(&smsg_list[i++], rfbFileListData, rfbTightVncVendor); SetCapInfo(&smsg_list[i++], rfbFileDownloadData, rfbTightVncVendor); SetCapInfo(&smsg_list[i++], rfbFileUploadCancel, rfbTightVncVendor); SetCapInfo(&smsg_list[i++], rfbFileDownloadFailed, rfbTightVncVendor); if (i != N_SMSG_CAPS) { rfbLog("rfbSendInteractionCaps: assertion failed, i != N_SMSG_CAPS\n"); rfbCloseClient(cl); return; } } /* Supported client->server message types. */ /* For file transfer support: */ i = 0; if((IsFileTransferEnabled() == TRUE) && ( cl->viewOnly == FALSE)) { SetCapInfo(&cmsg_list[i++], rfbFileListRequest, rfbTightVncVendor); SetCapInfo(&cmsg_list[i++], rfbFileDownloadRequest, rfbTightVncVendor); SetCapInfo(&cmsg_list[i++], rfbFileUploadRequest, rfbTightVncVendor); SetCapInfo(&cmsg_list[i++], rfbFileUploadData, rfbTightVncVendor); SetCapInfo(&cmsg_list[i++], rfbFileDownloadCancel, rfbTightVncVendor); SetCapInfo(&cmsg_list[i++], rfbFileUploadFailed, rfbTightVncVendor); if (i != N_CMSG_CAPS) { rfbLog("rfbSendInteractionCaps: assertion failed, i != N_CMSG_CAPS\n"); rfbCloseClient(cl); return; } } /* Encoding types. */ i = 0; SetCapInfo(&enc_list[i++], rfbEncodingCopyRect, rfbStandardVendor); SetCapInfo(&enc_list[i++], rfbEncodingRRE, rfbStandardVendor); SetCapInfo(&enc_list[i++], rfbEncodingCoRRE, rfbStandardVendor); SetCapInfo(&enc_list[i++], rfbEncodingHextile, rfbStandardVendor); #ifdef LIBVNCSERVER_HAVE_LIBZ SetCapInfo(&enc_list[i++], rfbEncodingZlib, rfbTridiaVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingTight, rfbTightVncVendor); #else n_enc_caps -= 2; #endif SetCapInfo(&enc_list[i++], rfbEncodingCompressLevel0, rfbTightVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingQualityLevel0, rfbTightVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingXCursor, rfbTightVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingRichCursor, rfbTightVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingPointerPos, rfbTightVncVendor); SetCapInfo(&enc_list[i++], rfbEncodingLastRect, rfbTightVncVendor); if (i != n_enc_caps) { rfbLog("rfbSendInteractionCaps: assertion failed, i != N_ENC_CAPS\n"); rfbCloseClient(cl); return; } /* Send header and capability lists */ if (rfbWriteExact(cl, (char *)&intr_caps, sz_rfbInteractionCapsMsg) < 0 || rfbWriteExact(cl, (char *)&smsg_list[0], sz_rfbCapabilityInfo * N_SMSG_CAPS) < 0 || rfbWriteExact(cl, (char *)&cmsg_list[0], sz_rfbCapabilityInfo * N_CMSG_CAPS) < 0 || rfbWriteExact(cl, (char *)&enc_list[0], sz_rfbCapabilityInfo * N_ENC_CAPS) < 0) { rfbLogPerror("rfbSendInteractionCaps: write"); rfbCloseClient(cl); return; } /* Dispatch client input to rfbProcessClientNormalMessage(). */ cl->state = RFB_NORMAL; }