@ -77,6 +77,9 @@
# define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
# define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */
# define WS_HYBI_MASK_LEN 4
# define ARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))) / (size_t)(!(sizeof(a) % sizeof((a[0])))))
enum {
WEBSOCKETS_VERSION_HIXIE ,
@ -93,20 +96,20 @@ static int gettid() {
typedef int ( * wsEncodeFunc ) ( rfbClientPtr cl , const char * src , int len , char * * dst ) ;
typedef int ( * wsDecodeFunc ) ( rfbClientPtr cl , char * dst , int len ) ;
typedef struct ws_ctx_s {
char codeBufDecode [ B64LEN ( UPDATE_BUF_SIZE ) + WSHLENMAX ] ; /* base64 + maximum frame header length */
char codeBufEncode [ B64LEN ( UPDATE_BUF_SIZE ) + WSHLENMAX ] ; /* base64 + maximum frame header length */
char readbuf [ 8192 ] ;
int readbufstart ;
int readbuflen ;
int dblen ;
char carryBuf [ 3 ] ; /* For base64 carry-over */
int carrylen ;
int version ;
int base64 ;
wsEncodeFunc encode ;
wsDecodeFunc decode ;
} ws_ctx_t ;
enum {
/* header not yet received completely */
WS_HYBI_STATE_HEADER_PENDING ,
/* data available */
WS_HYBI_STATE_DATA_AVAILABLE ,
WS_HYBI_STATE_DATA_NEEDED ,
/* received a complete frame */
WS_HYBI_STATE_FRAME_COMPLETE ,
/* received part of a 'close' frame */
WS_HYBI_STATE_CLOSE_REASON_PENDING ,
/* */
WS_HYBI_STATE_ERR
} ;
typedef union ws_mask_s {
char c [ 4 ] ;
@ -146,6 +149,38 @@ __attribute__ ((__packed__))
} u ;
} ws_header_t ;
typedef struct ws_header_data_s {
ws_header_t * data ;
/** bytes read */
int nRead ;
/** mask value */
ws_mask_t mask ;
/** length of frame header including payload len, but without mask */
int headerLen ;
/** length of the payload data */
int payloadLen ;
/** opcode */
unsigned char opcode ;
} ws_header_data_t ;
typedef struct ws_ctx_s {
char codeBufDecode [ B64LEN ( UPDATE_BUF_SIZE ) + WSHLENMAX ] ; /* base64 + maximum frame header length */
char codeBufEncode [ B64LEN ( UPDATE_BUF_SIZE ) + WSHLENMAX ] ; /* base64 + maximum frame header length */
char * writePos ;
unsigned char * readPos ;
int readlen ;
int hybiDecodeState ;
char carryBuf [ 3 ] ; /* For base64 carry-over */
int carrylen ;
int version ;
int base64 ;
ws_header_data_t header ;
int nReadRaw ;
int nToRead ;
wsEncodeFunc encode ;
wsDecodeFunc decode ;
} ws_ctx_t ;
enum
{
WS_OPCODE_CONTINUATION = 0x0 ,
@ -206,6 +241,8 @@ static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char
static int webSocketsDecodeHybi ( rfbClientPtr cl , char * dst , int len ) ;
static int webSocketsDecodeHixie ( rfbClientPtr cl , char * dst , int len ) ;
static void hybiDecodeCleanup ( ws_ctx_t * wsctx ) ;
static int
min ( int a , int b ) {
return a < b ? a : b ;
@ -467,10 +504,11 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)
wsctx - > decode = webSocketsDecodeHixie ;
}
wsctx - > base64 = base64 ;
hybiDecodeCleanup ( wsctx ) ;
cl - > wsctx = ( wsCtx * ) wsctx ;
return TRUE ;
}
void
webSocketsGenMd5 ( char * target , char * key1 , char * key2 , char * key3 )
{
@ -662,146 +700,439 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)
}
static int
webSocketsDecodeHybi( rfbClientPtr cl , char * dst , int len )
hybiRemaining( ws_ctx_t * wsctx )
{
char * buf , * payload ;
uint32_t * payload32 ;
int ret = - 1 , result = - 1 ;
int total = 0 ;
ws_mask_t mask ;
ws_header_t * header ;
int i ;
unsigned char opcode ;
ws_ctx_t * wsctx = ( ws_ctx_t * ) cl - > wsctx ;
int flength , fhlen ;
/* int fin; */ /* not used atm */
return wsctx - > nToRead - wsctx - > nReadRaw ;
}
/* rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); */
static void
hybiDecodeCleanup ( ws_ctx_t * wsctx )
{
wsctx - > header . payloadLen = 0 ;
wsctx - > header . mask . u = 0 ;
wsctx - > nReadRaw = 0 ;
wsctx - > nToRead = 0 ;
wsctx - > carrylen = 0 ;
wsctx - > readPos = ( unsigned char * ) wsctx - > codeBufDecode ;
wsctx - > readlen = 0 ;
wsctx - > hybiDecodeState = WS_HYBI_STATE_HEADER_PENDING ;
wsctx - > writePos = NULL ;
rfbLog ( " cleaned up wsctx \n " ) ;
}
if ( wsctx - > readbuflen ) {
/* simply return what we have */
if ( wsctx - > readbuflen > len ) {
memcpy ( dst , wsctx - > readbuf + wsctx - > readbufstart , len ) ;
result = len ;
wsctx - > readbuflen - = len ;
wsctx - > readbufstart + = len ;
/**
* Return payload data that has been decoded / unmasked from
* a websocket frame .
*
* @ param [ out ] dst destination buffer
* @ param [ in ] len bytes to copy to destination buffer
* @ param [ in , out ] wsctx internal state of decoding procedure
* @ param [ out ] number of bytes actually written to dst buffer
* @ return next hybi decoding state
*/
static int
hybiReturnData ( char * dst , int len , ws_ctx_t * wsctx , int * nWritten )
{
int nextState = WS_HYBI_STATE_ERR ;
/* if we have something already decoded copy and return */
if ( wsctx - > readlen > 0 ) {
/* simply return what we have */
if ( wsctx - > readlen > len ) {
rfbLog ( " copy to %d bytes to dst buffer; readPos=%p, readLen=%d \n " , len , wsctx - > readPos , wsctx - > readlen ) ;
memcpy ( dst , wsctx - > readPos , len ) ;
* nWritten = len ;
wsctx - > readlen - = len ;
wsctx - > readPos + = len ;
nextState = WS_HYBI_STATE_DATA_AVAILABLE ;
} else {
rfbLog ( " copy to %d bytes to dst buffer; readPos=%p, readLen=%d \n " , wsctx - > readlen , wsctx - > readPos , wsctx - > readlen ) ;
memcpy ( dst , wsctx - > readPos , wsctx - > readlen ) ;
* nWritten = wsctx - > readlen ;
wsctx - > readlen = 0 ;
wsctx - > readPos = NULL ;
if ( hybiRemaining ( wsctx ) = = 0 ) {
nextState = WS_HYBI_STATE_FRAME_COMPLETE ;
} else {
memcpy ( dst , wsctx - > readbuf + wsctx - > readbufstart , wsctx - > readbuflen ) ;
result = wsctx - > readbuflen ;
wsctx - > readbuflen = 0 ;
wsctx - > readbufstart = 0 ;
nextState = WS_HYBI_STATE_DATA_NEEDED ;
}
goto spor ;
}
rfbLog ( " after copy: readPos=%p, readLen=%d \n " , wsctx - > readPos , wsctx - > readlen ) ;
} else if ( wsctx - > hybiDecodeState = = WS_HYBI_STATE_CLOSE_REASON_PENDING ) {
nextState = WS_HYBI_STATE_CLOSE_REASON_PENDING ;
}
return nextState ;
}
buf = wsctx - > codeBufDecode ;
header = ( ws_header_t * ) wsctx - > codeBufDecode ;
ret = ws_peek ( cl , buf , B64LEN ( len ) + WSHLENMAX ) ;
if ( ret < 2 ) {
/* save errno because rfbErr() will tamper it */
if ( - 1 = = ret ) {
int olderrno = errno ;
rfbErr ( " %s: peek; %m \n " , __func__ ) ;
errno = olderrno ;
} else if ( 0 = = ret ) {
result = 0 ;
} else {
errno = EAGAIN ;
}
goto spor ;
/**
* Read an RFC 6455 websocket frame ( IETF hybi working group ) .
*
* Internal state is updated according to bytes received and the
* decoding of header information .
*
* @ param [ in ] cl client ptr with ptr to raw socket and ws_ctx_t ptr
* @ param [ out ] sockRet emulated recv return value
* @ return next hybi decoding state ; WS_HYBI_STATE_HEADER_PENDING indicates
* that the header was not received completely .
*/
static int
hybiReadHeader ( rfbClientPtr cl , int * sockRet )
{
int ret ;
ws_ctx_t * wsctx = ( ws_ctx_t * ) cl - > wsctx ;
char * headerDst = wsctx - > codeBufDecode + wsctx - > nReadRaw ;
int n = WSHLENMAX - wsctx - > nReadRaw ;
rfbLog ( " header_read to %p with len=%d \n " , headerDst , n ) ;
ret = ws_read ( cl , headerDst , n ) ;
rfbLog ( " read %d bytes from socket \n " , ret ) ;
if ( ret < = 0 ) {
if ( - 1 = = ret ) {
/* save errno because rfbErr() will tamper it */
int olderrno = errno ;
rfbErr ( " %s: peek; %m \n " , __func__ ) ;
errno = olderrno ;
* sockRet = - 1 ;
} else {
* sockRet = 0 ;
}
return WS_HYBI_STATE_ERR ;
}
wsctx - > nReadRaw + = ret ;
if ( wsctx - > nReadRaw < 2 ) {
/* cannot decode header with less than two bytes */
errno = EAGAIN ;
* sockRet = - 1 ;
return WS_HYBI_STATE_HEADER_PENDING ;
}
/* first two header bytes received; interpret header data and get rest */
wsctx - > header . data = ( ws_header_t * ) wsctx - > codeBufDecode ;
wsctx - > header . opcode = wsctx - > header . data - > b0 & 0x0f ;
/* fin = (header->b0 & 0x80) >> 7; */ /* not used atm */
wsctx - > header . payloadLen = wsctx - > header . data - > b1 & 0x7f ;
rfbLog ( " first header bytes received; opcode=%d lenbyte=%d \n " , wsctx - > header . opcode , wsctx - > header . payloadLen ) ;
/*
* 4.3 . Client - to - Server Masking
*
* The client MUST mask all frames sent to the server . A server MUST
* close the connection upon receiving a frame with the MASK bit set to 0.
* */
if ( ! ( wsctx - > header . data - > b1 & 0x80 ) ) {
rfbErr ( " %s: got frame without mask ret=%d \n " , __func__ , ret ) ;
errno = EIO ;
* sockRet = - 1 ;
return WS_HYBI_STATE_ERR ;
}
if ( wsctx - > header . payloadLen < 126 & & wsctx - > nReadRaw > = 6 ) {
wsctx - > header . headerLen = 2 + WS_HYBI_MASK_LEN ;
wsctx - > header . mask = wsctx - > header . data - > u . m ;
} else if ( wsctx - > header . payloadLen = = 126 & & 8 < = wsctx - > nReadRaw ) {
wsctx - > header . headerLen = 4 + WS_HYBI_MASK_LEN ;
wsctx - > header . payloadLen = WS_NTOH16 ( wsctx - > header . data - > u . s16 . l16 ) ;
wsctx - > header . mask = wsctx - > header . data - > u . s16 . m16 ;
} else if ( wsctx - > header . payloadLen = = 127 & & 14 < = wsctx - > nReadRaw ) {
wsctx - > header . headerLen = 10 + WS_HYBI_MASK_LEN ;
wsctx - > header . payloadLen = WS_NTOH64 ( wsctx - > header . data - > u . s64 . l64 ) ;
wsctx - > header . mask = wsctx - > header . data - > u . s64 . m64 ;
} else {
/* Incomplete frame header, try again */
rfbErr ( " %s: incomplete frame header; ret=%d \n " , __func__ , ret ) ;
errno = EAGAIN ;
* sockRet = - 1 ;
return WS_HYBI_STATE_HEADER_PENDING ;
}
/* absolute length of frame */
wsctx - > nToRead = wsctx - > header . headerLen + wsctx - > header . payloadLen ;
/* set payload pointer just after header */
wsctx - > writePos = wsctx - > codeBufDecode + wsctx - > nReadRaw ;
wsctx - > readPos = ( unsigned char * ) ( wsctx - > codeBufDecode + wsctx - > header . headerLen ) ;
rfbLog ( " header complete: state=%d flen=%d writeTo=%p \n " , wsctx - > hybiDecodeState , wsctx - > nToRead , wsctx - > writePos ) ;
return WS_HYBI_STATE_DATA_NEEDED ;
}
opcode = header - > b0 & 0x0f ;
/* fin = (header->b0 & 0x80) >> 7; */ /* not used atm */
flength = header - > b1 & 0x7f ;
static int
hybiWsFrameComplete ( ws_ctx_t * wsctx )
{
return wsctx ! = NULL & & hybiRemaining ( wsctx ) = = 0 ;
}
/*
* 4.3 . Client - to - Server Masking
*
* The client MUST mask all frames sent to the server . A server MUST
* close the connection upon receiving a frame with the MASK bit set to 0.
* */
if ( ! ( header - > b1 & 0x80 ) ) {
rfbErr ( " %s: got frame without mask \n " , __func__ , ret ) ;
errno = EIO ;
goto spor ;
}
if ( flength < 126 ) {
fhlen = 2 ;
mask = header - > u . m ;
} else if ( flength = = 126 & & 4 < = ret ) {
flength = WS_NTOH16 ( header - > u . s16 . l16 ) ;
fhlen = 4 ;
mask = header - > u . s16 . m16 ;
} else if ( flength = = 127 & & 10 < = ret ) {
flength = WS_NTOH64 ( header - > u . s64 . l64 ) ;
fhlen = 10 ;
mask = header - > u . s64 . m64 ;
} else {
/* Incomplete frame header */
rfbErr ( " %s: incomplete frame header \n " , __func__ , ret ) ;
errno = EIO ;
goto spor ;
}
static char *
hybiPayloadStart ( ws_ctx_t * wsctx )
{
return wsctx - > codeBufDecode + wsctx - > header . headerLen ;
}
/* absolute length of frame */
total = fhlen + flength + 4 ;
payload = buf + fhlen + 4 ; /* header length + mask */
if ( - 1 = = ( ret = ws_read ( cl , buf , total ) ) ) {
/**
* Read the remaining payload bytes from associated raw socket .
*
* - try to read remaining bytes from socket
* - unmask all multiples of 4
* - if frame incomplete but some bytes are left , these are copied to
* the carry buffer
* - if opcode is TEXT : Base64 - decode all unmasked received bytes
* - set state for reading decoded data
* - reset write position to begin of buffer ( + header )
* - - > before we retrieve more data we let the caller clear all bytes
* from the reception buffer
* - execute return data routine
*
* Sets errno corresponding to what it gets from the underlying
* socket or EIO if some internal sanity check fails .
*
* @ param [ in ] cl client ptr with raw socket reference
* @ param [ out ] dst destination buffer
* @ param [ in ] len size of destination buffer
* @ param [ out ] sockRet emulated recv return value
* @ return next hybi decode state
*/
static int
hybiReadAndDecode ( rfbClientPtr cl , char * dst , int len , int * sockRet )
{
int n ;
int i ;
int toReturn ;
int toDecode ;
int bufsize ;
int nextRead ;
unsigned char * data ;
uint32_t * data32 ;
ws_ctx_t * wsctx = ( ws_ctx_t * ) cl - > wsctx ;
/* if data was carried over, copy to start of buffer */
memcpy ( wsctx - > writePos , wsctx - > carryBuf , wsctx - > carrylen ) ;
wsctx - > writePos + = wsctx - > carrylen ;
/* -1 accounts for potential '\0' terminator for base64 decoding */
bufsize = wsctx - > codeBufDecode + ARRAYSIZE ( wsctx - > codeBufDecode ) - wsctx - > writePos - 1 ;
if ( hybiRemaining ( wsctx ) > bufsize ) {
nextRead = bufsize ;
} else {
nextRead = hybiRemaining ( wsctx ) ;
}
rfbLog ( " calling read with buf=%p and len=%d (decodebuf=%p headerLen=%d \n ) " , wsctx - > writePos , nextRead , wsctx - > codeBufDecode , wsctx - > header . headerLen ) ;
if ( wsctx - > nReadRaw < wsctx - > nToRead ) {
/* decode more data */
if ( - 1 = = ( n = ws_read ( cl , wsctx - > writePos , nextRead ) ) ) {
int olderrno = errno ;
rfbErr ( " %s: read; %m " , __func__ ) ;
errno = olderrno ;
return ret ;
} else if ( ret < total ) {
/* GT TODO: hmm? */
rfbLog ( " %s: read; got partial data \n " , __func__ ) ;
} else {
buf [ ret ] = ' \0 ' ;
* sockRet = - 1 ;
return WS_HYBI_STATE_ERR ;
} else if ( n = = 0 ) {
* sockRet = 0 ;
return WS_HYBI_STATE_ERR ;
}
/* process 1 frame (32 bit op) */
payload32 = ( uint32_t * ) payload ;
for ( i = 0 ; i < flength / 4 ; i + + ) {
payload32 [ i ] ^ = mask . u ;
wsctx - > nReadRaw + = n ;
rfbLog ( " read %d bytes from socket; nRead=%d \n " , n , wsctx - > nReadRaw ) ;
} else {
n = 0 ;
}
wsctx - > writePos + = n ;
if ( wsctx - > nReadRaw > = wsctx - > nToRead ) {
if ( wsctx - > nReadRaw > wsctx - > nToRead ) {
rfbErr ( " %s: internal error, read past websocket frame " , __func__ ) ;
errno = EIO ;
* sockRet = - 1 ;
return WS_HYBI_STATE_ERR ;
}
}
toDecode = wsctx - > writePos - hybiPayloadStart ( wsctx ) ;
rfbLog ( " toDecode=%d from n=%d carrylen=%d headerLen=%d \n " , toDecode , n , wsctx - > carrylen , wsctx - > header . headerLen ) ;
if ( toDecode < 0 ) {
rfbErr ( " %s: internal error; negative number of bytes to decode: %d " , __func__ , toDecode ) ;
errno = EIO ;
* sockRet = - 1 ;
return WS_HYBI_STATE_ERR ;
}
/* for a possible base64 decoding, we decode multiples of 4 bytes until
* the whole frame is received and carry over any remaining bytes in the carry buf */
data = ( unsigned char * ) hybiPayloadStart ( wsctx ) ;
data32 = ( uint32_t * ) data ;
for ( i = 0 ; i < ( toDecode > > 2 ) ; i + + ) {
data32 [ i ] ^ = wsctx - > header . mask . u ;
}
rfbLog ( " mask decoding; i=%d toDecode=%d \n " , i , toDecode ) ;
if ( wsctx - > hybiDecodeState = = WS_HYBI_STATE_FRAME_COMPLETE ) {
/* process the remaining bytes (if any) */
for ( i * = 4 ; i < flength ; i + + ) {
payload [ i ] ^ = mask . c [ i % 4 ] ;
}
switch ( opcode ) {
case WS_OPCODE_CLOSE :
rfbLog ( " got closure, reason %d \n " , WS_NTOH16 ( ( ( uint16_t * ) payload ) [ 0 ] ) ) ;
errno = ECONNRESET ;
break ;
case WS_OPCODE_TEXT_FRAME :
if ( - 1 = = ( flength = b64_pton ( payload , ( unsigned char * ) wsctx - > codeBufDecode , sizeof ( wsctx - > codeBufDecode ) ) ) ) {
rfbErr ( " %s: Base64 decode error; %m \n " , __func__ ) ;
break ;
}
payload = wsctx - > codeBufDecode ;
/* fall through */
case WS_OPCODE_BINARY_FRAME :
if ( flength > len ) {
memcpy ( wsctx - > readbuf , payload + len , flength - len ) ;
wsctx - > readbufstart = 0 ;
wsctx - > readbuflen = flength - len ;
flength = len ;
}
memcpy ( dst , payload , flength ) ;
result = flength ;
break ;
for ( i * = 4 ; i < toDecode ; i + + ) {
data [ i ] ^ = wsctx - > header . mask . c [ i % 4 ] ;
}
/* all data is here, no carrying */
wsctx - > carrylen = 0 ;
} else {
/* carry over remaining, non-multiple-of-four bytes */
wsctx - > carrylen = toDecode - ( i * 4 ) ;
if ( wsctx - > carrylen < 0 | | wsctx - > carrylen > ARRAYSIZE ( wsctx - > carryBuf ) ) {
rfbErr ( " %s: internal error, invalid carry over size: carrylen=%d, toDecode=%d, i=%d " , __func__ , wsctx - > carrylen , toDecode , i ) ;
* sockRet = - 1 ;
errno = EIO ;
return WS_HYBI_STATE_ERR ;
}
rfbLog ( " carrying over %d bytes from %p to %p \n " , wsctx - > carrylen , wsctx - > writePos + ( i * 4 ) , wsctx - > carryBuf ) ;
memcpy ( wsctx - > carryBuf , data + ( i * 4 ) , wsctx - > carrylen ) ;
}
toReturn = toDecode - wsctx - > carrylen ;
switch ( wsctx - > header . opcode ) {
case WS_OPCODE_CLOSE :
/* this data is not returned as payload data */
if ( hybiWsFrameComplete ( wsctx ) ) {
rfbLog ( " got closure, reason %d \n " , WS_NTOH16 ( ( ( uint16_t * ) data ) [ 0 ] ) ) ;
errno = ECONNRESET ;
* sockRet = - 1 ;
return WS_HYBI_STATE_FRAME_COMPLETE ;
} else {
rfbErr ( " %s: close reason with long frame not supported " , __func__ ) ;
errno = EIO ;
* sockRet = - 1 ;
return WS_HYBI_STATE_ERR ;
}
break ;
case WS_OPCODE_TEXT_FRAME :
data [ toReturn ] = ' \0 ' ;
rfbLog ( " Initiate Base64 decoding in %p with max size %d and ' \\ 0' at %p \n " , data , bufsize , data + toReturn ) ;
if ( - 1 = = ( wsctx - > readlen = b64_pton ( ( char * ) data , data , bufsize ) ) ) {
rfbErr ( " Base64 decode error in %s; data=%p bufsize=%d " , __func__ , data , bufsize ) ;
rfbErr ( " %s: Base64 decode error; %m \n " , __func__ ) ;
}
wsctx - > writePos = hybiPayloadStart ( wsctx ) ;
break ;
case WS_OPCODE_BINARY_FRAME :
wsctx - > readlen = toReturn ;
wsctx - > writePos = hybiPayloadStart ( wsctx ) ;
break ;
default :
rfbErr ( " %s: unhandled opcode %d, b0: %02x, b1: %02x \n " , __func__ , ( int ) wsctx - > header . opcode , wsctx - > header . data - > b0 , wsctx - > header . data - > b1 ) ;
}
wsctx - > readPos = data ;
return hybiReturnData ( dst , len , wsctx , sockRet ) ;
}
/**
* Read function for websocket - socket emulation .
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* + - + - + - + - + - - - - - - - + - + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | F | R | R | R | opcode | M | Payload len | Extended payload length |
* | I | S | S | S | ( 4 ) | A | ( 7 ) | ( 16 / 64 ) |
* | N | V | V | V | | S | | ( if payload len = = 126 / 127 ) |
* | | 1 | 2 | 3 | | K | | |
* + - + - + - + - + - - - - - - - + - + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
* | Extended payload length continued , if payload len = = 127 |
* + - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | | Masking - key , if MASK set to 1 |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Masking - key ( continued ) | Payload Data |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* : Payload Data continued . . . :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued . . . |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*
* Using the decode buffer , this function :
* - reads the complete header from the underlying socket
* - reads any remaining data bytes
* - unmasks the payload data using the provided mask
* - decodes Base64 encoded text data
* - copies len bytes of decoded payload data into dst
*
* Emulates a read call on a socket .
*/
static int
webSocketsDecodeHybi ( rfbClientPtr cl , char * dst , int len )
{
int result = - 1 ;
ws_ctx_t * wsctx = ( ws_ctx_t * ) cl - > wsctx ;
/* int fin; */ /* not used atm */
/* rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); */
rfbLog ( " %s_enter: len=%d; "
" CTX: readlen=%d readPos=%p "
" writeTo=%p "
" state=%d toRead=%d remaining=%d "
" nReadRaw=%d carrylen=%d carryBuf=%p \n " ,
__func__ , len ,
wsctx - > readlen , wsctx - > readPos ,
wsctx - > writePos ,
wsctx - > hybiDecodeState , wsctx - > nToRead , hybiRemaining ( wsctx ) ,
wsctx - > nReadRaw , wsctx - > carrylen , wsctx - > carryBuf ) ;
switch ( wsctx - > hybiDecodeState ) {
case WS_HYBI_STATE_HEADER_PENDING :
wsctx - > hybiDecodeState = hybiReadHeader ( cl , & result ) ;
if ( wsctx - > hybiDecodeState = = WS_HYBI_STATE_ERR ) {
goto spor ;
}
if ( wsctx - > hybiDecodeState ! = WS_HYBI_STATE_HEADER_PENDING ) {
/* when header is complete, try to read some more data */
wsctx - > hybiDecodeState = hybiReadAndDecode ( cl , dst , len , & result ) ;
}
break ;
case WS_HYBI_STATE_DATA_AVAILABLE :
wsctx - > hybiDecodeState = hybiReturnData ( dst , len , wsctx , & result ) ;
break ;
case WS_HYBI_STATE_DATA_NEEDED :
wsctx - > hybiDecodeState = hybiReadAndDecode ( cl , dst , len , & result ) ;
break ;
case WS_HYBI_STATE_CLOSE_REASON_PENDING :
wsctx - > hybiDecodeState = hybiReadAndDecode ( cl , dst , len , & result ) ;
break ;
default :
rfbErr ( " %s: unhandled opcode %d, b0: %02x, b1: %02x \n " , __func__ , ( int ) opcode , header - > b0 , header - > b1 ) ;
/* invalid state */
rfbErr ( " %s: called with invalid state %d \n " , wsctx - > hybiDecodeState ) ;
result = - 1 ;
errno = EIO ;
wsctx - > hybiDecodeState = WS_HYBI_STATE_ERR ;
}
/* single point of return, if someone has questions :-) */
spor :
/* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */
if ( wsctx - > hybiDecodeState = = WS_HYBI_STATE_FRAME_COMPLETE ) {
rfbLog ( " frame received successfully, cleaning up: read=%d hlen=%d plen=%d \n " , wsctx - > header . nRead , wsctx - > header . headerLen , wsctx - > header . payloadLen ) ;
/* frame finished, cleanup state */
hybiDecodeCleanup ( wsctx ) ;
} else if ( wsctx - > hybiDecodeState = = WS_HYBI_STATE_ERR ) {
hybiDecodeCleanup ( wsctx ) ;
}
rfbLog ( " %s_exit: len=%d; "
" CTX: readlen=%d readPos=%p "
" writePos=%p "
" state=%d toRead=%d remaining=%d "
" nRead=%d carrylen=%d carryBuf=%p "
" result=%d \n " ,
__func__ , len ,
wsctx - > readlen , wsctx - > readPos ,
wsctx - > writePos ,
wsctx - > hybiDecodeState , wsctx - > nToRead , hybiRemaining ( wsctx ) ,
wsctx - > nReadRaw , wsctx - > carrylen , wsctx - > carryBuf ,
result ) ;
return result ;
}
@ -951,7 +1282,7 @@ webSocketsHasDataInBuffer(rfbClientPtr cl)
{
ws_ctx_t * wsctx = ( ws_ctx_t * ) cl - > wsctx ;
if ( wsctx & & wsctx - > read buf len)
if ( wsctx & & wsctx - > read len)
return TRUE ;
return ( cl - > sslctx & & rfbssl_pending ( cl ) > 0 ) ;