| /* |
| * This file is called main.c, because it contains most of the new functions |
| * for use with LibVNCServer. |
| * |
| * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de> |
| * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. |
| * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. |
| * All Rights Reserved. |
| * |
| * see GPL (latest version) for full details |
| */ |
| |
| #ifdef __STRICT_ANSI__ |
| #define _BSD_SOURCE |
| #endif |
| #include <rfb/rfb.h> |
| #include <rfb/rfbregion.h> |
| #include "private.h" |
| |
| #include <stdarg.h> |
| #include <errno.h> |
| |
| #ifndef false |
| #define false 0 |
| #define true -1 |
| #endif |
| |
| #ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| #ifndef WIN32 |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <signal.h> |
| #include <time.h> |
| |
| static int extMutex_initialized = 0; |
| static int logMutex_initialized = 0; |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| static MUTEX(logMutex); |
| static MUTEX(extMutex); |
| #endif |
| |
| static int rfbEnableLogging=1; |
| |
| #ifdef LIBVNCSERVER_WORDS_BIGENDIAN |
| char rfbEndianTest = (1==0); |
| #else |
| char rfbEndianTest = (1==1); |
| #endif |
| |
| /* |
| * Protocol extensions |
| */ |
| |
| static rfbProtocolExtension* rfbExtensionHead = NULL; |
| |
| /* |
| * This method registers a list of new extensions. |
| * It avoids same extension getting registered multiple times. |
| * The order is not preserved if multiple extensions are |
| * registered at one-go. |
| */ |
| void |
| rfbRegisterProtocolExtension(rfbProtocolExtension* extension) |
| { |
| rfbProtocolExtension *head = rfbExtensionHead, *next = NULL; |
| |
| if(extension == NULL) |
| return; |
| |
| next = extension->next; |
| |
| if (! extMutex_initialized) { |
| INIT_MUTEX(extMutex); |
| extMutex_initialized = 1; |
| } |
| |
| LOCK(extMutex); |
| |
| while(head != NULL) { |
| if(head == extension) { |
| UNLOCK(extMutex); |
| rfbRegisterProtocolExtension(next); |
| return; |
| } |
| |
| head = head->next; |
| } |
| |
| extension->next = rfbExtensionHead; |
| rfbExtensionHead = extension; |
| |
| UNLOCK(extMutex); |
| rfbRegisterProtocolExtension(next); |
| } |
| |
| /* |
| * This method unregisters a list of extensions. |
| * These extensions won't be available for any new |
| * client connection. |
| */ |
| void |
| rfbUnregisterProtocolExtension(rfbProtocolExtension* extension) |
| { |
| |
| rfbProtocolExtension *cur = NULL, *pre = NULL; |
| |
| if(extension == NULL) |
| return; |
| |
| if (! extMutex_initialized) { |
| INIT_MUTEX(extMutex); |
| extMutex_initialized = 1; |
| } |
| |
| LOCK(extMutex); |
| |
| if(rfbExtensionHead == extension) { |
| rfbExtensionHead = rfbExtensionHead->next; |
| UNLOCK(extMutex); |
| rfbUnregisterProtocolExtension(extension->next); |
| return; |
| } |
| |
| cur = pre = rfbExtensionHead; |
| |
| while(cur) { |
| if(cur == extension) { |
| pre->next = cur->next; |
| break; |
| } |
| pre = cur; |
| cur = cur->next; |
| } |
| |
| UNLOCK(extMutex); |
| |
| rfbUnregisterProtocolExtension(extension->next); |
| } |
| |
| rfbProtocolExtension* rfbGetExtensionIterator() |
| { |
| if (! extMutex_initialized) { |
| INIT_MUTEX(extMutex); |
| extMutex_initialized = 1; |
| } |
| |
| LOCK(extMutex); |
| return rfbExtensionHead; |
| } |
| |
| void rfbReleaseExtensionIterator() |
| { |
| UNLOCK(extMutex); |
| } |
| |
| rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension, |
| void* data) |
| { |
| rfbExtensionData* extData; |
| |
| /* make sure extension is not yet enabled. */ |
| for(extData = cl->extensions; extData; extData = extData->next) |
| if(extData->extension == extension) |
| return FALSE; |
| |
| extData = calloc(sizeof(rfbExtensionData),1); |
| extData->extension = extension; |
| extData->data = data; |
| extData->next = cl->extensions; |
| cl->extensions = extData; |
| |
| return TRUE; |
| } |
| |
| rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension) |
| { |
| rfbExtensionData* extData; |
| rfbExtensionData* prevData = NULL; |
| |
| for(extData = cl->extensions; extData; extData = extData->next) { |
| if(extData->extension == extension) { |
| if(extData->data) |
| free(extData->data); |
| if(prevData == NULL) |
| cl->extensions = extData->next; |
| else |
| prevData->next = extData->next; |
| return TRUE; |
| } |
| prevData = extData; |
| } |
| |
| return FALSE; |
| } |
| |
| void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension) |
| { |
| rfbExtensionData* data = cl->extensions; |
| |
| while(data && data->extension != extension) |
| data = data->next; |
| |
| if(data == NULL) { |
| rfbLog("Extension is not enabled !\n"); |
| /* rfbCloseClient(cl); */ |
| return NULL; |
| } |
| |
| return data->data; |
| } |
| |
| /* |
| * Logging |
| */ |
| |
| void rfbLogEnable(int enabled) { |
| rfbEnableLogging=enabled; |
| } |
| |
| /* |
| * rfbLog prints a time-stamped message to the log file (stderr). |
| */ |
| |
| static void |
| rfbDefaultLog(const char *format, ...) |
| { |
| va_list args; |
| char buf[256]; |
| time_t log_clock; |
| |
| if(!rfbEnableLogging) |
| return; |
| |
| if (! logMutex_initialized) { |
| INIT_MUTEX(logMutex); |
| logMutex_initialized = 1; |
| } |
| |
| LOCK(logMutex); |
| va_start(args, format); |
| |
| time(&log_clock); |
| strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock)); |
| fprintf(stderr, "%s", buf); |
| |
| vfprintf(stderr, format, args); |
| fflush(stderr); |
| |
| va_end(args); |
| UNLOCK(logMutex); |
| } |
| |
| rfbLogProc rfbLog=rfbDefaultLog; |
| rfbLogProc rfbErr=rfbDefaultLog; |
| |
| void rfbLogPerror(const char *str) |
| { |
| rfbErr("%s: %s\n", str, strerror(errno)); |
| } |
| |
| void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy) |
| { |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr cl; |
| |
| iterator=rfbGetClientIterator(rfbScreen); |
| while((cl=rfbClientIteratorNext(iterator))) { |
| LOCK(cl->updateMutex); |
| if(cl->useCopyRect) { |
| sraRegionPtr modifiedRegionBackup; |
| if(!sraRgnEmpty(cl->copyRegion)) { |
| if(cl->copyDX!=dx || cl->copyDY!=dy) { |
| /* if a copyRegion was not yet executed, treat it as a |
| * modifiedRegion. The idea: in this case it could be |
| * source of the new copyRect or modified anyway. */ |
| sraRgnOr(cl->modifiedRegion,cl->copyRegion); |
| sraRgnMakeEmpty(cl->copyRegion); |
| } else { |
| /* we have to set the intersection of the source of the copy |
| * and the old copy to modified. */ |
| modifiedRegionBackup=sraRgnCreateRgn(copyRegion); |
| sraRgnOffset(modifiedRegionBackup,-dx,-dy); |
| sraRgnAnd(modifiedRegionBackup,cl->copyRegion); |
| sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); |
| sraRgnDestroy(modifiedRegionBackup); |
| } |
| } |
| |
| sraRgnOr(cl->copyRegion,copyRegion); |
| cl->copyDX = dx; |
| cl->copyDY = dy; |
| |
| /* if there were modified regions, which are now copied, |
| * mark them as modified, because the source of these can be overlapped |
| * either by new modified or now copied regions. */ |
| modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion); |
| sraRgnOffset(modifiedRegionBackup,dx,dy); |
| sraRgnAnd(modifiedRegionBackup,cl->copyRegion); |
| sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); |
| sraRgnDestroy(modifiedRegionBackup); |
| |
| if(!cl->enableCursorShapeUpdates) { |
| /* |
| * n.b. (dx, dy) is the vector pointing in the direction the |
| * copyrect displacement will take place. copyRegion is the |
| * destination rectangle (say), not the source rectangle. |
| */ |
| sraRegionPtr cursorRegion; |
| int x = cl->cursorX - cl->screen->cursor->xhot; |
| int y = cl->cursorY - cl->screen->cursor->yhot; |
| int w = cl->screen->cursor->width; |
| int h = cl->screen->cursor->height; |
| |
| cursorRegion = sraRgnCreateRect(x, y, x + w, y + h); |
| sraRgnAnd(cursorRegion, cl->copyRegion); |
| if(!sraRgnEmpty(cursorRegion)) { |
| /* |
| * current cursor rect overlaps with the copy region *dest*, |
| * mark it as modified since we won't copy-rect stuff to it. |
| */ |
| sraRgnOr(cl->modifiedRegion, cursorRegion); |
| } |
| sraRgnDestroy(cursorRegion); |
| |
| cursorRegion = sraRgnCreateRect(x, y, x + w, y + h); |
| /* displace it to check for overlap with copy region source: */ |
| sraRgnOffset(cursorRegion, dx, dy); |
| sraRgnAnd(cursorRegion, cl->copyRegion); |
| if(!sraRgnEmpty(cursorRegion)) { |
| /* |
| * current cursor rect overlaps with the copy region *source*, |
| * mark the *displaced* cursorRegion as modified since we |
| * won't copyrect stuff to it. |
| */ |
| sraRgnOr(cl->modifiedRegion, cursorRegion); |
| } |
| sraRgnDestroy(cursorRegion); |
| } |
| |
| } else { |
| sraRgnOr(cl->modifiedRegion,copyRegion); |
| } |
| TSIGNAL(cl->updateCond); |
| UNLOCK(cl->updateMutex); |
| } |
| |
| rfbReleaseClientIterator(iterator); |
| } |
| |
| void rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy) |
| { |
| sraRectangleIterator* i; |
| sraRect rect; |
| int j,widthInBytes,bpp=screen->serverFormat.bitsPerPixel/8, |
| rowstride=screen->paddedWidthInBytes; |
| char *in,*out; |
| |
| /* copy it, really */ |
| i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0); |
| while(sraRgnIteratorNext(i,&rect)) { |
| widthInBytes = (rect.x2-rect.x1)*bpp; |
| out = screen->frameBuffer+rect.x1*bpp+rect.y1*rowstride; |
| in = screen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride; |
| if(dy<0) |
| for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride) |
| memmove(out,in,widthInBytes); |
| else { |
| out += rowstride*(rect.y2-rect.y1-1); |
| in += rowstride*(rect.y2-rect.y1-1); |
| for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride) |
| memmove(out,in,widthInBytes); |
| } |
| } |
| sraRgnReleaseIterator(i); |
| |
| rfbScheduleCopyRegion(screen,copyRegion,dx,dy); |
| } |
| |
| void rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy) |
| { |
| sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); |
| rfbDoCopyRegion(screen,region,dx,dy); |
| sraRgnDestroy(region); |
| } |
| |
| void rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy) |
| { |
| sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); |
| rfbScheduleCopyRegion(screen,region,dx,dy); |
| sraRgnDestroy(region); |
| } |
| |
| void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion) |
| { |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr cl; |
| |
| iterator=rfbGetClientIterator(screen); |
| while((cl=rfbClientIteratorNext(iterator))) { |
| LOCK(cl->updateMutex); |
| sraRgnOr(cl->modifiedRegion,modRegion); |
| TSIGNAL(cl->updateCond); |
| UNLOCK(cl->updateMutex); |
| } |
| |
| rfbReleaseClientIterator(iterator); |
| } |
| |
| void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2); |
| void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2) |
| { |
| sraRegionPtr region; |
| int i; |
| |
| if(x1>x2) { i=x1; x1=x2; x2=i; } |
| if(x1<0) x1=0; |
| if(x2>screen->width) x2=screen->width; |
| if(x1==x2) return; |
| |
| if(y1>y2) { i=y1; y1=y2; y2=i; } |
| if(y1<0) y1=0; |
| if(y2>screen->height) y2=screen->height; |
| if(y1==y2) return; |
| |
| /* update scaled copies for this rectangle */ |
| rfbScaledScreenUpdate(screen,x1,y1,x2,y2); |
| |
| region = sraRgnCreateRect(x1,y1,x2,y2); |
| rfbMarkRegionAsModified(screen,region); |
| sraRgnDestroy(region); |
| } |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| #include <unistd.h> |
| |
| static void * |
| clientOutput(void *data) |
| { |
| rfbClientPtr cl = (rfbClientPtr)data; |
| rfbBool haveUpdate; |
| sraRegion* updateRegion; |
| |
| while (1) { |
| haveUpdate = false; |
| while (!haveUpdate) { |
| if (cl->sock == -1) { |
| /* Client has disconnected. */ |
| return NULL; |
| } |
| if (cl->state != RFB_NORMAL || cl->onHold) { |
| /* just sleep until things get normal */ |
| usleep(cl->screen->deferUpdateTime * 1000); |
| continue; |
| } |
| |
| LOCK(cl->updateMutex); |
| |
| if (sraRgnEmpty(cl->requestedRegion)) { |
| ; /* always require a FB Update Request (otherwise can crash.) */ |
| } else { |
| haveUpdate = FB_UPDATE_PENDING(cl); |
| if(!haveUpdate) { |
| updateRegion = sraRgnCreateRgn(cl->modifiedRegion); |
| haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion); |
| sraRgnDestroy(updateRegion); |
| } |
| } |
| |
| if (!haveUpdate) { |
| WAIT(cl->updateCond, cl->updateMutex); |
| } |
| |
| UNLOCK(cl->updateMutex); |
| } |
| |
| /* OK, now, to save bandwidth, wait a little while for more |
| updates to come along. */ |
| usleep(cl->screen->deferUpdateTime * 1000); |
| |
| /* Now, get the region we're going to update, and remove |
| it from cl->modifiedRegion _before_ we send the update. |
| That way, if anything that overlaps the region we're sending |
| is updated, we'll be sure to do another update later. */ |
| LOCK(cl->updateMutex); |
| updateRegion = sraRgnCreateRgn(cl->modifiedRegion); |
| UNLOCK(cl->updateMutex); |
| |
| /* Now actually send the update. */ |
| rfbIncrClientRef(cl); |
| LOCK(cl->sendMutex); |
| rfbSendFramebufferUpdate(cl, updateRegion); |
| UNLOCK(cl->sendMutex); |
| rfbDecrClientRef(cl); |
| |
| sraRgnDestroy(updateRegion); |
| } |
| |
| /* Not reached. */ |
| return NULL; |
| } |
| |
| 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)) |
| rfbProcessClientMessage(cl); |
| } |
| |
| /* 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* |
| 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); |
| } |
| |
| void |
| rfbStartOnHoldClient(rfbClientPtr cl) |
| { |
| pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl); |
| } |
| |
| #else |
| |
| void |
| rfbStartOnHoldClient(rfbClientPtr cl) |
| { |
| cl->onHold = FALSE; |
| } |
| |
| #endif |
| |
| void |
| rfbRefuseOnHoldClient(rfbClientPtr cl) |
| { |
| rfbCloseClient(cl); |
| rfbClientConnectionGone(cl); |
| } |
| |
| static void |
| rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl) |
| { |
| } |
| |
| void |
| rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl) |
| { |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr other_client; |
| rfbScreenInfoPtr s = cl->screen; |
| |
| if (x != s->cursorX || y != s->cursorY) { |
| LOCK(s->cursorMutex); |
| s->cursorX = x; |
| s->cursorY = y; |
| UNLOCK(s->cursorMutex); |
| |
| /* The cursor was moved by this client, so don't send CursorPos. */ |
| if (cl->enableCursorPosUpdates) |
| cl->cursorWasMoved = FALSE; |
| |
| /* But inform all remaining clients about this cursor movement. */ |
| iterator = rfbGetClientIterator(s); |
| while ((other_client = rfbClientIteratorNext(iterator)) != NULL) { |
| if (other_client != cl && other_client->enableCursorPosUpdates) { |
| other_client->cursorWasMoved = TRUE; |
| } |
| } |
| rfbReleaseClientIterator(iterator); |
| } |
| } |
| |
| static void rfbDefaultSetXCutText(char* text, int len, rfbClientPtr cl) |
| { |
| } |
| |
| /* TODO: add a nice VNC or RFB cursor */ |
| |
| #if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI) |
| static rfbCursor myCursor = |
| { |
| FALSE, FALSE, FALSE, FALSE, |
| (unsigned char*)"\000\102\044\030\044\102\000", |
| (unsigned char*)"\347\347\176\074\176\347\347", |
| 8, 7, 3, 3, |
| 0, 0, 0, |
| 0xffff, 0xffff, 0xffff, |
| NULL |
| }; |
| #else |
| static rfbCursor myCursor = |
| { |
| cleanup: FALSE, |
| cleanupSource: FALSE, |
| cleanupMask: FALSE, |
| cleanupRichSource: FALSE, |
| source: "\000\102\044\030\044\102\000", |
| mask: "\347\347\176\074\176\347\347", |
| width: 8, height: 7, xhot: 3, yhot: 3, |
| foreRed: 0, foreGreen: 0, foreBlue: 0, |
| backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff, |
| richSource: NULL |
| }; |
| #endif |
| |
| static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl) |
| { |
| return(cl->screen->cursor); |
| } |
| |
| /* response is cl->authChallenge vncEncrypted with passwd */ |
| static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len) |
| { |
| int i; |
| char *passwd=rfbDecryptPasswdFromFile(cl->screen->authPasswdData); |
| |
| if(!passwd) { |
| rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData); |
| return(FALSE); |
| } |
| |
| rfbEncryptBytes(cl->authChallenge, passwd); |
| |
| /* Lose the password from memory */ |
| for (i = strlen(passwd); i >= 0; i--) { |
| passwd[i] = '\0'; |
| } |
| |
| free(passwd); |
| |
| if (memcmp(cl->authChallenge, response, len) != 0) { |
| rfbErr("authProcessClientMessage: authentication failed from %s\n", |
| cl->host); |
| return(FALSE); |
| } |
| |
| return(TRUE); |
| } |
| |
| /* for this method, authPasswdData is really a pointer to an array |
| of char*'s, where the last pointer is 0. */ |
| rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len) |
| { |
| char **passwds; |
| int i=0; |
| |
| for(passwds=(char**)cl->screen->authPasswdData;*passwds;passwds++,i++) { |
| uint8_t auth_tmp[CHALLENGESIZE]; |
| memcpy((char *)auth_tmp, (char *)cl->authChallenge, CHALLENGESIZE); |
| rfbEncryptBytes(auth_tmp, *passwds); |
| |
| if (memcmp(auth_tmp, response, len) == 0) { |
| if(i>=cl->screen->authPasswdFirstViewOnly) |
| cl->viewOnly=TRUE; |
| return(TRUE); |
| } |
| } |
| |
| rfbErr("authProcessClientMessage: authentication failed from %s\n", |
| cl->host); |
| return(FALSE); |
| } |
| |
| void rfbDoNothingWithClient(rfbClientPtr cl) |
| { |
| } |
| |
| static enum rfbNewClientAction rfbDefaultNewClientHook(rfbClientPtr cl) |
| { |
| return RFB_CLIENT_ACCEPT; |
| } |
| |
| /* |
| * Update server's pixel format in screenInfo structure. This |
| * function is called from rfbGetScreen() and rfbNewFramebuffer(). |
| */ |
| |
| static void rfbInitServerFormat(rfbScreenInfoPtr screen, int bitsPerSample) |
| { |
| rfbPixelFormat* format=&screen->serverFormat; |
| |
| format->bitsPerPixel = screen->bitsPerPixel; |
| format->depth = screen->depth; |
| format->bigEndian = rfbEndianTest?FALSE:TRUE; |
| format->trueColour = TRUE; |
| screen->colourMap.count = 0; |
| screen->colourMap.is16 = 0; |
| screen->colourMap.data.bytes = NULL; |
| |
| if (format->bitsPerPixel == 8) { |
| format->redMax = 7; |
| format->greenMax = 7; |
| format->blueMax = 3; |
| format->redShift = 0; |
| format->greenShift = 3; |
| format->blueShift = 6; |
| } else { |
| format->redMax = (1 << bitsPerSample) - 1; |
| format->greenMax = (1 << bitsPerSample) - 1; |
| format->blueMax = (1 << bitsPerSample) - 1; |
| if(rfbEndianTest) { |
| format->redShift = 0; |
| format->greenShift = bitsPerSample; |
| format->blueShift = bitsPerSample * 2; |
| } else { |
| if(format->bitsPerPixel==8*3) { |
| format->redShift = bitsPerSample*2; |
| format->greenShift = bitsPerSample*1; |
| format->blueShift = 0; |
| } else { |
| format->redShift = bitsPerSample*3; |
| format->greenShift = bitsPerSample*2; |
| format->blueShift = bitsPerSample; |
| } |
| } |
| } |
| } |
| |
| rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, |
| int width,int height,int bitsPerSample,int samplesPerPixel, |
| int bytesPerPixel) |
| { |
| rfbScreenInfoPtr screen=calloc(sizeof(rfbScreenInfo),1); |
| |
| if (! logMutex_initialized) { |
| INIT_MUTEX(logMutex); |
| logMutex_initialized = 1; |
| } |
| |
| |
| if(width&3) |
| rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width); |
| |
| screen->autoPort=FALSE; |
| screen->clientHead=NULL; |
| screen->pointerClient=NULL; |
| screen->port=5900; |
| screen->ipv6port=5900; |
| screen->socketState=RFB_SOCKET_INIT; |
| |
| screen->inetdInitDone = FALSE; |
| screen->inetdSock=-1; |
| |
| screen->udpSock=-1; |
| screen->udpSockConnected=FALSE; |
| screen->udpPort=0; |
| screen->udpClient=NULL; |
| |
| screen->maxFd=0; |
| screen->listenSock=-1; |
| screen->listen6Sock=-1; |
| |
| screen->httpInitDone=FALSE; |
| screen->httpEnableProxyConnect=FALSE; |
| screen->httpPort=0; |
| screen->http6Port=0; |
| screen->httpDir=NULL; |
| screen->httpListenSock=-1; |
| screen->httpListen6Sock=-1; |
| screen->httpSock=-1; |
| |
| screen->desktopName = "LibVNCServer"; |
| screen->alwaysShared = FALSE; |
| screen->neverShared = FALSE; |
| screen->dontDisconnect = FALSE; |
| screen->authPasswdData = NULL; |
| screen->authPasswdFirstViewOnly = 1; |
| |
| screen->width = width; |
| screen->height = height; |
| screen->bitsPerPixel = screen->depth = 8*bytesPerPixel; |
| |
| screen->passwordCheck = rfbDefaultPasswordCheck; |
| |
| screen->ignoreSIGPIPE = TRUE; |
| |
| /* disable progressive updating per default */ |
| screen->progressiveSliceHeight = 0; |
| |
| screen->listenInterface = htonl(INADDR_ANY); |
| |
| screen->deferUpdateTime=5; |
| screen->maxRectsPerUpdate=50; |
| |
| screen->handleEventsEagerly = FALSE; |
| |
| screen->protocolMajorVersion = rfbProtocolMajorVersion; |
| screen->protocolMinorVersion = rfbProtocolMinorVersion; |
| |
| screen->permitFileTransfer = FALSE; |
| |
| if(!rfbProcessArguments(screen,argc,argv)) { |
| free(screen); |
| return NULL; |
| } |
| |
| #ifdef WIN32 |
| { |
| DWORD dummy=255; |
| GetComputerName(screen->thisHost,&dummy); |
| } |
| #else |
| gethostname(screen->thisHost, 255); |
| #endif |
| |
| screen->paddedWidthInBytes = width*bytesPerPixel; |
| |
| /* format */ |
| |
| rfbInitServerFormat(screen, bitsPerSample); |
| |
| /* cursor */ |
| |
| screen->cursorX=screen->cursorY=screen->underCursorBufferLen=0; |
| screen->underCursorBuffer=NULL; |
| screen->dontConvertRichCursorToXCursor = FALSE; |
| screen->cursor = &myCursor; |
| INIT_MUTEX(screen->cursorMutex); |
| |
| IF_PTHREADS(screen->backgroundLoop = FALSE); |
| |
| /* proc's and hook's */ |
| |
| screen->kbdAddEvent = rfbDefaultKbdAddEvent; |
| screen->kbdReleaseAllKeys = rfbDoNothingWithClient; |
| screen->ptrAddEvent = rfbDefaultPtrAddEvent; |
| screen->setXCutText = rfbDefaultSetXCutText; |
| screen->getCursorPtr = rfbDefaultGetCursorPtr; |
| screen->setTranslateFunction = rfbSetTranslateFunction; |
| screen->newClientHook = rfbDefaultNewClientHook; |
| screen->displayHook = NULL; |
| screen->displayFinishedHook = NULL; |
| screen->getKeyboardLedStateHook = NULL; |
| screen->xvpHook = NULL; |
| |
| /* initialize client list and iterator mutex */ |
| rfbClientListInit(screen); |
| |
| return(screen); |
| } |
| |
| /* |
| * Switch to another framebuffer (maybe of different size and color |
| * format). Clients supporting NewFBSize pseudo-encoding will change |
| * their local framebuffer dimensions if necessary. |
| * NOTE: Rich cursor data should be converted to new pixel format by |
| * the caller. |
| */ |
| |
| void rfbNewFramebuffer(rfbScreenInfoPtr screen, char *framebuffer, |
| int width, int height, |
| int bitsPerSample, int samplesPerPixel, |
| int bytesPerPixel) |
| { |
| rfbPixelFormat old_format; |
| rfbBool format_changed = FALSE; |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr cl; |
| |
| /* Update information in the screenInfo structure */ |
| |
| old_format = screen->serverFormat; |
| |
| if (width & 3) |
| rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width); |
| |
| screen->width = width; |
| screen->height = height; |
| screen->bitsPerPixel = screen->depth = 8*bytesPerPixel; |
| screen->paddedWidthInBytes = width*bytesPerPixel; |
| |
| rfbInitServerFormat(screen, bitsPerSample); |
| |
| if (memcmp(&screen->serverFormat, &old_format, |
| sizeof(rfbPixelFormat)) != 0) { |
| format_changed = TRUE; |
| } |
| |
| screen->frameBuffer = framebuffer; |
| |
| /* Adjust pointer position if necessary */ |
| |
| if (screen->cursorX >= width) |
| screen->cursorX = width - 1; |
| if (screen->cursorY >= height) |
| screen->cursorY = height - 1; |
| |
| /* For each client: */ |
| iterator = rfbGetClientIterator(screen); |
| while ((cl = rfbClientIteratorNext(iterator)) != NULL) { |
| |
| /* Re-install color translation tables if necessary */ |
| |
| if (format_changed) |
| screen->setTranslateFunction(cl); |
| |
| /* Mark the screen contents as changed, and schedule sending |
| NewFBSize message if supported by this client. */ |
| |
| LOCK(cl->updateMutex); |
| sraRgnDestroy(cl->modifiedRegion); |
| cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height); |
| sraRgnMakeEmpty(cl->copyRegion); |
| cl->copyDX = 0; |
| cl->copyDY = 0; |
| |
| if (cl->useNewFBSize) |
| cl->newFBSizePending = TRUE; |
| |
| TSIGNAL(cl->updateCond); |
| UNLOCK(cl->updateMutex); |
| } |
| rfbReleaseClientIterator(iterator); |
| } |
| |
| /* hang up on all clients and free all reserved memory */ |
| |
| void rfbScreenCleanup(rfbScreenInfoPtr screen) |
| { |
| rfbClientIteratorPtr i=rfbGetClientIterator(screen); |
| rfbClientPtr cl,cl1=rfbClientIteratorNext(i); |
| while(cl1) { |
| cl=rfbClientIteratorNext(i); |
| rfbClientConnectionGone(cl1); |
| cl1=cl; |
| } |
| rfbReleaseClientIterator(i); |
| |
| #define FREE_IF(x) if(screen->x) free(screen->x) |
| FREE_IF(colourMap.data.bytes); |
| FREE_IF(underCursorBuffer); |
| TINI_MUTEX(screen->cursorMutex); |
| if(screen->cursor && screen->cursor->cleanup) |
| rfbFreeCursor(screen->cursor); |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| rfbZlibCleanup(screen); |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| rfbTightCleanup(screen); |
| #endif |
| |
| /* free all 'scaled' versions of this screen */ |
| while (screen->scaledScreenNext!=NULL) |
| { |
| rfbScreenInfoPtr ptr; |
| ptr = screen->scaledScreenNext; |
| screen->scaledScreenNext = ptr->scaledScreenNext; |
| free(ptr->frameBuffer); |
| free(ptr); |
| } |
| |
| #endif |
| free(screen); |
| } |
| |
| void rfbInitServer(rfbScreenInfoPtr screen) |
| { |
| #ifdef WIN32 |
| WSADATA trash; |
| WSAStartup(MAKEWORD(2,2),&trash); |
| #endif |
| rfbInitSockets(screen); |
| rfbHttpInitSockets(screen); |
| #ifndef WIN32 |
| if(screen->ignoreSIGPIPE) |
| signal(SIGPIPE,SIG_IGN); |
| #endif |
| } |
| |
| void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) { |
| if(disconnectClients) { |
| rfbClientPtr cl; |
| rfbClientIteratorPtr iter = rfbGetClientIterator(screen); |
| while( (cl = rfbClientIteratorNext(iter)) ) { |
| if (cl->sock > -1) { |
| /* we don't care about maxfd here, because the server goes away */ |
| rfbCloseClient(cl); |
| rfbClientConnectionGone(cl); |
| } |
| } |
| rfbReleaseClientIterator(iter); |
| } |
| |
| rfbShutdownSockets(screen); |
| rfbHttpShutdownSockets(screen); |
| } |
| |
| #ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY |
| #include <fcntl.h> |
| #include <conio.h> |
| #include <sys/timeb.h> |
| |
| void gettimeofday(struct timeval* tv,char* dummy) |
| { |
| SYSTEMTIME t; |
| GetSystemTime(&t); |
| tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond; |
| tv->tv_usec=t.wMilliseconds*1000; |
| } |
| #endif |
| |
| rfbBool |
| rfbProcessEvents(rfbScreenInfoPtr screen,long usec) |
| { |
| rfbClientIteratorPtr i; |
| rfbClientPtr cl,clPrev; |
| rfbBool result=FALSE; |
| extern rfbClientIteratorPtr |
| rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen); |
| |
| if(usec<0) |
| usec=screen->deferUpdateTime*1000; |
| |
| rfbCheckFds(screen,usec); |
| rfbHttpCheckFds(screen); |
| |
| i = rfbGetClientIteratorWithClosed(screen); |
| cl=rfbClientIteratorHead(i); |
| while(cl) { |
| result = rfbUpdateClient(cl); |
| clPrev=cl; |
| cl=rfbClientIteratorNext(i); |
| if(clPrev->sock==-1) { |
| rfbClientConnectionGone(clPrev); |
| result=TRUE; |
| } |
| } |
| rfbReleaseClientIterator(i); |
| |
| return result; |
| } |
| |
| rfbBool |
| rfbUpdateClient(rfbClientPtr cl) |
| { |
| struct timeval tv; |
| rfbBool result=FALSE; |
| rfbScreenInfoPtr screen = cl->screen; |
| |
| if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) && |
| !sraRgnEmpty(cl->requestedRegion)) { |
| result=TRUE; |
| if(screen->deferUpdateTime == 0) { |
| rfbSendFramebufferUpdate(cl,cl->modifiedRegion); |
| } else if(cl->startDeferring.tv_usec == 0) { |
| gettimeofday(&cl->startDeferring,NULL); |
| if(cl->startDeferring.tv_usec == 0) |
| cl->startDeferring.tv_usec++; |
| } else { |
| gettimeofday(&tv,NULL); |
| if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */ |
| || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000 |
| +(tv.tv_usec-cl->startDeferring.tv_usec)/1000) |
| > screen->deferUpdateTime) { |
| cl->startDeferring.tv_usec = 0; |
| rfbSendFramebufferUpdate(cl,cl->modifiedRegion); |
| } |
| } |
| } |
| |
| if (!cl->viewOnly && cl->lastPtrX >= 0) { |
| if(cl->startPtrDeferring.tv_usec == 0) { |
| gettimeofday(&cl->startPtrDeferring,NULL); |
| if(cl->startPtrDeferring.tv_usec == 0) |
| cl->startPtrDeferring.tv_usec++; |
| } else { |
| struct timeval tv; |
| gettimeofday(&tv,NULL); |
| if(tv.tv_sec < cl->startPtrDeferring.tv_sec /* at midnight */ |
| || ((tv.tv_sec-cl->startPtrDeferring.tv_sec)*1000 |
| +(tv.tv_usec-cl->startPtrDeferring.tv_usec)/1000) |
| > cl->screen->deferPtrUpdateTime) { |
| cl->startPtrDeferring.tv_usec = 0; |
| cl->screen->ptrAddEvent(cl->lastPtrButtons, |
| cl->lastPtrX, |
| cl->lastPtrY, cl); |
| cl->lastPtrX = -1; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo) { |
| return screenInfo->socketState!=RFB_SOCKET_SHUTDOWN || screenInfo->clientHead!=NULL; |
| } |
| |
| void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground) |
| { |
| if(runInBackground) { |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| pthread_t listener_thread; |
| |
| screen->backgroundLoop = TRUE; |
| |
| pthread_create(&listener_thread, NULL, listenerRun, screen); |
| return; |
| #else |
| rfbErr("Can't run in background, because I don't have PThreads!\n"); |
| return; |
| #endif |
| } |
| |
| if(usec<0) |
| usec=screen->deferUpdateTime*1000; |
| |
| while(rfbIsActive(screen)) |
| rfbProcessEvents(screen,usec); |
| } |