Merge pull request #2484 from dvincent-devolutions/gateway
RDP8 Gateway support
This commit is contained in:
commit
3a2a915221
@ -78,6 +78,7 @@ COMMAND_LINE_ARGUMENT_A args[] =
|
||||
{ "gu", COMMAND_LINE_VALUE_REQUIRED, "[<domain>\\]<user> or <user>[@<domain>]", NULL, NULL, -1, NULL, "Gateway username" },
|
||||
{ "gp", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Gateway password" },
|
||||
{ "gd", COMMAND_LINE_VALUE_REQUIRED, "<domain>", NULL, NULL, -1, NULL, "Gateway domain" },
|
||||
{ "gt", COMMAND_LINE_VALUE_REQUIRED, "<rpc|http|auto>", NULL, NULL, -1, NULL, "Gateway transport type" },
|
||||
{ "gateway-usage-method", COMMAND_LINE_VALUE_REQUIRED, "<direct|detect>", NULL, NULL, -1, "gum", "Gateway usage method" },
|
||||
{ "load-balance-info", COMMAND_LINE_VALUE_REQUIRED, "<info string>", NULL, NULL, -1, NULL, "Load balance info" },
|
||||
{ "app", COMMAND_LINE_VALUE_REQUIRED, "<executable path> or <||alias>", NULL, NULL, -1, NULL, "Remote application program" },
|
||||
@ -1511,6 +1512,24 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
|
||||
settings->GatewayPassword = _strdup(arg->Value);
|
||||
settings->GatewayUseSameCredentials = FALSE;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "gt")
|
||||
{
|
||||
if (_stricmp(arg->Value, "rpc") == 0)
|
||||
{
|
||||
settings->GatewayRpcTransport = TRUE;
|
||||
settings->GatewayHttpTransport = FALSE;
|
||||
}
|
||||
else if (_stricmp(arg->Value, "http") == 0)
|
||||
{
|
||||
settings->GatewayRpcTransport = FALSE;
|
||||
settings->GatewayHttpTransport = TRUE;
|
||||
}
|
||||
else if (_stricmp(arg->Value, "auto") == 0)
|
||||
{
|
||||
settings->GatewayRpcTransport = TRUE;
|
||||
settings->GatewayHttpTransport = TRUE;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchCase(arg, "gateway-usage-method")
|
||||
{
|
||||
int type;
|
||||
|
@ -692,6 +692,9 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
|
||||
#define FreeRDP_GatewayUseSameCredentials 1991
|
||||
#define FreeRDP_GatewayEnabled 1992
|
||||
#define FreeRDP_GatewayBypassLocal 1993
|
||||
#define FreeRDP_GatewayRpcTransport 1994
|
||||
#define FreeRDP_GatewayHttpTransport 1995
|
||||
#define FreeRDP_GatewayUdpTransport 1996
|
||||
#define FreeRDP_RemoteApplicationMode 2112
|
||||
#define FreeRDP_RemoteApplicationName 2113
|
||||
#define FreeRDP_RemoteApplicationIcon 2114
|
||||
@ -1129,7 +1132,10 @@ struct rdp_settings
|
||||
ALIGN64 BOOL GatewayUseSameCredentials; /* 1991 */
|
||||
ALIGN64 BOOL GatewayEnabled; /* 1992 */
|
||||
ALIGN64 BOOL GatewayBypassLocal; /* 1993 */
|
||||
UINT64 padding2048[2048 - 1994]; /* 1994 */
|
||||
ALIGN64 BOOL GatewayRpcTransport; /* 1994 */
|
||||
ALIGN64 BOOL GatewayHttpTransport; /* 1995 */
|
||||
ALIGN64 BOOL GatewayUdpTransport; /* 1996 */
|
||||
UINT64 padding2048[2048 - 1997]; /* 1997 */
|
||||
UINT64 padding2112[2112 - 2048]; /* 2048 */
|
||||
|
||||
/**
|
||||
|
@ -1629,6 +1629,168 @@ void freerdp_clrconv_free(HCLRCONV clrconv)
|
||||
}
|
||||
}
|
||||
|
||||
int freerdp_image1_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
|
||||
int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
|
||||
{
|
||||
int x, y;
|
||||
int nSrcPad;
|
||||
int nDstPad;
|
||||
int nAlignedWidth;
|
||||
int dstBitsPerPixel;
|
||||
int dstBytesPerPixel;
|
||||
BOOL vFlip = FALSE;
|
||||
BOOL invert = FALSE;
|
||||
|
||||
dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
|
||||
dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
|
||||
|
||||
nAlignedWidth = nWidth + nWidth % 8;
|
||||
|
||||
if (nSrcStep < 0)
|
||||
nSrcStep = nAlignedWidth / 8;
|
||||
|
||||
if (nDstStep < 0)
|
||||
nDstStep = dstBytesPerPixel * nWidth;
|
||||
|
||||
nSrcPad = (nSrcStep - (nAlignedWidth / 8));
|
||||
nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
|
||||
|
||||
if (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat))
|
||||
invert = TRUE;
|
||||
|
||||
if (FREERDP_PIXEL_FORMAT_FLIP(DstFormat) == FREERDP_PIXEL_FLIP_VERTICAL)
|
||||
vFlip = TRUE;
|
||||
|
||||
if (dstBytesPerPixel == 4)
|
||||
{
|
||||
BYTE SrcPixel;
|
||||
BYTE* pSrcPixel;
|
||||
UINT32* pDstPixel;
|
||||
|
||||
if (!invert)
|
||||
{
|
||||
if (!vFlip)
|
||||
{
|
||||
pSrcPixel = &pSrcData[nYSrc * nSrcStep];
|
||||
pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
|
||||
|
||||
for (y = 0; y < nHeight; y++)
|
||||
{
|
||||
for (x = 0; x < nWidth / 8; x++)
|
||||
{
|
||||
SrcPixel = *pSrcPixel;
|
||||
pDstPixel[0] = (SrcPixel & 0x80) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[1] = (SrcPixel & 0x40) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[2] = (SrcPixel & 0x20) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[3] = (SrcPixel & 0x10) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[4] = (SrcPixel & 0x08) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[5] = (SrcPixel & 0x04) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[6] = (SrcPixel & 0x02) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel[7] = (SrcPixel & 0x01) ? 0xFFFFFFFF : 0xFF000000;
|
||||
pDstPixel += 8;
|
||||
pSrcPixel++;
|
||||
}
|
||||
|
||||
if (nWidth % 8)
|
||||
{
|
||||
SrcPixel = *pSrcPixel;
|
||||
|
||||
for (x = 0; x < nWidth % 8; x++)
|
||||
{
|
||||
*pDstPixel = (SrcPixel & 0x80) ? 0xFFFFFFFF : 0xFF000000;
|
||||
SrcPixel <<= 1;
|
||||
pDstPixel++;
|
||||
}
|
||||
|
||||
pSrcPixel++;
|
||||
}
|
||||
|
||||
pSrcPixel += nSrcPad;
|
||||
pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int freerdp_image4_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
|
||||
int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
|
||||
{
|
||||
int x, y;
|
||||
int nSrcPad;
|
||||
int nDstPad;
|
||||
int nAlignedWidth;
|
||||
int dstBitsPerPixel;
|
||||
int dstBytesPerPixel;
|
||||
BOOL vFlip = FALSE;
|
||||
BOOL invert = FALSE;
|
||||
|
||||
dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
|
||||
dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
|
||||
|
||||
nAlignedWidth = nWidth + (nWidth % 2);
|
||||
|
||||
if (nSrcStep < 0)
|
||||
nSrcStep = nAlignedWidth / 2;
|
||||
|
||||
if (nDstStep < 0)
|
||||
nDstStep = dstBytesPerPixel * nWidth;
|
||||
|
||||
nSrcPad = (nSrcStep - (nAlignedWidth / 2));
|
||||
nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
|
||||
|
||||
if (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat))
|
||||
invert = TRUE;
|
||||
|
||||
if (FREERDP_PIXEL_FORMAT_FLIP(DstFormat) == FREERDP_PIXEL_FLIP_VERTICAL)
|
||||
vFlip = TRUE;
|
||||
|
||||
if (dstBytesPerPixel == 4)
|
||||
{
|
||||
BYTE* pSrcPixel;
|
||||
UINT32* pDstPixel;
|
||||
UINT32* values = (UINT32*) palette;
|
||||
|
||||
if (!invert)
|
||||
{
|
||||
if (!vFlip)
|
||||
{
|
||||
pSrcPixel = &pSrcData[nYSrc * nSrcStep];
|
||||
pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
|
||||
|
||||
for (y = 0; y < nHeight; y++)
|
||||
{
|
||||
for (x = 0; x < nWidth / 2; x++)
|
||||
{
|
||||
pDstPixel[0] = values[*pSrcPixel >> 4];
|
||||
pDstPixel[1] = values[*pSrcPixel & 0xF];
|
||||
pDstPixel += 2;
|
||||
pSrcPixel++;
|
||||
}
|
||||
|
||||
if (nWidth % 2)
|
||||
{
|
||||
pDstPixel[0] = values[*pSrcPixel >> 4];
|
||||
pDstPixel++;
|
||||
pSrcPixel++;
|
||||
}
|
||||
|
||||
pSrcPixel += nSrcPad;
|
||||
pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int freerdp_image8_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
|
||||
int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
|
||||
{
|
||||
@ -3312,6 +3474,16 @@ int freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst,
|
||||
status = freerdp_image8_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
|
||||
nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
|
||||
}
|
||||
else if (srcBitsPerPixel == 1)
|
||||
{
|
||||
status = freerdp_image1_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
|
||||
nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
|
||||
}
|
||||
else if (srcBitsPerPixel == 4)
|
||||
{
|
||||
status = freerdp_image4_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
|
||||
nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
|
@ -936,6 +936,15 @@ BOOL freerdp_get_param_bool(rdpSettings* settings, int id)
|
||||
case FreeRDP_GatewayBypassLocal:
|
||||
return settings->GatewayBypassLocal;
|
||||
|
||||
case FreeRDP_GatewayRpcTransport:
|
||||
return settings->GatewayRpcTransport;
|
||||
|
||||
case FreeRDP_GatewayHttpTransport:
|
||||
return settings->GatewayHttpTransport;
|
||||
|
||||
case FreeRDP_GatewayUdpTransport:
|
||||
return settings->GatewayUdpTransport;
|
||||
|
||||
case FreeRDP_RemoteApplicationMode:
|
||||
return settings->RemoteApplicationMode;
|
||||
|
||||
@ -1396,6 +1405,18 @@ int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param)
|
||||
settings->GatewayBypassLocal = param;
|
||||
break;
|
||||
|
||||
case FreeRDP_GatewayRpcTransport:
|
||||
settings->GatewayRpcTransport = param;
|
||||
break;
|
||||
|
||||
case FreeRDP_GatewayHttpTransport:
|
||||
settings->GatewayHttpTransport = param;
|
||||
break;
|
||||
|
||||
case FreeRDP_GatewayUdpTransport:
|
||||
settings->GatewayUdpTransport = param;
|
||||
break;
|
||||
|
||||
case FreeRDP_RemoteApplicationMode:
|
||||
settings->RemoteApplicationMode = param;
|
||||
break;
|
||||
|
@ -28,6 +28,8 @@ set(${MODULE_PREFIX}_GATEWAY_DIR "gateway")
|
||||
set(${MODULE_PREFIX}_GATEWAY_SRCS
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.c
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.h
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/rdg.c
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/rdg.h
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.c
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.h
|
||||
${${MODULE_PREFIX}_GATEWAY_DIR}/rpc_bind.c
|
||||
|
@ -174,6 +174,17 @@ BOOL http_context_set_pragma(HttpContext* context, const char* Pragma)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL http_context_set_rdg_connection_id(HttpContext* context, const char* RdgConnectionId)
|
||||
{
|
||||
free(context->RdgConnectionId);
|
||||
context->RdgConnectionId = _strdup(RdgConnectionId);
|
||||
|
||||
if (!context->RdgConnectionId)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void http_context_free(HttpContext* context)
|
||||
{
|
||||
if (context)
|
||||
@ -186,6 +197,7 @@ void http_context_free(HttpContext* context)
|
||||
free(context->CacheControl);
|
||||
free(context->Connection);
|
||||
free(context->Pragma);
|
||||
free(context->RdgConnectionId);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
@ -234,6 +246,17 @@ BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL http_request_set_transfer_encoding(HttpRequest* request, const char* TransferEncoding)
|
||||
{
|
||||
free(request->TransferEncoding);
|
||||
request->TransferEncoding = _strdup(TransferEncoding);
|
||||
|
||||
if (!request->TransferEncoding)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char* http_encode_body_line(char* param, char* value)
|
||||
{
|
||||
char* line;
|
||||
@ -325,6 +348,26 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (context->RdgConnectionId)
|
||||
{
|
||||
lines[count] = http_encode_body_line("RDG-Connection-Id", context->RdgConnectionId);
|
||||
|
||||
if (!lines[count])
|
||||
goto out_free;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
if (request->TransferEncoding)
|
||||
{
|
||||
lines[count] = http_encode_body_line("Transfer-Encoding", request->TransferEncoding);
|
||||
|
||||
if (!lines[count])
|
||||
goto out_free;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
if (request->Authorization)
|
||||
{
|
||||
lines[count] = http_encode_body_line("Authorization", request->Authorization);
|
||||
@ -403,6 +446,9 @@ void http_request_free(HttpRequest* request)
|
||||
free(request->Content);
|
||||
free(request->Method);
|
||||
free(request->URI);
|
||||
|
||||
free(request->TransferEncoding);
|
||||
|
||||
free(request);
|
||||
}
|
||||
|
||||
@ -589,7 +635,7 @@ HttpResponse* http_response_recv(rdpTls* tls)
|
||||
int payloadOffset;
|
||||
HttpResponse* response;
|
||||
|
||||
size = 1024;
|
||||
size = 2048;
|
||||
payload = NULL;
|
||||
payloadOffset = 0;
|
||||
|
||||
@ -716,11 +762,17 @@ HttpResponse* http_response_recv(rdpTls* tls)
|
||||
|
||||
if (response->ContentType)
|
||||
{
|
||||
if (_stricmp(response->ContentType, "text/plain") == 0)
|
||||
if (_stricmp(response->ContentType, "application/rpc") != 0)
|
||||
bodyLength = response->ContentLength;
|
||||
else if (_stricmp(response->ContentType, "text/plain") == 0)
|
||||
bodyLength = response->ContentLength;
|
||||
else if (_stricmp(response->ContentType, "text/html") == 0)
|
||||
bodyLength = response->ContentLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyLength = response->BodyLength;
|
||||
}
|
||||
|
||||
if (bodyLength != response->BodyLength)
|
||||
{
|
||||
@ -786,8 +838,11 @@ void http_response_free(HttpResponse* response)
|
||||
|
||||
ListDictionary_Free(response->Authenticates);
|
||||
|
||||
if (response->ContentLength > 0)
|
||||
if (response->BodyContent)
|
||||
{
|
||||
free(response->BodyContent);
|
||||
response->BodyContent = NULL;
|
||||
}
|
||||
|
||||
free(response);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ struct _http_context
|
||||
char* CacheControl;
|
||||
char* Connection;
|
||||
char* Pragma;
|
||||
char* RdgConnectionId;
|
||||
};
|
||||
|
||||
BOOL http_context_set_method(HttpContext* context, const char* Method);
|
||||
@ -50,6 +51,7 @@ BOOL http_context_set_accept(HttpContext* context, const char* Accept);
|
||||
BOOL http_context_set_cache_control(HttpContext* context, const char* CacheControl);
|
||||
BOOL http_context_set_connection(HttpContext* context, const char* Connection);
|
||||
BOOL http_context_set_pragma(HttpContext* context, const char* Pragma);
|
||||
BOOL http_context_set_rdg_connection_id(HttpContext* context, const char* RdgConnectionId);
|
||||
|
||||
HttpContext* http_context_new(void);
|
||||
void http_context_free(HttpContext* context);
|
||||
@ -63,12 +65,14 @@ struct _http_request
|
||||
char* Authorization;
|
||||
int ContentLength;
|
||||
char* Content;
|
||||
char* TransferEncoding;
|
||||
};
|
||||
|
||||
BOOL http_request_set_method(HttpRequest* request, const char* Method);
|
||||
BOOL http_request_set_uri(HttpRequest* request, const char* URI);
|
||||
BOOL http_request_set_auth_scheme(HttpRequest* request, const char* AuthScheme);
|
||||
BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam);
|
||||
BOOL http_request_set_transfer_encoding(HttpRequest* request, const char* TransferEncoding);
|
||||
|
||||
wStream* http_request_write(HttpContext* context, HttpRequest* request);
|
||||
|
||||
|
@ -302,5 +302,7 @@ void ntlm_free(rdpNtlm* ntlm)
|
||||
ntlm->outputBuffer[0].pvBuffer = NULL;
|
||||
}
|
||||
|
||||
ntlm_client_uninit(ntlm);
|
||||
|
||||
free(ntlm);
|
||||
}
|
||||
|
1533
libfreerdp/core/gateway/rdg.c
Normal file
1533
libfreerdp/core/gateway/rdg.c
Normal file
File diff suppressed because it is too large
Load Diff
153
libfreerdp/core/gateway/rdg.h
Normal file
153
libfreerdp/core/gateway/rdg.h
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Remote Desktop Gateway (RDG)
|
||||
*
|
||||
* Copyright 2015 Denis Vincent <dvincent@devolutions.net>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CORE_RDG_H
|
||||
#define FREERDP_CORE_RDG_H
|
||||
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/utils/ringbuffer.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/crypto/tls.h>
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/settings.h>
|
||||
|
||||
typedef struct rdp_rdg rdpRdg;
|
||||
|
||||
#include "http.h"
|
||||
#include "ntlm.h"
|
||||
#include "../transport.h"
|
||||
|
||||
/* HTTP channel response fields present flags. */
|
||||
#define HTTP_CHANNEL_RESPONSE_FIELD_CHANNELID 0x1
|
||||
#define HTTP_CHANNEL_RESPONSE_OPTIONAL 0x2
|
||||
#define HTTP_CHANNEL_RESPONSE_FIELD_UDPPORT 0x4
|
||||
|
||||
/* HTTP extended auth. */
|
||||
#define HTTP_EXTENDED_AUTH_NONE 0x0
|
||||
#define HTTP_EXTENDED_AUTH_SC 0x1 /* Smart card authentication. */
|
||||
#define HTTP_EXTENDED_AUTH_PAA 0x02 /* Pluggable authentication. */
|
||||
|
||||
/* HTTP packet types. */
|
||||
#define PKT_TYPE_HANDSHAKE_REQUEST 0x1
|
||||
#define PKT_TYPE_HANDSHAKE_RESPONSE 0x2
|
||||
#define PKT_TYPE_EXTENDED_AUTH_MSG 0x3
|
||||
#define PKT_TYPE_TUNNEL_CREATE 0x4
|
||||
#define PKT_TYPE_TUNNEL_RESPONSE 0x5
|
||||
#define PKT_TYPE_TUNNEL_AUTH 0x6
|
||||
#define PKT_TYPE_TUNNEL_AUTH_RESPONSE 0x7
|
||||
#define PKT_TYPE_CHANNEL_CREATE 0x8
|
||||
#define PKT_TYPE_CHANNEL_RESPONSE 0x9
|
||||
#define PKT_TYPE_DATA 0xA
|
||||
#define PKT_TYPE_SERVICE_MESSAGE 0xB
|
||||
#define PKT_TYPE_REAUTH_MESSAGE 0xC
|
||||
#define PKT_TYPE_KEEPALIVE 0xD
|
||||
#define PKT_TYPE_CLOSE_CHANNEL 0x10
|
||||
#define PKT_TYPE_CLOSE_CHANNEL_RESPONSE 0x11
|
||||
|
||||
/* HTTP tunnel auth fields present flags. */
|
||||
#define HTTP_TUNNEL_AUTH_FIELD_SOH 0x1
|
||||
|
||||
/* HTTP tunnel auth response fields present flags. */
|
||||
#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_REDIR_FLAGS 0x1
|
||||
#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_IDLE_TIMEOUT 0x2
|
||||
#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_SOH_RESPONSE 0x4
|
||||
|
||||
/* HTTP tunnel packet fields present flags. */
|
||||
#define HTTP_TUNNEL_PACKET_FIELD_PAA_COOKIE 0x1
|
||||
#define HTTP_TUNNEL_PACKET_FIELD_REAUTH 0x2
|
||||
|
||||
/* HTTP tunnel redir flags. */
|
||||
#define HTTP_TUNNEL_REDIR_ENABLE_ALL 0x80000000
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_ALL 0x40000000
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_DRIVE 0x1
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_PRINTER 0x2
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_PORT 0x4
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD 0x8
|
||||
#define HTTP_TUNNEL_REDIR_DISABLE_PNP 0x10
|
||||
|
||||
/* HTTP tunnel response fields present flags. */
|
||||
#define HTTP_TUNNEL_RESPONSE_FIELD_TUNNEL_ID 0x1
|
||||
#define HTTP_TUNNEL_RESPONSE_FIELD_CAPS 0x2
|
||||
#define HTTP_TUNNEL_RESPONSE_FIELD_SOH_REQ 0x4
|
||||
#define HTTP_TUNNEL_RESPONSE_FIELD_CONSENT_MSG 0x10
|
||||
|
||||
/* HTTP capability type enumeration. */
|
||||
#define HTTP_CAPABILITY_TYPE_QUAR_SOH 0x1
|
||||
#define HTTP_CAPABILITY_IDLE_TIMEOUT 0x2
|
||||
#define HTTP_CAPABILITY_MESSAGING_CONSENT_SIGN 0x4
|
||||
#define HTTP_CAPABILITY_MESSAGING_SERVICE_MSG 0x8
|
||||
#define HTTP_CAPABILITY_REAUTH 0x10
|
||||
#define HTTP_CAPABILITY_UDP_TRANSPORT 0x20
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
RDG_CLIENT_STATE_INITIAL,
|
||||
RDG_CLIENT_STATE_OUT_CHANNEL_REQUEST,
|
||||
RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE,
|
||||
RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZED,
|
||||
RDG_CLIENT_STATE_IN_CHANNEL_REQUEST,
|
||||
RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE,
|
||||
RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZED,
|
||||
RDG_CLIENT_STATE_HANDSHAKE,
|
||||
RDG_CLIENT_STATE_TUNNEL_CREATE,
|
||||
RDG_CLIENT_STATE_TUNNEL_AUTHORIZE,
|
||||
RDG_CLIENT_STATE_CHANNEL_CREATE,
|
||||
RDG_CLIENT_STATE_OPENED,
|
||||
RDG_CLIENT_STATE_CLOSE,
|
||||
RDG_CLIENT_STATE_CLOSED,
|
||||
RDG_CLIENT_STATE_NOT_FOUND,
|
||||
};
|
||||
|
||||
struct rdp_rdg
|
||||
{
|
||||
rdpContext* context;
|
||||
rdpSettings* settings;
|
||||
BIO* frontBio;
|
||||
rdpTls* tlsIn;
|
||||
rdpTls* tlsOut;
|
||||
rdpNtlm* ntlm;
|
||||
HttpContext* http;
|
||||
HANDLE readEvent;
|
||||
|
||||
UUID guid;
|
||||
|
||||
int state;
|
||||
UINT16 packetRemainingCount;
|
||||
int nonBlocking;
|
||||
int timeout;
|
||||
};
|
||||
|
||||
|
||||
rdpRdg* rdg_new(rdpTransport* transport);
|
||||
void rdg_free(rdpRdg* rdg);
|
||||
|
||||
BOOL rdg_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout);
|
||||
UINT32 rdg_get_event_handles(rdpRdg* rdg, HANDLE* events);
|
||||
BOOL rdg_check_event_handles(rdpRdg* rdg);
|
||||
|
||||
|
||||
#endif /* FREERDP_CORE_RDG_H */
|
@ -554,6 +554,10 @@ int rpc_out_channel_transition_to_state(RpcOutChannel* outChannel, CLIENT_OUT_CH
|
||||
str = "CLIENT_OUT_CHANNEL_STATE_OPENED_B3W";
|
||||
break;
|
||||
|
||||
case CLIENT_OUT_CHANNEL_STATE_RECYCLED:
|
||||
str = "CLIENT_OUT_CHANNEL_STATE_RECYCLED";
|
||||
break;
|
||||
|
||||
case CLIENT_OUT_CHANNEL_STATE_FINAL:
|
||||
str = "CLIENT_OUT_CHANNEL_STATE_FINAL";
|
||||
break;
|
||||
@ -866,8 +870,6 @@ int rpc_out_channel_connect(RpcOutChannel* outChannel, int timeout)
|
||||
|
||||
int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout)
|
||||
{
|
||||
int status = 0;
|
||||
HttpResponse* response = NULL;
|
||||
rdpRpc* rpc = outChannel->rpc;
|
||||
|
||||
/* Connect OUT Channel */
|
||||
|
@ -448,16 +448,23 @@ int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment)
|
||||
int rpc_client_default_out_channel_recv(rdpRpc* rpc)
|
||||
{
|
||||
int status = -1;
|
||||
UINT32 statusCode;
|
||||
HttpResponse* response;
|
||||
RpcInChannel* inChannel;
|
||||
RpcOutChannel* outChannel;
|
||||
HANDLE outChannelEvent = NULL;
|
||||
RpcVirtualConnection* connection = rpc->VirtualConnection;
|
||||
|
||||
inChannel = connection->DefaultInChannel;
|
||||
outChannel = connection->DefaultOutChannel;
|
||||
|
||||
BIO_get_event(outChannel->tls->bio, &outChannelEvent);
|
||||
|
||||
if (outChannel->State < CLIENT_OUT_CHANNEL_STATE_OPENED)
|
||||
{
|
||||
if (WaitForSingleObject(outChannelEvent, 0) != WAIT_OBJECT_0)
|
||||
return 1;
|
||||
|
||||
response = http_response_recv(outChannel->tls);
|
||||
|
||||
if (!response)
|
||||
@ -512,18 +519,23 @@ int rpc_client_default_out_channel_recv(rdpRpc* rpc)
|
||||
{
|
||||
/* Receive OUT channel response */
|
||||
|
||||
if (WaitForSingleObject(outChannelEvent, 0) != WAIT_OBJECT_0)
|
||||
return 1;
|
||||
|
||||
response = http_response_recv(outChannel->tls);
|
||||
|
||||
if (!response)
|
||||
return -1;
|
||||
|
||||
if (response->StatusCode != HTTP_STATUS_OK)
|
||||
statusCode = response->StatusCode;
|
||||
|
||||
if (statusCode != HTTP_STATUS_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "error! Status Code: %d", response->StatusCode);
|
||||
WLog_ERR(TAG, "error! Status Code: %d", statusCode);
|
||||
http_response_print(response);
|
||||
http_response_free(response);
|
||||
|
||||
if (response->StatusCode == HTTP_STATUS_DENIED)
|
||||
if (statusCode == HTTP_STATUS_DENIED)
|
||||
{
|
||||
if (!freerdp_get_last_error(rpc->context))
|
||||
freerdp_set_last_error(rpc->context, FREERDP_ERROR_AUTHENTICATION_FAILED);
|
||||
@ -634,22 +646,33 @@ int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc)
|
||||
int status = -1;
|
||||
HttpResponse* response;
|
||||
RpcOutChannel* nextOutChannel;
|
||||
HANDLE nextOutChannelEvent = NULL;
|
||||
|
||||
nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
|
||||
|
||||
BIO_get_event(nextOutChannel->tls->bio, &nextOutChannelEvent);
|
||||
|
||||
if (WaitForSingleObject(nextOutChannelEvent, 0) != WAIT_OBJECT_0)
|
||||
return 1;
|
||||
|
||||
response = http_response_recv(nextOutChannel->tls);
|
||||
|
||||
if (response)
|
||||
{
|
||||
if (nextOutChannel->State == CLIENT_OUT_CHANNEL_STATE_SECURITY)
|
||||
{
|
||||
status = rpc_ncacn_http_recv_out_channel_response(rpc, nextOutChannel, response);
|
||||
|
||||
if (status >= 0)
|
||||
{
|
||||
status = rpc_ncacn_http_send_out_channel_request(rpc, nextOutChannel, TRUE);
|
||||
|
||||
if (status >= 0)
|
||||
{
|
||||
rpc_ncacn_http_ntlm_uninit(rpc, (RpcChannel*)nextOutChannel);
|
||||
rpc_ncacn_http_ntlm_uninit(rpc, (RpcChannel*) nextOutChannel);
|
||||
|
||||
status = rts_send_OUT_R1_A3_pdu(rpc);
|
||||
|
||||
if (status >= 0)
|
||||
{
|
||||
rpc_out_channel_transition_to_state(nextOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W);
|
||||
@ -678,37 +701,31 @@ int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc)
|
||||
|
||||
int rpc_client_out_channel_recv(rdpRpc* rpc)
|
||||
{
|
||||
RpcOutChannel* outChannel;
|
||||
RpcOutChannel* nextOutChannel;
|
||||
int status;
|
||||
RpcVirtualConnection* connection = rpc->VirtualConnection;
|
||||
HANDLE outChannelEvent = NULL;
|
||||
HANDLE nextOutChannelEvent = NULL;
|
||||
|
||||
outChannel = connection->DefaultOutChannel;
|
||||
BIO_get_event(outChannel->tls->bio, &outChannelEvent);
|
||||
|
||||
if (WaitForSingleObject(outChannelEvent, 0) == WAIT_OBJECT_0)
|
||||
if (connection->DefaultOutChannel)
|
||||
{
|
||||
return rpc_client_default_out_channel_recv(rpc);
|
||||
status = rpc_client_default_out_channel_recv(rpc);
|
||||
|
||||
if (status < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
nextOutChannel = connection->NonDefaultOutChannel;
|
||||
if (nextOutChannel)
|
||||
if (connection->NonDefaultOutChannel)
|
||||
{
|
||||
BIO_get_event(nextOutChannel->tls->bio, &nextOutChannelEvent);
|
||||
status = rpc_client_nondefault_out_channel_recv(rpc);
|
||||
|
||||
if (WaitForSingleObject(nextOutChannelEvent, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
return rpc_client_nondefault_out_channel_recv(rpc);
|
||||
}
|
||||
if (status < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rpc_client_in_channel_recv(rdpRpc* rpc)
|
||||
{
|
||||
int status = -1;
|
||||
int status = 1;
|
||||
HttpResponse* response;
|
||||
RpcInChannel* inChannel;
|
||||
RpcOutChannel* outChannel;
|
||||
@ -773,6 +790,17 @@ int rpc_client_in_channel_recv(rdpRpc* rpc)
|
||||
|
||||
http_response_free(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = http_response_recv(inChannel->tls);
|
||||
|
||||
if (!response)
|
||||
return -1;
|
||||
|
||||
/* We can receive an unauthorized HTTP response on the IN channel */
|
||||
|
||||
http_response_free(response);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -678,7 +678,7 @@ int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UI
|
||||
offset += rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset,
|
||||
&BytesReceived, &AvailableWindow, (BYTE*) &ChannelCookie) + 4;
|
||||
|
||||
WLog_ERR(TAG, "Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %d AvailableWindow: %d",
|
||||
WLog_DBG(TAG, "Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %d AvailableWindow: %d",
|
||||
BytesReceived, AvailableWindow);
|
||||
|
||||
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
|
||||
|
@ -1720,7 +1720,7 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, int timeout)
|
||||
|
||||
while (tsg->state != TSG_STATE_PIPE_CREATED)
|
||||
{
|
||||
WaitForMultipleObjects(nCount, events, FALSE, 100);
|
||||
WaitForMultipleObjects(nCount, events, FALSE, 250);
|
||||
|
||||
if (tsg_check_event_handles(tsg) < 0)
|
||||
{
|
||||
@ -1913,10 +1913,11 @@ static int transport_bio_tsg_write(BIO* bio, const char* buf, int num)
|
||||
if (status < 0)
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
return -1;
|
||||
}
|
||||
else if (status == 0)
|
||||
{
|
||||
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE);
|
||||
WSASetLastError(WSAEWOULDBLOCK);
|
||||
}
|
||||
else
|
||||
@ -1939,10 +1940,11 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size)
|
||||
if (status < 0)
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
return -1;
|
||||
}
|
||||
else if (status == 0)
|
||||
{
|
||||
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ);
|
||||
WSASetLastError(WSAEWOULDBLOCK);
|
||||
}
|
||||
else
|
||||
|
@ -397,6 +397,9 @@ rdpSettings* freerdp_settings_new(DWORD flags)
|
||||
|
||||
settings->GatewayUseSameCredentials = FALSE;
|
||||
settings->GatewayBypassLocal = FALSE;
|
||||
settings->GatewayRpcTransport = TRUE;
|
||||
settings->GatewayHttpTransport = TRUE;
|
||||
settings->GatewayUdpTransport = TRUE;
|
||||
|
||||
settings->FastPathInput = TRUE;
|
||||
settings->FastPathOutput = TRUE;
|
||||
|
@ -207,18 +207,50 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por
|
||||
|
||||
if (transport->GatewayEnabled)
|
||||
{
|
||||
transport->tsg = tsg_new(transport);
|
||||
if (!status && settings->GatewayHttpTransport)
|
||||
{
|
||||
transport->rdg = rdg_new(transport);
|
||||
|
||||
if (!transport->tsg)
|
||||
return FALSE;
|
||||
if (!transport->rdg)
|
||||
return FALSE;
|
||||
|
||||
if (!tsg_connect(transport->tsg, hostname, port, timeout))
|
||||
return FALSE;
|
||||
status = rdg_connect(transport->rdg, hostname, port, timeout);
|
||||
|
||||
transport->frontBio = transport->tsg->bio;
|
||||
transport->layer = TRANSPORT_LAYER_TSG;
|
||||
if (status)
|
||||
{
|
||||
transport->frontBio = transport->rdg->frontBio;
|
||||
BIO_set_nonblock(transport->frontBio, 0);
|
||||
transport->layer = TRANSPORT_LAYER_TSG;
|
||||
status = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
rdg_free(transport->rdg);
|
||||
transport->rdg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
status = TRUE;
|
||||
if (!status && settings->GatewayRpcTransport)
|
||||
{
|
||||
transport->tsg = tsg_new(transport);
|
||||
|
||||
if (!transport->tsg)
|
||||
return FALSE;
|
||||
|
||||
status = tsg_connect(transport->tsg, hostname, port, timeout);
|
||||
|
||||
if (status)
|
||||
{
|
||||
transport->frontBio = transport->tsg->bio;
|
||||
transport->layer = TRANSPORT_LAYER_TSG;
|
||||
status = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
tsg_free(transport->tsg);
|
||||
transport->tsg = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -621,7 +653,14 @@ UINT32 transport_get_event_handles(rdpTransport* transport, HANDLE* events)
|
||||
}
|
||||
else
|
||||
{
|
||||
nCount += tsg_get_event_handles(transport->tsg, events);
|
||||
if (transport->rdg)
|
||||
{
|
||||
nCount += rdg_get_event_handles(transport->rdg, events);
|
||||
}
|
||||
else if (transport->tsg)
|
||||
{
|
||||
nCount += tsg_get_event_handles(transport->tsg, events);
|
||||
}
|
||||
}
|
||||
|
||||
return nCount;
|
||||
@ -780,6 +819,12 @@ BOOL transport_disconnect(rdpTransport* transport)
|
||||
transport->tsg = NULL;
|
||||
}
|
||||
|
||||
if (transport->rdg)
|
||||
{
|
||||
rdg_free(transport->rdg);
|
||||
transport->rdg = NULL;
|
||||
}
|
||||
|
||||
transport->frontBio = NULL;
|
||||
|
||||
transport->layer = TRANSPORT_LAYER_TCP;
|
||||
|
@ -35,6 +35,7 @@ typedef struct rdp_transport rdpTransport;
|
||||
#include "nla.h"
|
||||
|
||||
#include "gateway/tsg.h"
|
||||
#include "gateway/rdg.h"
|
||||
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/wlog.h>
|
||||
@ -56,6 +57,7 @@ struct rdp_transport
|
||||
{
|
||||
TRANSPORT_LAYER layer;
|
||||
BIO* frontBio;
|
||||
rdpRdg* rdg;
|
||||
rdpTsg* tsg;
|
||||
rdpTls* tls;
|
||||
rdpContext* context;
|
||||
|
@ -39,6 +39,10 @@
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||
#include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
#define TAG FREERDP_TAG("crypto")
|
||||
|
||||
struct _BIO_RDP_TLS
|
||||
@ -180,6 +184,13 @@ static int bio_rdp_tls_read(BIO* bio, char* buf, int size)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||
if (status > 0)
|
||||
{
|
||||
VALGRIND_MAKE_MEM_DEFINED(buf, status);
|
||||
}
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ BOOL CloseHandle(HANDLE hObject)
|
||||
ULONG Type;
|
||||
WINPR_HANDLE *Object;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hObject, &Type, (PVOID*)&Object))
|
||||
if (!winpr_Handle_GetInfo(hObject, &Type, (PVOID*) &Object))
|
||||
return FALSE;
|
||||
|
||||
if (!Object)
|
||||
@ -57,7 +57,7 @@ BOOL CloseHandle(HANDLE hObject)
|
||||
if (!Object->ops)
|
||||
return FALSE;
|
||||
|
||||
if(Object->ops->CloseHandle)
|
||||
if (Object->ops->CloseHandle)
|
||||
return Object->ops->CloseHandle(hObject);
|
||||
|
||||
return FALSE;
|
||||
|
@ -54,13 +54,14 @@ static int MutexGetFd(HANDLE handle)
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOL MutexCloseHandle(HANDLE handle) {
|
||||
WINPR_MUTEX *mutex = (WINPR_MUTEX *) handle;
|
||||
BOOL MutexCloseHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_MUTEX* mutex = (WINPR_MUTEX*) handle;
|
||||
|
||||
if (!MutexIsHandled(handle))
|
||||
return FALSE;
|
||||
|
||||
if(pthread_mutex_destroy(&mutex->mutex))
|
||||
if (!pthread_mutex_destroy(&mutex->mutex))
|
||||
return FALSE;
|
||||
|
||||
free(handle);
|
||||
@ -68,11 +69,12 @@ BOOL MutexCloseHandle(HANDLE handle) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = {
|
||||
MutexIsHandled,
|
||||
MutexCloseHandle,
|
||||
MutexGetFd,
|
||||
NULL /* CleanupHandle */
|
||||
static HANDLE_OPS ops =
|
||||
{
|
||||
MutexIsHandled,
|
||||
MutexCloseHandle,
|
||||
MutexGetFd,
|
||||
NULL /* CleanupHandle */
|
||||
};
|
||||
|
||||
HANDLE CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName)
|
||||
|
@ -326,6 +326,9 @@ BYTE* BipBuffer_ReadReserve(wBipBuffer* bb, size_t size)
|
||||
|
||||
block = BipBuffer_ReadTryReserve(bb, size, &reserved);
|
||||
|
||||
if (reserved != size)
|
||||
return NULL;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user