* Implement chunked encoding for RDG_OUT_DATA
This commit is contained in:
akarl10 2021-01-25 08:39:30 +01:00 committed by GitHub
parent bcf2de2ffb
commit 43691d59ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 239 additions and 40 deletions

View File

@ -63,7 +63,7 @@ struct _http_request
char* Authorization;
size_t ContentLength;
char* Content;
char* TransferEncoding;
TRANSFER_ENCODING TransferEncoding;
};
struct _http_response
@ -76,6 +76,7 @@ struct _http_response
size_t ContentLength;
const char* ContentType;
TRANSFER_ENCODING TransferEncoding;
size_t BodyLength;
BYTE* BodyContent;
@ -342,16 +343,12 @@ BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam)
return TRUE;
}
BOOL http_request_set_transfer_encoding(HttpRequest* request, const char* TransferEncoding)
BOOL http_request_set_transfer_encoding(HttpRequest* request, TRANSFER_ENCODING TransferEncoding)
{
if (!request || !TransferEncoding)
if (!request || TransferEncoding == TransferEncodingUnknown)
return FALSE;
free(request->TransferEncoding);
request->TransferEncoding = _strdup(TransferEncoding);
if (!request->TransferEncoding)
return FALSE;
request->TransferEncoding = TransferEncoding;
return TRUE;
}
@ -448,9 +445,14 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request)
goto fail;
}
if (request->TransferEncoding)
if (request->TransferEncoding != TransferEncodingIdentity)
{
if (!http_encode_body_line(s, "Transfer-Encoding", request->TransferEncoding))
if (request->TransferEncoding == TransferEncodingChunked)
{
if (!http_encode_body_line(s, "Transfer-Encoding", "chunked"))
goto fail;
}
else
goto fail;
}
else
@ -480,7 +482,12 @@ fail:
HttpRequest* http_request_new(void)
{
return (HttpRequest*)calloc(1, sizeof(HttpRequest));
HttpRequest* request = (HttpRequest*)calloc(1, sizeof(HttpRequest));
if (!request)
return NULL;
request->TransferEncoding = TransferEncodingIdentity;
return request;
}
void http_request_free(HttpRequest* request)
@ -494,7 +501,6 @@ void http_request_free(HttpRequest* request)
free(request->Content);
free(request->Method);
free(request->URI);
free(request->TransferEncoding);
free(request);
}
@ -572,6 +578,15 @@ static BOOL http_response_parse_header_field(HttpResponse* response, const char*
if (!response->ContentType)
return FALSE;
}
else if (_stricmp(name, "Transfer-Encoding") == 0)
{
if (_stricmp(value, "identity") == 0)
response->TransferEncoding = TransferEncodingIdentity;
else if (_stricmp(value, "chunked") == 0)
response->TransferEncoding = TransferEncodingChunked;
else
response->TransferEncoding = TransferEncodingUnknown;
}
else if (_stricmp(name, "WWW-Authenticate") == 0)
{
char* separator = NULL;
@ -948,6 +963,8 @@ HttpResponse* http_response_new(void)
ListDictionary_KeyObject(response->Authenticates)->fnObjectEquals = strings_equals_nocase;
ListDictionary_ValueObject(response->Authenticates)->fnObjectEquals = strings_equals_nocase;
response->TransferEncoding = TransferEncodingIdentity;
return response;
fail:
http_response_free(response);
@ -1006,13 +1023,21 @@ SSIZE_T http_response_get_body_length(HttpResponse* response)
return (SSIZE_T)response->BodyLength;
}
const char* http_response_get_auth_token(HttpResponse* respone, const char* method)
const char* http_response_get_auth_token(HttpResponse* response, const char* method)
{
if (!respone || !method)
if (!response || !method)
return NULL;
if (!ListDictionary_Contains(respone->Authenticates, method))
if (!ListDictionary_Contains(response->Authenticates, method))
return NULL;
return ListDictionary_GetItemValue(respone->Authenticates, method);
return ListDictionary_GetItemValue(response->Authenticates, method);
}
TRANSFER_ENCODING http_response_get_transfer_encoding(HttpResponse* response)
{
if (!response)
return TransferEncodingUnknown;
return response->TransferEncoding;
}

View File

