diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index 79a446f1..bdd40341 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -138,6 +138,9 @@ typedef int (*rfbFileTransferPermitted) (struct _rfbClientRec* cl); /** Handle the textchat messages */ typedef void (*rfbSetTextChat) (struct _rfbClientRec* cl, int length, char *string); +/* error handling (server side) */ +typedef void (*ClientErrorChangedProc)(struct _rfbClientRec *client); + typedef struct { uint32_t count; rfbBool is16; /**< is the data format short? */ @@ -302,6 +305,8 @@ typedef struct _rfbScreenInfo rfbFileTransferPermitted getFileTransferPermission; rfbSetTextChat setTextChat; + ClientErrorChangedProc clientErrorChanged; + /** newClientHook is called just after a new client is created */ rfbNewClientHookPtr newClientHook; /** displayHook is called just before a frame buffer update */ @@ -440,6 +445,13 @@ typedef struct _rfbClientRec { * This is useful if the IO functions have to behave client specific. */ void* clientData; + + /** + * error handling + */ + + char* lastError; + ClientGoneHookPtr clientGoneHook; rfbSocket sock; @@ -1017,6 +1029,8 @@ extern rfbBool rfbProcessSizeArguments(int* width,int* height,int* bpp,int* argc extern void rfbLogEnable(int enabled); typedef void (*rfbLogProc)(const char *format, ...); +typedef void (*rfbClientSetErrProc)(rfbClientPtr cl, const char *format, ...); +extern rfbClientSetErrProc rfbClientSetErr; extern rfbLogProc rfbLog, rfbErr; extern void rfbLogPerror(const char *str); diff --git a/src/libvncserver/auth.c b/src/libvncserver/auth.c index 748027b1..5731efc6 100644 --- a/src/libvncserver/auth.c +++ b/src/libvncserver/auth.c @@ -382,6 +382,7 @@ rfbAuthProcessClientMessage(rfbClientPtr cl) if(!cl->screen->passwordCheck(cl,(const char*)response,CHALLENGESIZE)) { rfbErr("rfbAuthProcessClientMessage: password check failed\n"); + rfbClientSetErr(cl, "rfbAuthProcessClientMessage: password check failed"); authResult = Swap32IfLE(rfbVncAuthFailed); if (rfbWriteExact(cl, (char *)&authResult, 4) < 0) { rfbLogPerror("rfbAuthProcessClientMessage: write"); diff --git a/src/libvncserver/main.c b/src/libvncserver/main.c index 1efa8387..9385f70b 100644 --- a/src/libvncserver/main.c +++ b/src/libvncserver/main.c @@ -260,8 +260,35 @@ rfbDefaultLog(const char *format, ...) UNLOCK(logMutex); } +static void rfbDefaultClientSetErr(rfbClientPtr cl, const char *format, ...) { + // props to josemr goudetalvim (@josealvim) for this logic + va_list ap; + va_start(ap, format); + size_t len = (size_t)vsnprintf(NULL, 0, format, ap); + va_end(ap); + + char *buffer = malloc(len + 1); + if (!buffer) + return; + + va_start(ap, format); + vsprintf(buffer, format, ap); + + va_end(ap); + + char *oldError = cl->lastError; + cl->lastError = buffer; + + // call the callback if it exists + if (cl->screen->clientErrorChanged != NULL) + cl->screen->clientErrorChanged(cl); + + free(oldError); +} + rfbLogProc rfbLog=rfbDefaultLog; rfbLogProc rfbErr=rfbDefaultLog; +rfbClientSetErrProc rfbClientSetErr = rfbDefaultClientSetErr; void rfbLogPerror(const char *str) { diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index a80fd1ae..2e2b05a9 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -660,6 +660,7 @@ rfbClientConnectionGone(rfbClientPtr cl) rfbPrintStats(cl); rfbResetStats(cl); + free(cl->lastError); free(cl); } @@ -715,7 +716,9 @@ rfbProcessClientProtocolVersion(rfbClientPtr cl) pv[sz_rfbProtocolVersionMsg] = 0; if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) { rfbErr("rfbProcessClientProtocolVersion: not a valid RFB client: %s\n", pv); - rfbCloseClient(cl); + rfbClientSetErr( + cl, "rfbProcessClientProtocolVersion: not a valid RFB client: %s\n", pv); + rfbCloseClient(cl); return; } rfbLog("Client Protocol Version %d.%d\n", major_, minor_);