From b4312ddb7316ec880b7b684ce25d8acbc938c4ae Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Thu, 4 May 2023 10:20:10 +0200 Subject: [PATCH 1/3] libvncserver: Extension2 system --- include/rfb/rfb.h | 91 +++++++++ src/libvncserver/cargs.c | 50 +++-- src/libvncserver/main.c | 351 ++++++++++++++++++++++++++++++----- src/libvncserver/private.h | 14 ++ src/libvncserver/rfbserver.c | 221 ++++++++++++++-------- src/libvncserver/sockets.c | 25 ++- 6 files changed, 617 insertions(+), 135 deletions(-) diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index 79a446f1b..a1ff520dd 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -192,6 +192,86 @@ typedef struct _rfbExtensionData { struct _rfbExtensionData* next; } rfbExtensionData; +/** + * Protocol extended extension handling. + */ + +enum rfbProtocolExtensionHookType { + RFB_PROTOCOL_EXTENSION_HOOK_NONE = 0, + RFB_PROTOCOL_EXTENSION_HOOK_RESERVED_1 = 1, + RFB_PROTOCOL_EXTENSION_HOOK_NEW_CLIENT, + RFB_PROTOCOL_EXTENSION_HOOK_INIT, + RFB_PROTOCOL_EXTENSION_HOOK_PSEUDO_ENCODINGS, + RFB_PROTOCOL_EXTENSION_HOOK_ENABLE_PSEUDO_ENCODING, + RFB_PROTOCOL_EXTENSION_HOOK_HANDLE_MESSAGE, + RFB_PROTOCOL_EXTENSION_HOOK_CLOSE, + RFB_PROTOCOL_EXTENSION_HOOK_USAGE, + RFB_PROTOCOL_EXTENSION_HOOK_PROCESS_ARGUMENT, +}; + +typedef void* rfbProtocolExtensionHookGeneric; + +/** returns FALSE if extension should be deactivated for client. + if newClient == NULL, it is always deactivated. */ +typedef rfbBool (*rfbProtocolExtensionHookNewClient)(struct _rfbClientRec* client, void** data); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookNewClient), "extension hook size doesn't match"); + +/** returns FALSE if extension should be deactivated for client. + if init == NULL, it stays activated. */ +typedef rfbBool (*rfbProtocolExtensionHookInit)(struct _rfbClientRec* client, void* data); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookInit), "extension hook size doesn't match"); + +/** if pseudoEncodings is not NULL, it contains a 0 terminated + list of the pseudo encodings handled by this extension. */ +typedef int* rfbProtocolExtensionHookPseudoEncodings; +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookPseudoEncodings), "extension hook size doesn't match"); + +/** returns TRUE if that pseudo encoding is handled by the extension. + encodingNumber==0 means "reset encodings". */ +typedef rfbBool (*rfbProtocolExtensionHookEnablePseudoEncoding)(struct _rfbClientRec* client, + void** data, int encodingNumber); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookEnablePseudoEncoding), "extension hook size doesn't match"); + +/** returns TRUE if message was handled */ +typedef rfbBool (*rfbProtocolExtensionHookHandleMessage)(struct _rfbClientRec* client, + void* data, + const rfbClientToServerMsg* message); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookHandleMessage), "extension hook size doesn't match"); + +typedef void (*rfbProtocolExtensionHookClose)(struct _rfbClientRec* client, void* data); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookClose), "extension hook size doesn't match"); + +typedef void (*rfbProtocolExtensionHookUsage)(void); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookUsage), "extension hook size doesn't match"); + +/** processArguments returns the number of handled arguments */ +typedef int (*rfbProtocolExtensionHookProcessArgument)(int argc, char *argv[]); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookProcessArgument), "extension hook size doesn't match"); + +typedef struct _rfbProtocolExtensionElement { + union { + /** for the type 1 extensions */ + rfbProtocolExtensionHookGeneric generic; + + rfbProtocolExtensionHookNewClient newClient; + rfbProtocolExtensionHookInit init; + rfbProtocolExtensionHookPseudoEncodings pseudoEncodings; + rfbProtocolExtensionHookEnablePseudoEncoding enablePseudoEncoding; + rfbProtocolExtensionHookHandleMessage handleMessage; + rfbProtocolExtensionHookClose close; + rfbProtocolExtensionHookUsage usage; + rfbProtocolExtensionHookProcessArgument processArgument; + } hook; + /** which hook it is */ + int type; +} rfbProtocolExtensionElement; + +typedef struct _rfbProtocolExtension2 { + rfbProtocolExtensionElement* elements; + size_t elementsCount; + struct _rfbProtocolExtension2* next; +} rfbProtocolExtension2; + /** * Per-screen (framebuffer) structure. There can be as many as you wish, * each serving different clients. However, you have to call @@ -707,6 +787,8 @@ typedef struct _rfbClientRec { int tightPngDstDataLen; #endif #endif + + struct _rfbExtension2Data* extensions2; } rfbClientRec, *rfbClientPtr; /** @@ -1039,6 +1121,15 @@ rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension, rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension); void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension); +void rfbRegisterProtocolExtension2(rfbProtocolExtension2* extension2); +void rfbUnregisterProtocolExtension2(rfbProtocolExtension2* extension2); +struct _rfbProtocolExtension2* rfbGetExtension2Iterator(void); +void rfbReleaseExtension2Iterator(void); +rfbBool rfbEnableExtension2(rfbClientPtr cl, rfbProtocolExtension2* extension2, + void* data); +rfbBool rfbDisableExtension2(rfbClientPtr cl, rfbProtocolExtension2* extension2); +void* rfbGetExtension2ClientData(rfbClientPtr cl, rfbProtocolExtension2* extension2); + /** to check against plain passwords */ rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len); diff --git a/src/libvncserver/cargs.c b/src/libvncserver/cargs.c index 277438e1c..d238e07bf 100644 --- a/src/libvncserver/cargs.c +++ b/src/libvncserver/cargs.c @@ -14,13 +14,13 @@ #include +#include "private.h" + extern int rfbStringToAddr(char *str, in_addr_t *iface); void rfbUsage(void) { - rfbProtocolExtension* extension; - fprintf(stderr, "-rfbport port TCP port for RFB protocol\n"); #ifdef LIBVNCSERVER_IPv6 fprintf(stderr, "-rfbportv6 port TCP6 port for RFB protocol\n"); @@ -61,10 +61,24 @@ rfbUsage(void) fprintf(stderr, " addr ipv6addr. '-listen localhost' and hostname work too.\n"); #endif - for(extension=rfbGetExtensionIterator();extension;extension=extension->next) - if(extension->usage) - extension->usage(); - rfbReleaseExtensionIterator(); + rfbProtocolExtension2* extension2 = rfbGetExtension2Iterator(); + for (; extension2; extension2 = extension2->next) { + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + rfbProtocolExtensionHookUsage ptrUsage = NULL; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_USAGE) { + ptrUsage = el->hook.usage; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrUsage = extension->usage; + } + if (ptrUsage) { + ptrUsage(); + break; + } + } + } + rfbReleaseExtension2Iterator(); } /* purges COUNT arguments from ARGV at POSITION and decrements ARGC. @@ -217,14 +231,26 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) rfbScreen->sslcertfile = argv[++i]; #endif } else { - rfbProtocolExtension* extension; + rfbProtocolExtension2* extension2; int handled=0; - for(extension=rfbGetExtensionIterator();handled==0 && extension; - extension=extension->next) - if(extension->processArgument) - handled = extension->processArgument(*argc - i, argv + i); - rfbReleaseExtensionIterator(); + for (extension2 = rfbGetExtension2Iterator(); handled == 0 && extension2; extension2 = extension2->next) { + rfbProtocolExtensionHookProcessArgument ptrProcessArgument = NULL; + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_PROCESS_ARGUMENT) { + ptrProcessArgument = el->hook.processArgument; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrProcessArgument = extension->processArgument; + } + if (ptrProcessArgument) { + handled = ptrProcessArgument(*argc - i, argv + i); + break; + } + } + } + rfbReleaseExtension2Iterator(); if(handled==0) { i++; diff --git a/src/libvncserver/main.c b/src/libvncserver/main.c index 32519f14a..187e1358d 100644 --- a/src/libvncserver/main.c +++ b/src/libvncserver/main.c @@ -59,7 +59,49 @@ char rfbEndianTest = (1==1); * Protocol extensions */ -static rfbProtocolExtension* rfbExtensionHead = NULL; +static rfbProtocolExtension2* rfbExtension2Head = NULL; +static rfbProtocolExtension* rfbExtensionViewHead = NULL; + +static rfbBool rfbIsSameExtension(rfbProtocolExtension2* extension2, rfbProtocolExtension* extension) +{ + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1 && + (rfbProtocolExtensionHookExtension1) el->hook.generic == extension) { + return TRUE; + } + } + return FALSE; +} + +static rfbProtocolExtension2* rfbCreateExtension2FromExtension1(rfbProtocolExtension* extension1) +{ + rfbProtocolExtension2* extension2 = (rfbProtocolExtension2*) malloc(sizeof(rfbProtocolExtension2)); + if (!extension2) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return extension2; + } + + extension2->elements = (rfbProtocolExtensionElement*) malloc(sizeof(rfbProtocolExtensionElement)); + if (!extension2->elements) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + free(extension2); + extension2 = NULL; + return extension2; + } + + extension2->elements[0].type = RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1; + extension2->elements[0].hook.generic = (rfbProtocolExtensionHookGeneric) extension1; + extension2->elementsCount = 1; + + return extension2; +} + +static void rfbDestroyExtension2FromExtension1(rfbProtocolExtension2* extension2) +{ + free(extension2->elements); + free(extension2); +} /* * This method registers a list of new extensions. @@ -70,7 +112,9 @@ static rfbProtocolExtension* rfbExtensionHead = NULL; void rfbRegisterProtocolExtension(rfbProtocolExtension* extension) { - rfbProtocolExtension *head = rfbExtensionHead, *next = NULL; + rfbProtocolExtension2* head = rfbExtension2Head; + rfbProtocolExtension* next = NULL; + rfbProtocolExtension2* extension2 = NULL; if(extension == NULL) return; @@ -85,7 +129,7 @@ rfbRegisterProtocolExtension(rfbProtocolExtension* extension) LOCK(extMutex); while(head != NULL) { - if(head == extension) { + if(rfbIsSameExtension(head, extension)) { UNLOCK(extMutex); rfbRegisterProtocolExtension(next); return; @@ -94,8 +138,13 @@ rfbRegisterProtocolExtension(rfbProtocolExtension* extension) head = head->next; } - extension->next = rfbExtensionHead; - rfbExtensionHead = extension; + extension2 = rfbCreateExtension2FromExtension1(extension); + if (!extension2) { + return; + } + + extension2->next = rfbExtension2Head; + rfbExtension2Head = extension2; UNLOCK(extMutex); rfbRegisterProtocolExtension(next); @@ -110,7 +159,7 @@ void rfbUnregisterProtocolExtension(rfbProtocolExtension* extension) { - rfbProtocolExtension *cur = NULL, *pre = NULL; + rfbProtocolExtension2 *cur = NULL, *pre = NULL, *next = NULL; if(extension == NULL) return; @@ -122,18 +171,22 @@ rfbUnregisterProtocolExtension(rfbProtocolExtension* extension) LOCK(extMutex); - if(rfbExtensionHead == extension) { - rfbExtensionHead = rfbExtensionHead->next; + if(rfbIsSameExtension(rfbExtension2Head, extension)) { + next = rfbExtension2Head->next; + rfbDestroyExtension2FromExtension1(rfbExtension2Head); + rfbExtension2Head = next; UNLOCK(extMutex); rfbUnregisterProtocolExtension(extension->next); return; } - cur = pre = rfbExtensionHead; + cur = pre = rfbExtension2Head; while(cur) { - if(cur == extension) { - pre->next = cur->next; + if(rfbIsSameExtension(cur, extension)) { + next = cur->next; + rfbDestroyExtension2FromExtension1(cur); + pre->next = next; break; } pre = cur; @@ -147,13 +200,30 @@ rfbUnregisterProtocolExtension(rfbProtocolExtension* extension) rfbProtocolExtension* rfbGetExtensionIterator(void) { + rfbProtocolExtension2 *curr2; + if (! extMutex_initialized) { INIT_MUTEX(extMutex); extMutex_initialized = 1; } LOCK(extMutex); - return rfbExtensionHead; + + rfbExtensionViewHead = NULL; + + for (curr2 = rfbExtension2Head; curr2; curr2 = curr2->next) { + rfbProtocolExtensionElement* el = curr2->elements; + for (; el && el < curr2->elements + curr2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension *found1 = (rfbProtocolExtensionHookExtension1) el->hook.generic; + found1->next = rfbExtensionViewHead; + rfbExtensionViewHead = found1; + break; + } + } + } + + return rfbExtensionViewHead; } void rfbReleaseExtensionIterator(void) @@ -164,43 +234,38 @@ void rfbReleaseExtensionIterator(void) 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); - if(!extData) - return FALSE; - extData->extension = extension; - extData->data = data; - extData->next = cl->extensions; - cl->extensions = extData; - - return TRUE; + rfbProtocolExtension2* extension2Found = NULL; + rfbProtocolExtension2* extension2 = rfbGetExtension2Iterator(); + for (; extension2 && !extension2Found; extension2 = extension2->next) { + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount && !extension2Found; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1 && (rfbProtocolExtensionHookExtension1) el->hook.generic == extension) { + extension2Found = extension2; + } + } + + } + rfbReleaseExtension2Iterator(); + + return extension2Found ? rfbEnableExtension2(cl, extension2Found, data) : FALSE; } 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; - } + rfbProtocolExtension2* extension2Found = NULL; + rfbProtocolExtension2* extension2 = rfbGetExtension2Iterator(); + for (; extension2 && !extension2Found; extension2 = extension2->next) { + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount && !extension2Found; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1 && (rfbProtocolExtensionHookExtension1) el->hook.generic == extension) { + extension2Found = extension2; + } + } + + } + rfbReleaseExtension2Iterator(); - return FALSE; + return extension2Found ? rfbDisableExtension2(cl, extension2Found) : FALSE; } void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension) @@ -219,6 +284,204 @@ void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension return data->data; } +void rfbRegisterProtocolExtension2(rfbProtocolExtension2* extension2) +{ + rfbProtocolExtension2 *head = rfbExtension2Head, *next = NULL; + + if (extension2 == NULL) + return; + + next = extension2->next; + + if (!extMutex_initialized) { + INIT_MUTEX(extMutex); + extMutex_initialized = 1; + } + + LOCK(extMutex); + + while (head != NULL) { + if (head == extension2) { + UNLOCK(extMutex); + rfbRegisterProtocolExtension2(next); + return; + } + + head = head->next; + } + + extension2->next = rfbExtension2Head; + rfbExtension2Head = extension2; + + UNLOCK(extMutex); + rfbRegisterProtocolExtension2(next); +} + +void rfbUnregisterProtocolExtension2(rfbProtocolExtension2* extension2) +{ + rfbProtocolExtension2 *cur = NULL, *pre = NULL; + + if (extension2 == NULL) + return; + + if (!extMutex_initialized) { + INIT_MUTEX(extMutex); + extMutex_initialized = 1; + } + + LOCK(extMutex); + + if (rfbExtension2Head == extension2) { + rfbExtension2Head = rfbExtension2Head->next; + UNLOCK(extMutex); + rfbUnregisterProtocolExtension2(extension2->next); + return; + } + + cur = pre = rfbExtension2Head; + + while (cur) { + if (cur == extension2) { + pre->next = cur->next; + break; + } + pre = cur; + cur = cur->next; + } + + UNLOCK(extMutex); + + rfbUnregisterProtocolExtension2(extension2->next); +} + +struct _rfbProtocolExtension2* rfbGetExtension2Iterator(void) +{ + if (!extMutex_initialized) { + INIT_MUTEX(extMutex); + extMutex_initialized = 1; + } + + LOCK(extMutex); + + return rfbExtension2Head; +} + +void rfbReleaseExtension2Iterator(void) +{ + UNLOCK(extMutex); +} + +rfbBool rfbEnableExtension2(rfbClientPtr cl, rfbProtocolExtension2* extension2, + void* data) +{ + rfbExtensionData* extData = NULL; + + /* make sure extension is not yet enabled. */ + rfbExtension2Data* ext2Data = cl->extensions2; + for (; ext2Data; ext2Data = ext2Data->next) + if (ext2Data->extension2 == extension2) + return FALSE; + + /* prepare extension 1 data. */ + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + + /* 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); + if (!extData) { + return FALSE; + } + extData->extension = extension; + extData->data = data; + extData->next = cl->extensions; + + break; + } + } + + ext2Data = calloc(sizeof(rfbExtension2Data), 1); + if (!ext2Data) { + free(extData); + return FALSE; + } + ext2Data->extension2 = extension2; + ext2Data->data = data; + ext2Data->next = cl->extensions2; + cl->extensions2 = ext2Data; + + if (extData) { + cl->extensions = extData; + } + + return TRUE; +} + +rfbBool rfbDisableExtension2(rfbClientPtr cl, rfbProtocolExtension2* extension2) +{ + rfbExtension2Data* prev2Data = NULL; + rfbExtension2Data* ext2Data = cl->extensions2; + for (; ext2Data; ext2Data = ext2Data->next) { + if (ext2Data->extension2 == extension2) { + free(ext2Data->data); + if (prev2Data == NULL) + cl->extensions2 = ext2Data->next; + else + prev2Data->next = ext2Data->next; + free(ext2Data); + + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + + rfbExtensionData* prevData = NULL; + rfbExtensionData* extData = cl->extensions; + for (; extData; extData = extData->next) { + if (extData->extension == extension) { + if (prevData == NULL) + cl->extensions = extData->next; + else + prevData->next = extData->next; + free(extData); + break; + } + prevData = extData; + } + + break; + } + } + + return TRUE; + } + prev2Data = ext2Data; + } + + return FALSE; +} + +void* rfbGetExtension2ClientData(rfbClientPtr cl, rfbProtocolExtension2* extension2) +{ + rfbExtension2Data* data = cl->extensions2; + + while(data && data->extension2 != extension2) + data = data->next; + + if(data == NULL) { + rfbLog("Extension is not enabled !\n"); + /* rfbCloseClient(cl); */ + return NULL; + } + + return data->data; +} + /* * Logging */ diff --git a/src/libvncserver/private.h b/src/libvncserver/private.h index 6f28a2956..bdf0cea9e 100644 --- a/src/libvncserver/private.h +++ b/src/libvncserver/private.h @@ -9,6 +9,20 @@ void rfbRedrawAfterHideCursor(rfbClientPtr cl,sraRegionPtr updateRegion); /* from main.c */ +enum rfbProtocolExtensionHookTypeReserved { + RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1 = 1, +}; + +/** pointer to the type 1 extension */ +typedef rfbProtocolExtension* rfbProtocolExtensionHookExtension1; +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookExtension1), "extension hook size doesn't match"); + +typedef struct _rfbExtension2Data { + struct _rfbProtocolExtension2* extension2; + void* data; + struct _rfbExtension2Data* next; +} rfbExtension2Data; + rfbClientPtr rfbClientIteratorHead(rfbClientIteratorPtr i); /* from tight.c */ diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index a80fd1aee..9fd15882f 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -312,7 +312,6 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, struct sockaddr_in addr; #endif socklen_t addrlen = sizeof(addr); - rfbProtocolExtension* extension; cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); @@ -463,6 +462,7 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->progressiveSliceY = 0; cl->extensions = NULL; + cl->extensions2 = NULL; cl->lastPtrX = -1; @@ -504,15 +504,29 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, } } - for(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); + rfbProtocolExtension2* extension2 = rfbGetExtension2Iterator(); + for (; extension2; extension2 = extension2->next) { + rfbProtocolExtensionHookNewClient ptrNewClient; + /* if the extension does not have a newClient method, it wants + * to be initialized later. */ + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_NEW_CLIENT) { + ptrNewClient = el->hook.newClient; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrNewClient = extension->newClient; + } + if (ptrNewClient) { + void* data = NULL; + if (ptrNewClient(cl, &data)) { + rfbEnableExtension2(cl, extension2, data); + } + break; + } + } } - rfbReleaseExtensionIterator(); + rfbReleaseExtension2Iterator(); switch (cl->screen->newClientHook(cl)) { case RFB_CLIENT_ON_HOLD: @@ -812,7 +826,6 @@ rfbProcessClientInitMessage(rfbClientPtr cl) int len, n; rfbClientIteratorPtr iterator; rfbClientPtr otherCl; - rfbExtensionData* extension; if (cl->state == RFB_INITIALISATION_SHARED) { /* In this case behave as though an implicit ClientInit message has @@ -851,13 +864,28 @@ rfbProcessClientInitMessage(rfbClientPtr cl) return; } - for(extension = cl->extensions; extension;) { - rfbExtensionData* next = extension->next; - if(extension->extension->init && - !extension->extension->init(cl, extension->data)) - /* extension requested that it be removed */ - rfbDisableExtension(cl, extension->extension); - extension = next; + rfbExtension2Data* extension2Data = cl->extensions2; + for (; extension2Data; extension2Data = extension2Data->next) { + rfbProtocolExtensionElement* el = extension2Data->extension2->elements; + for (; el && el < extension2Data->extension2->elements + extension2Data->extension2->elementsCount; ++el) { + rfbProtocolExtensionHookInit ptrInit = NULL; + void* data; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_INIT) { + ptrInit = el->hook.init; + data = extension2Data->data; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrInit = extension->init; + data = rfbGetExtensionClientData(cl, extension); + } + if (ptrInit) { + if (!ptrInit(cl, data)) { + /* extension requested that it be removed */ + rfbDisableExtension2(cl, extension2Data->extension2); + } + break; + } + } } cl->state = RFB_NORMAL; @@ -2509,54 +2537,86 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) #endif } else #endif - { - rfbExtensionData* e; - for(e = cl->extensions; e;) { - rfbExtensionData* next = e->next; - if(e->extension->enablePseudoEncoding && - e->extension->enablePseudoEncoding(cl, - &e->data, (int)enc)) - /* ext handles this encoding */ - break; - e = next; - } - if(e == NULL) { - rfbBool handled = FALSE; - /* if the pseudo encoding is not handled by the - enabled extensions, search through all - extensions. */ - rfbProtocolExtension* e; - - for(e = rfbGetExtensionIterator(); e;) { - int* encs = e->pseudoEncodings; - while(encs && *encs!=0) { - if(*encs==(int)enc) { - void* data = NULL; - if(!e->enablePseudoEncoding(cl, &data, (int)enc)) { - rfbLog("Installed extension pretends to handle pseudo encoding 0x%x, but does not!\n",(int)enc); - } else { - rfbEnableExtension(cl, e, data); - handled = TRUE; - e = NULL; - break; - } - } - encs++; - } - - if(e) - e = e->next; - } - rfbReleaseExtensionIterator(); - - if(!handled) - rfbLog("rfbProcessClientNormalMessage: " - "ignoring unsupported encoding type %s\n", - encodingName(enc,encBuf,sizeof(encBuf))); - } - } - } + { + rfbBool handled = FALSE; + + rfbExtension2Data* extension2Data = cl->extensions2; + for (; !handled && extension2Data; extension2Data = extension2Data->next) { + rfbProtocolExtensionElement* el = extension2Data->extension2->elements; + for (; el && el < extension2Data->extension2->elements + extension2Data->extension2->elementsCount; ++el) { + rfbProtocolExtensionHookEnablePseudoEncoding ptrEnablePseudoEncoding = NULL; + void* data; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_ENABLE_PSEUDO_ENCODING) { + ptrEnablePseudoEncoding = el->hook.enablePseudoEncoding; + data = extension2Data->data; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrEnablePseudoEncoding = extension->enablePseudoEncoding; + data = rfbGetExtensionClientData(cl, extension); + } + if (ptrEnablePseudoEncoding) { + if ((*ptrEnablePseudoEncoding)(cl, &data, (int) enc)) { + handled = TRUE; + } + break; + } + } + } + if (!handled) { + /* if the pseudo encoding is not handled by the + enabled extensions, search through all + extensions. */ + rfbProtocolExtension2 *extension2 = rfbGetExtension2Iterator(); + for (; !handled && extension2; extension2 = extension2->next) { + int* encs = NULL; + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_PSEUDO_ENCODINGS) { + encs = el->hook.pseudoEncodings; + break; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + encs = extension->pseudoEncodings; + break; + } + } + for (; !handled && encs && *encs != 0; ++encs) { + if (*encs == (int) enc) { + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + rfbProtocolExtensionHookEnablePseudoEncoding ptrEnablePseudoEncoding = NULL; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_ENABLE_PSEUDO_ENCODING) { + ptrEnablePseudoEncoding = el->hook.enablePseudoEncoding; + break; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrEnablePseudoEncoding = extension->enablePseudoEncoding; + break; + } + if (ptrEnablePseudoEncoding) { + void* data = NULL; + if (!ptrEnablePseudoEncoding(cl, &data, (int) enc)) { + rfbLog("Installed extension pretends to handle pseudo encoding 0x%x, but does not!\n", (int) enc); + } else { + rfbEnableExtension2(cl, extension2, data); + handled = TRUE; + break; + } + } + } + } + } + } + rfbReleaseExtension2Iterator(); + + if (!handled) + rfbLog("rfbProcessClientNormalMessage: " + "ignoring unsupported encoding type %s\n", + encodingName(enc, encBuf, sizeof(encBuf))); + } + } } + } @@ -3079,18 +3139,29 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) default: { - rfbExtensionData *e,*next; - - for(e=cl->extensions; e;) { - next = e->next; - if(e->extension->handleMessage && - e->extension->handleMessage(cl, e->data, &msg)) - { - rfbStatRecordMessageRcvd(cl, msg.type, 0, 0); /* Extension should handle this */ - return; + rfbExtension2Data* extension2Data = cl->extensions2; + for (; extension2Data; extension2Data = extension2Data->next) { + rfbProtocolExtensionElement* el = extension2Data->extension2->elements; + for (; el && el < extension2Data->extension2->elements + extension2Data->extension2->elementsCount; ++el) { + rfbProtocolExtensionHookHandleMessage ptrHandleMessage = NULL; + void *data; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_HANDLE_MESSAGE) { + ptrHandleMessage = el->hook.handleMessage; + data = extension2Data->data; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrHandleMessage = extension->handleMessage; + data = rfbGetExtensionClientData(cl, extension); + } + if (ptrHandleMessage) { + if (ptrHandleMessage(cl, data, &msg)) { + rfbStatRecordMessageRcvd(cl, msg.type, 0, 0); /* Extension should handle this */ + return; + } + break; + } } - e = next; - } + } rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n", msg.type); diff --git a/src/libvncserver/sockets.c b/src/libvncserver/sockets.c index 1bf6cbf15..9061f165a 100644 --- a/src/libvncserver/sockets.c +++ b/src/libvncserver/sockets.c @@ -96,6 +96,7 @@ int allow_severity=LOG_INFO; int deny_severity=LOG_WARNING; #endif +#include "private.h" #include "sockets.h" int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has @@ -548,11 +549,27 @@ rfbCloseClient(rfbClientPtr cl) #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION cl->sock = RFB_INVALID_SOCKET; #endif - rfbExtensionData* extension; - for(extension=cl->extensions; extension; extension=extension->next) - if(extension->extension->close) - extension->extension->close(cl, extension->data); + rfbExtension2Data* extension2Data = cl->extensions2; + for (; extension2Data; extension2Data = extension2Data->next) { + rfbProtocolExtensionHookClose ptrClose = NULL; + rfbProtocolExtensionElement* el = extension2Data->extension2->elements; + for (; el && el < extension2Data->extension2->elements + extension2Data->extension2->elementsCount; ++el) { + void* data; + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_CLOSE) { + ptrClose = el->hook.close; + data = extension2Data->data; + } else if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_EXTENSION1) { + rfbProtocolExtension* extension = (rfbProtocolExtensionHookExtension1) el->hook.generic; + ptrClose = extension->close; + data = rfbGetExtensionClientData(cl, extension); + } + if (ptrClose) { + (*ptrClose)(cl, data); + break; + } + } + } LOCK(cl->updateMutex); #if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) From eb831231086ea9ae1da94e6c860d059d42dd7574 Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Thu, 4 May 2023 10:21:05 +0200 Subject: [PATCH 2/3] libvncserver: fbu hooks --- include/rfb/rfb.h | 12 ++++++++++++ src/libvncserver/rfbserver.c | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index a1ff520dd..f5ac32ffe 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -207,6 +207,8 @@ enum rfbProtocolExtensionHookType { RFB_PROTOCOL_EXTENSION_HOOK_CLOSE, RFB_PROTOCOL_EXTENSION_HOOK_USAGE, RFB_PROTOCOL_EXTENSION_HOOK_PROCESS_ARGUMENT, + RFB_PROTOCOL_EXTENSION_HOOK_PRE_FBU, + RFB_PROTOCOL_EXTENSION_HOOK_POST_FBU, }; typedef void* rfbProtocolExtensionHookGeneric; @@ -248,6 +250,13 @@ _Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExte typedef int (*rfbProtocolExtensionHookProcessArgument)(int argc, char *argv[]); _Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookProcessArgument), "extension hook size doesn't match"); +/** returns TRUE if proceed with the framebuffer update (PostFbu is called in any case). */ +typedef rfbBool (*rfbProtocolExtensionHookPreFbu)(struct _rfbClientRec* client, void* data); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookPreFbu), "extension hook size doesn't match"); + +typedef void (*rfbProtocolExtensionHookPostFbu)(struct _rfbClientRec* client, void* data); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookPostFbu), "extension hook size doesn't match"); + typedef struct _rfbProtocolExtensionElement { union { /** for the type 1 extensions */ @@ -261,6 +270,9 @@ typedef struct _rfbProtocolExtensionElement { rfbProtocolExtensionHookClose close; rfbProtocolExtensionHookUsage usage; rfbProtocolExtensionHookProcessArgument processArgument; + + rfbProtocolExtensionHookPreFbu preFbu; + rfbProtocolExtensionHookPostFbu postFbu; } hook; /** which hook it is */ int type; diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index 9fd15882f..8d28a8759 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -3411,6 +3411,22 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, * Now send the update. */ + rfbBool extensionsAllowProceed = TRUE; + rfbExtension2Data *e = cl->extensions2; + for (; e; e = e->next) { + rfbProtocolExtensionElement* el = e->extension2->elements; + for (; el && el < e->extension2->elements + e->extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_PRE_FBU) { + extensionsAllowProceed = extensionsAllowProceed && el->hook.preFbu(cl, e->data); + break; + } + } + } + + if (!extensionsAllowProceed) { + goto updateFailed; + } + rfbStatRecordMessageSent(cl, rfbFramebufferUpdate, 0, 0); if (cl->preferredEncoding == rfbEncodingCoRRE) { nUpdateRegionRects = 0; @@ -3647,6 +3663,17 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, result = FALSE; } + e = cl->extensions2; + for (; e; e = e->next) { + rfbProtocolExtensionElement* el = e->extension2->elements; + for (; el && el < e->extension2->elements + e->extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_POST_FBU) { + el->hook.postFbu(cl, e->data); + break; + } + } + } + if (!cl->enableCursorShapeUpdates) { rfbHideCursor(cl); } From 3257de815865c7b039dfdf88c2efd7de801b2800 Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Fri, 5 May 2023 09:38:26 +0200 Subject: [PATCH 3/3] libvncserver: post SetEncodings hook --- include/rfb/rfb.h | 5 +++++ src/libvncserver/rfbserver.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index f5ac32ffe..fbd5c294b 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -207,6 +207,7 @@ enum rfbProtocolExtensionHookType { RFB_PROTOCOL_EXTENSION_HOOK_CLOSE, RFB_PROTOCOL_EXTENSION_HOOK_USAGE, RFB_PROTOCOL_EXTENSION_HOOK_PROCESS_ARGUMENT, + RFB_PROTOCOL_EXTENSION_HOOK_POST_SET_ENCODINGS, RFB_PROTOCOL_EXTENSION_HOOK_PRE_FBU, RFB_PROTOCOL_EXTENSION_HOOK_POST_FBU, }; @@ -250,6 +251,9 @@ _Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExte typedef int (*rfbProtocolExtensionHookProcessArgument)(int argc, char *argv[]); _Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookProcessArgument), "extension hook size doesn't match"); +typedef void (*rfbProtocolExtensionHookPostSetEncodings)(struct _rfbClientRec* client); +_Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookPostSetEncodings), "extension hook size doesn't match"); + /** returns TRUE if proceed with the framebuffer update (PostFbu is called in any case). */ typedef rfbBool (*rfbProtocolExtensionHookPreFbu)(struct _rfbClientRec* client, void* data); _Static_assert(sizeof(rfbProtocolExtensionHookGeneric) == sizeof(rfbProtocolExtensionHookPreFbu), "extension hook size doesn't match"); @@ -271,6 +275,7 @@ typedef struct _rfbProtocolExtensionElement { rfbProtocolExtensionHookUsage usage; rfbProtocolExtensionHookProcessArgument processArgument; + rfbProtocolExtensionHookPostSetEncodings postSetEncodings; rfbProtocolExtensionHookPreFbu preFbu; rfbProtocolExtensionHookPostFbu postFbu; } hook; diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index 8d28a8759..229aec148 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -2647,6 +2647,18 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableCursorPosUpdates = FALSE; } + rfbProtocolExtension2 *extension2 = rfbGetExtension2Iterator(); + for (; extension2; extension2 = extension2->next) { + rfbProtocolExtensionElement* el = extension2->elements; + for (; el && el < extension2->elements + extension2->elementsCount; ++el) { + if (el->type == RFB_PROTOCOL_EXTENSION_HOOK_POST_SET_ENCODINGS) { + el->hook.postSetEncodings(cl); + break; + } + } + } + rfbReleaseExtension2Iterator(); + return; }