@ -26,6 +26,13 @@
#include <freerdp/api.h>
#include <freerdp/crypto/tls.h>
typedef enum _TRANSFER_ENCODING
{
TransferEncodingUnknown,
TransferEncodingIdentity,
TransferEncodingChunked
} TRANSFER_ENCODING;
/* HTTP context */
typedef struct _http_context HttpContext;
@ -61,7 +68,7 @@ FREERDP_LOCAL BOOL http_request_set_uri(HttpRequest* request, const char* URI);
FREERDP_LOCAL BOOL http_request_set_auth_scheme(HttpRequest* request, const char* AuthScheme);
FREERDP_LOCAL BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam);
FREERDP_LOCAL BOOL http_request_set_transfer_encoding(HttpRequest* request,
const char* TransferEncoding);
TRANSFER_ENCODING TransferEncoding);
FREERDP_LOCAL wStream* http_request_write(HttpContext* context, HttpRequest* request);
@ -76,6 +83,7 @@ FREERDP_LOCAL HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLeng
FREERDP_LOCAL long http_response_get_status_code(HttpResponse* response);
FREERDP_LOCAL SSIZE_T http_response_get_body_length(HttpResponse* response);
FREERDP_LOCAL const char* http_response_get_auth_token(HttpResponse* respone, const char* method);
FREERDP_LOCAL const char* http_response_get_auth_token(HttpResponse* response, const char* method);
FREERDP_LOCAL TRANSFER_ENCODING http_response_get_transfer_encoding(HttpResponse* response);
#endif /* FREERDP_LIB_CORE_GATEWAY_HTTP_H */

View File

@ -104,6 +104,30 @@
#define HTTP_CAPABILITY_REAUTH 0x10
#define HTTP_CAPABILITY_UDP_TRANSPORT 0x20
typedef enum _CHUNK_STATE
{
ChunkStateLenghHeader,
ChunkStateData,
ChunkStateFooter
} CHUNK_STATE;
typedef struct
{
size_t nextOffset;
size_t headerFooterPos;
CHUNK_STATE state;
char lenBuffer[11];
} rdg_http_encoding_chunked_context;
typedef struct
{
TRANSFER_ENCODING httpTransferEncoding;
union _context
{
rdg_http_encoding_chunked_context chunked;
} context;
} rdg_http_encoding_context;
struct rdp_rdg
{
rdpContext* context;
@ -124,6 +148,7 @@ struct rdp_rdg
int timeout;
UINT16 extAuth;
UINT16 reserved2;
rdg_http_encoding_context transferEncoding;
};
enum
@ -298,21 +323,144 @@ static BOOL rdg_write_packet(rdpRdg* rdg, wStream* sPacket)
return TRUE;
}
static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, int size)
static int rdg_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size,
rdg_http_encoding_context* encodingContext)
{
int status;
int readCount = 0;
int effectiveDataLen = 0;
assert(encodingContext != NULL);
while (TRUE)
{
switch (encodingContext->context.chunked.state)
{
case ChunkStateData:
{
status = BIO_read(bio, pBuffer,
(size > encodingContext->context.chunked.nextOffset
? encodingContext->context.chunked.nextOffset
: size));
if (status <= 0)
return (effectiveDataLen > 0 ? effectiveDataLen : status);
encodingContext->context.chunked.nextOffset -= status;
if (encodingContext->context.chunked.nextOffset == 0)
{
encodingContext->context.chunked.state = ChunkStateFooter;
encodingContext->context.chunked.headerFooterPos = 0;
}
effectiveDataLen += status;
if ((size_t)status == size)
return effectiveDataLen;
pBuffer += status;
size -= status;
}
break;
case ChunkStateFooter:
{
char _dummy[2];
assert(encodingContext->context.chunked.nextOffset == 0);
assert(encodingContext->context.chunked.headerFooterPos < 2);
status =
BIO_read(bio, _dummy, 2 - encodingContext->context.chunked.headerFooterPos);
if (status >= 0)
{
encodingContext->context.chunked.headerFooterPos += status;
if (encodingContext->context.chunked.headerFooterPos == 2)
{
encodingContext->context.chunked.state = ChunkStateLenghHeader;
encodingContext->context.chunked.headerFooterPos = 0;
}
}
else
return (effectiveDataLen > 0 ? effectiveDataLen : status);
}
break;
case ChunkStateLenghHeader:
{
BOOL _haveNewLine = FALSE;
size_t tmp;
char* dst = &encodingContext->context.chunked
.lenBuffer[encodingContext->context.chunked.headerFooterPos];
assert(encodingContext->context.chunked.nextOffset == 0);
while (encodingContext->context.chunked.headerFooterPos < 10 && !_haveNewLine)
{
status = BIO_read(bio, dst, 1);
if (status >= 0)
{
if (*dst == '\n')
_haveNewLine = TRUE;
encodingContext->context.chunked.headerFooterPos += status;
dst += status;
}
else
return (effectiveDataLen > 0 ? effectiveDataLen : status);
}
*dst = '\0';
/* strtoul is tricky, error are reported via errno, we also need
* to ensure the result does not overflow */
errno = 0;
tmp = strtoul(encodingContext->context.chunked.lenBuffer, NULL, 16);
if ((errno != 0) || (tmp > SIZE_MAX))
return -1;
encodingContext->context.chunked.nextOffset = tmp;
encodingContext->context.chunked.state = ChunkStateData;
if (encodingContext->context.chunked.nextOffset == 0)
{ // end of stream
int fd = BIO_get_fd(bio, NULL);
if (fd >= 0)
close(fd);
WLog_WARN(TAG, "cunked encoding end of stream received");
encodingContext->context.chunked.headerFooterPos = 0;
encodingContext->context.chunked.state = ChunkStateFooter;
}
}
break;
default:
/* invalid state */
return -1;
}
}
return -1;
}
static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size,
rdg_http_encoding_context* encodingContext)
{
assert(encodingContext != NULL);
switch (encodingContext->httpTransferEncoding)
{
case TransferEncodingIdentity:
return BIO_read(bio, pBuffer, size);
case TransferEncodingChunked:
return rdg_chuncked_read(bio, pBuffer, size, encodingContext);
default:
return -1;
}
return -1; /* should not be reached */
}
static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size,
rdg_http_encoding_context* transferEncoding)
{
size_t readCount = 0;
BYTE* pBuffer = buffer;
while (readCount < size)
{
status = BIO_read(tls->bio, pBuffer, size - readCount);
int status = rdg_socket_read(tls->bio, pBuffer, size - readCount, transferEncoding);
if (status <= 0)
{
if (!BIO_should_retry(tls->bio))
return FALSE;
Sleep(10);
continue;
}
@ -334,7 +482,7 @@ static wStream* rdg_receive_packet(rdpRdg* rdg)
if (!s)
return NULL;
if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s), header))
if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s), header, &rdg->transferEncoding))
{
Stream_Free(s, TRUE);
return NULL;
@ -350,7 +498,8 @@ static wStream* rdg_receive_packet(rdpRdg* rdg)
return NULL;
}
if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s) + header, (int)packetLength - (int)header))
if (!rdg_read_all(rdg->tlsOut, Stream_Buffer(s) + header, (int)packetLength - (int)header,
&rdg->transferEncoding))
{
Stream_Free(s, TRUE);
return NULL;
@ -557,7 +706,7 @@ static BOOL rdg_set_ntlm_auth_header(rdpNtlm* ntlm, HttpRequest* request)
}
static wStream* rdg_build_http_request(rdpRdg* rdg, const char* method,
const char* transferEncoding)
TRANSFER_ENCODING transferEncoding)
{
wStream* s = NULL;
HttpRequest* request = NULL;
@ -649,7 +798,8 @@ static BOOL rdg_handle_ntlm_challenge(rdpNtlm* ntlm, HttpResponse* response)
return TRUE;
}
static BOOL rdg_skip_seed_payload(rdpTls* tls, SSIZE_T lastResponseLength)
static BOOL rdg_skip_seed_payload(rdpTls* tls, SSIZE_T lastResponseLength,
rdg_http_encoding_context* transferEncoding)
{
BYTE seed_payload[10];
const size_t size = sizeof(seed_payload);
@ -661,7 +811,7 @@ static BOOL rdg_skip_seed_payload(rdpTls* tls, SSIZE_T lastResponseLength)
*/
if (lastResponseLength < (SSIZE_T)size)
{
if (!rdg_read_all(tls, seed_payload, size - lastResponseLength))
if (!rdg_read_all(tls, seed_payload, size - lastResponseLength, transferEncoding))
{
return FALSE;
}
@ -1062,7 +1212,7 @@ static BOOL rdg_ntlm_init(rdpRdg* rdg, rdpTls* tls)
}
static BOOL rdg_send_http_request(rdpRdg* rdg, rdpTls* tls, const char* method,
const char* transferEncoding)
TRANSFER_ENCODING transferEncoding)
{
size_t sz;
wStream* s = NULL;
@ -1169,6 +1319,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
long statusCode;
SSIZE_T bodyLength;
long StatusCode;
TRANSFER_ENCODING encoding;
if (!rdg_tls_connect(rdg, tls, peerAddress, timeout))
return FALSE;
@ -1178,7 +1329,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
if (!rdg_ntlm_init(rdg, tls))
return FALSE;
if (!rdg_send_http_request(rdg, tls, method, NULL))
if (!rdg_send_http_request(rdg, tls, method, TransferEncodingIdentity))
return FALSE;
response = http_response_recv(tls, TRUE);
@ -1213,7 +1364,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
http_response_free(response);
}
if (!rdg_send_http_request(rdg, tls, method, NULL))
if (!rdg_send_http_request(rdg, tls, method, TransferEncodingIdentity))
return FALSE;
ntlm_free(rdg->ntlm);
@ -1225,6 +1376,7 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
statusCode = http_response_get_status_code(response);
bodyLength = http_response_get_body_length(response);
encoding = http_response_get_transfer_encoding(response);
http_response_free(response);
WLog_DBG(TAG, "%s authorization result: %d", method, statusCode);
@ -1241,12 +1393,21 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char*
if (strcmp(method, "RDG_OUT_DATA") == 0)
{
if (!rdg_skip_seed_payload(tls, bodyLength))
if (encoding == TransferEncodingChunked)
{
rdg->transferEncoding.httpTransferEncoding = TransferEncodingChunked;
rdg->transferEncoding.context.chunked.nextOffset = 0;
rdg->transferEncoding.context.chunked.headerFooterPos = 0;
rdg->transferEncoding.context.chunked.state = ChunkStateLenghHeader;
}
if (!rdg_skip_seed_payload(tls, bodyLength, &rdg->transferEncoding))
{
return FALSE;
}
}
else
{
if (!rdg_send_http_request(rdg, tls, method, "chunked"))
if (!rdg_send_http_request(rdg, tls, method, TransferEncodingChunked))
return FALSE;
}
@ -1479,8 +1640,8 @@ static BOOL rdg_process_control_packet(rdpRdg* rdg, int type, size_t packetLengt
while (readCount < payloadSize)
{
status =
BIO_read(rdg->tlsOut->bio, Stream_Pointer(s), (int)payloadSize - (int)readCount);
status = rdg_socket_read(rdg->tlsOut->bio, Stream_Pointer(s), payloadSize - readCount,
&rdg->transferEncoding);
if (status <= 0)
{
@ -1543,7 +1704,7 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size)
{
RdgPacketHeader header;
size_t readCount = 0;
int readSize;
size_t readSize;
int status;
if (!rdg->packetRemainingCount)
@ -1552,8 +1713,9 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size)
while (readCount < sizeof(RdgPacketHeader))
{
status = BIO_read(rdg->tlsOut->bio, (BYTE*)(&header) + readCount,
(int)sizeof(RdgPacketHeader) - (int)readCount);
status = rdg_socket_read(rdg->tlsOut->bio, (BYTE*)(&header) + readCount,
(int)sizeof(RdgPacketHeader) - (int)readCount,
&rdg->transferEncoding);
if (status <= 0)
{
@ -1587,8 +1749,9 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size)
while (readCount < 2)
{
status = BIO_read(rdg->tlsOut->bio, (BYTE*)(&rdg->packetRemainingCount) + readCount,
2 - (int)readCount);
status =
rdg_socket_read(rdg->tlsOut->bio, (BYTE*)(&rdg->packetRemainingCount) + readCount,
2 - (int)readCount, &rdg->transferEncoding);
if (status < 0)
{
@ -1603,8 +1766,8 @@ static int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size)
}
}
readSize = (rdg->packetRemainingCount < size ? rdg->packetRemainingCount : size);
status = BIO_read(rdg->tlsOut->bio, buffer, readSize);
readSize = (rdg->packetRemainingCount < size) ? rdg->packetRemainingCount : size;
status = rdg_socket_read(rdg->tlsOut->bio, buffer, readSize, &rdg->transferEncoding);
if (status <= 0)
{
@ -1868,6 +2031,8 @@ rdpRdg* rdg_new(rdpContext* context)
BIO_set_data(rdg->frontBio, rdg);
InitializeCriticalSection(&rdg->writeSection);
rdg->transferEncoding.httpTransferEncoding = TransferEncodingIdentity;
}
return rdg;
@ -1890,6 +2055,7 @@ void rdg_free(rdpRdg* rdg)
BIO_free_all(rdg->frontBio);
DeleteCriticalSection(&rdg->writeSection);
free(rdg);
}