import Oracle_OSS freerdp-3.10.3-12.el10_2.8

This commit is contained in:
AlmaLinux RelEng Bot 2026-08-13 16:37:08 -04:00
parent 3392ca130c
commit 67e786c1d0
11 changed files with 1206 additions and 1 deletions

View File

@ -0,0 +1,37 @@
From 18deeba63c4091dfa96973a02df5bc214b4844c3 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Sun, 12 Jul 2026 17:37:48 +0200
Subject: [PATCH] [channels,audin] limit FramesPerPacket
---
channels/audin/client/audin_main.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c
index 23093fb8e..7c403cbbb 100644
--- a/channels/audin/client/audin_main.c
+++ b/channels/audin/client/audin_main.c
@@ -485,7 +485,6 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* call
Stream_Read_UINT32(s, initialFormat);
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
FramesPerPacket, initialFormat);
- audin->FramesPerPacket = FramesPerPacket;
if (initialFormat >= callback->formats_count)
{
@@ -494,6 +493,15 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* call
return ERROR_INVALID_DATA;
}
+ /* The RDP protocol field allows UINT32_MAX, but most backend API use INT32 as input parameter
+ * or do some math with it, so ensure that the value does not exceed reasonable limits */
+ if (FramesPerPacket >= INT32_MAX)
+ {
+ WLog_Print(audin->log, WLOG_ERROR, "invalid frames per packet %" PRIu32, FramesPerPacket);
+ return ERROR_INVALID_DATA;
+ }
+
+ audin->FramesPerPacket = FramesPerPacket;
audin->format = &callback->formats[initialFormat];
if (!audin_open_device(audin, callback))

View File

@ -0,0 +1,228 @@
From 3b25c78c98d25b23246a5267d45f3654c2a8735d Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Wed, 15 Jul 2026 17:52:28 +0200
Subject: [PATCH] [channels,audin] tighten parameter checks
---
channels/audin/client/alsa/audin_alsa.c | 4 +-
channels/audin/client/opensles/opensl_io.c | 43 +++++++++++++++-------
channels/audin/client/opensles/opensl_io.h | 5 +--
channels/audin/client/oss/audin_oss.c | 3 ++
channels/audin/client/sndio/audin_sndio.c | 9 +++--
channels/audin/client/winmm/audin_winmm.c | 17 +++++----
6 files changed, 51 insertions(+), 30 deletions(-)
diff --git a/channels/audin/client/alsa/audin_alsa.c b/channels/audin/client/alsa/audin_alsa.c
index e74097129..5b9f4ea5b 100644
--- a/channels/audin/client/alsa/audin_alsa.c
+++ b/channels/audin/client/alsa/audin_alsa.c
@@ -138,8 +138,8 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
goto out;
}
- buffer =
- (BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
+ buffer = (BYTE*)calloc(1ull * alsa->frames_per_packet + alsa->aformat.nBlockAlign,
+ alsa->bytes_per_frame);
if (!buffer)
{
diff --git a/channels/audin/client/opensles/opensl_io.c b/channels/audin/client/opensles/opensl_io.c
index 39b3dc848..2f27bd60f 100644
--- a/channels/audin/client/opensles/opensl_io.c
+++ b/channels/audin/client/opensles/opensl_io.c
@@ -54,10 +54,10 @@ struct opensl_stream
SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
- unsigned int inchannels;
- unsigned int sr;
- unsigned int buffersize;
- unsigned int bits_per_sample;
+ size_t inchannels;
+ size_t sr;
+ size_t buffersize;
+ size_t bits_per_sample;
queue_element* prep;
queue_element* next;
@@ -109,8 +109,8 @@ engine_end:
static SLresult openSLRecOpen(OPENSL_STREAM* p)
{
SLresult result;
- SLuint32 sr = p->sr;
- SLuint32 channels = p->inchannels;
+ SLuint32 sr = WINPR_ASSERTING_INT_CAST(SLuint32, p->sr);
+ SLuint32 channels = WINPR_ASSERTING_INT_CAST(SLuint32, p->inchannels);
WINPR_ASSERT(!p->recorderObject);
if (channels)
@@ -201,7 +201,10 @@ static SLresult openSLRecOpen(OPENSL_STREAM* p)
format_pcm.containerSize = 8;
}
else
- WINPR_ASSERT(0);
+ {
+ WLog_ERR(TAG, "bits_per_sample=%" PRIuz, p->bits_per_sample);
+ return -1;
+ }
SLDataSink audioSnk = { &loc_bq, &format_pcm };
// create audio recorder
@@ -306,24 +309,31 @@ static void opensles_queue_element_free(void* obj)
}
// open the android audio device for input
-OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, int sr,
- int inchannels, int bufferframes, int bits_per_sample)
+OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, size_t sr,
+ size_t inchannels, size_t bufferframes, size_t bits_per_sample)
{
- OPENSL_STREAM* p;
-
if (!context || !receive)
return NULL;
- p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
+ OPENSL_STREAM* p = (OPENSL_STREAM*)calloc(1, sizeof(OPENSL_STREAM));
if (!p)
return NULL;
p->context = context;
p->receive = receive;
+ if (inchannels > INT32_MAX)
+ goto fail;
p->inchannels = inchannels;
+
+ if (sr > INT32_MAX)
+ goto fail;
p->sr = sr;
+
+ if (bufferframes > SIZE_MAX / 2)
+ goto fail;
p->buffersize = bufferframes;
+
p->bits_per_sample = bits_per_sample;
if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16))
@@ -335,9 +345,14 @@ OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive, in
if (openSLRecOpen(p) != SL_RESULT_SUCCESS)
goto fail;
+ const size_t bytesPerSample = p->bits_per_sample / 8ull;
+ if (p->buffersize >= SIZE_MAX / bytesPerSample)
+ goto fail;
+
+ const size_t queuesize = p->buffersize * bytesPerSample;
/* Create receive buffers, prepare them and start recording */
- p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
- p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
+ p->prep = opensles_queue_element_new(queuesize);
+ p->next = opensles_queue_element_new(queuesize);
if (!p->prep || !p->next)
goto fail;
diff --git a/channels/audin/client/opensles/opensl_io.h b/channels/audin/client/opensles/opensl_io.h
index e99522cfe..07255de5c 100644
--- a/channels/audin/client/opensles/opensl_io.h
+++ b/channels/audin/client/opensles/opensl_io.h
@@ -51,13 +51,12 @@ extern "C"
size in frames. Returns a handle to the OpenSL stream
*/
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
- int sr, int inchannels, int bufferframes,
- int bits_per_sample);
+ size_t sr, size_t inchannels,
+ size_t bufferframes, size_t bits_per_sample);
/*
Close the audio device
*/
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
-
#ifdef __cplusplus
};
#endif
diff --git a/channels/audin/client/oss/audin_oss.c b/channels/audin/client/oss/audin_oss.c
index 8b6ad6cc5..c76326d9c 100644
--- a/channels/audin/client/oss/audin_oss.c
+++ b/channels/audin/client/oss/audin_oss.c
@@ -229,6 +229,9 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
buffer_size =
(1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
+ if (buffer_size > INT32_MAX)
+ goto err_out;
+
buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
if (NULL == buffer)
diff --git a/channels/audin/client/sndio/audin_sndio.c b/channels/audin/client/sndio/audin_sndio.c
index af5426e72..655145db0 100644
--- a/channels/audin/client/sndio/audin_sndio.c
+++ b/channels/audin/client/sndio/audin_sndio.c
@@ -82,7 +82,7 @@ static void* audin_sndio_thread_func(void* arg)
struct sio_hdl* hdl;
struct sio_par par;
BYTE* buffer = NULL;
- size_t n, nbytes;
+ size_t n;
AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
UINT error = 0;
DWORD status;
@@ -125,8 +125,11 @@ static void* audin_sndio_thread_func(void* arg)
goto err_out;
}
- nbytes =
- (sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));
+ const size_t nbytes = (1ull * sndio->FramesPerPacket * sndio->format.nChannels *
+ (sndio->format.wBitsPerSample / 8));
+ if (nbytes > INT32_MAX)
+ goto err_out;
+
buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
if (buffer == NULL)
diff --git a/channels/audin/client/winmm/audin_winmm.c b/channels/audin/client/winmm/audin_winmm.c
index daa59254f..d6cceaf18 100644
--- a/channels/audin/client/winmm/audin_winmm.c
+++ b/channels/audin/client/winmm/audin_winmm.c
@@ -169,8 +169,6 @@ static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
- char* buffer = NULL;
- int size = 0;
WAVEHDR waveHdr[4] = { 0 };
DWORD status = 0;
MMRESULT rc = 0;
@@ -184,19 +182,22 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
return ERROR_INTERNAL_ERROR;
}
- size =
- (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
- 7) /
- 8;
+ const size_t framesize =
+ 1ull * winmm->pwfx_cur->wBitsPerSample / 8ull * winmm->pwfx_cur->nChannels;
+ if (framesize > UINT32_MAX)
+ return ERROR_INTERNAL_ERROR;
+ const size_t size = framesize * winmm->frames_per_packet;
+ if (size > UINT32_MAX)
+ return ERROR_INTERNAL_ERROR;
for (int i = 0; i < 4; i++)
{
- buffer = (char*)malloc(size);
+ char* buffer = (char*)calloc(framesize, winmm->frames_per_packet);
if (!buffer)
return CHANNEL_RC_NO_MEMORY;
- waveHdr[i].dwBufferLength = size;
+ waveHdr[i].dwBufferLength = WINPR_ASSERTING_INT_CAST(DWORD, size);
waveHdr[i].dwFlags = 0;
waveHdr[i].lpData = buffer;
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));

View File

@ -0,0 +1,51 @@
From c4286faee798f10cfa9897f8c482439d1e0a1a83 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 2 Jul 2026 10:21:54 +0200
Subject: [PATCH] [client,common] deactivate cli parsing in RDP files
This feature was uncodumented and might have unintended side effects.
Deactivate it by default and (for the time being) keep an option to
enable it at compile time.
---
client/common/CMakeLists.txt | 5 +++++
client/common/file.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt
index fedac6c5e..dc6fde72a 100644
--- a/client/common/CMakeLists.txt
+++ b/client/common/CMakeLists.txt
@@ -47,6 +47,11 @@ else()
set(OPT_FUSE_DEFAULT OFF)
endif()
+option(WITH_EMBEDDED_CLI_IN_RDP_FILES "[dangrous] allow embedded cli arguments in rdp files" OFF)
+if(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+ add_compile_definitions(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+endif()
+
option(WITH_FUSE "Build clipboard with FUSE file copy support" ${OPT_FUSE_DEFAULT})
if(WITH_FUSE)
find_package(PkgConfig REQUIRED)
diff --git a/client/common/file.c b/client/common/file.c
index f178cad23..696775361 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -881,13 +881,17 @@ static BOOL parse_line(rdpFile* file, char* line, size_t length, rdp_file_fkt_pa
const char* beg = line;
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
+#if defined(WITH_EMBEDDED_CLI_IN_RDP_FILES)
if (beg[0] == '/')
{
+ WLog_WARN(TAG, "Parsing CLI options within an RDP file is deprecated and "
+ "will be removed in FreeRDP 4.0");
if (!freerdp_client_add_option(file, line))
return FALSE;
return TRUE; /* FreeRDP option */
}
+#endif
#endif
char* d1 = strchr(line, ':');

View File

@ -0,0 +1,191 @@
From 802647fa254b761dc01240bcf4e94740aa39e894 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 2 Jul 2026 08:34:18 +0200
Subject: [PATCH] [client,common] deprecate freerdp command line options in rdp
files
---
client/common/file.c | 140 ++++++++++++++++++++-----------------------
1 file changed, 64 insertions(+), 76 deletions(-)
diff --git a/client/common/file.c b/client/common/file.c
index 35e8643f2..f178cad23 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -774,16 +774,6 @@ static SSIZE_T freerdp_client_rdp_file_add_line(rdpFile* file)
return index;
}
-static BOOL freerdp_client_parse_rdp_file_string(rdpFile* file, char* name, char* value)
-{
- return freerdp_client_rdp_file_set_string(file, name, value);
-}
-
-static BOOL freerdp_client_parse_rdp_file_option(rdpFile* file, const char* option)
-{
- return freerdp_client_add_option(file, option);
-}
-
BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, const BYTE* buffer, size_t size)
{
return freerdp_client_parse_rdp_file_buffer_ex(file, buffer, size, NULL);
@@ -884,19 +874,67 @@ static BOOL trim_strings(rdpFile* file)
return TRUE;
}
+static BOOL parse_line(rdpFile* file, char* line, size_t length, rdp_file_fkt_parse parse)
+{
+ if (length <= 1)
+ return TRUE;
+
+ const char* beg = line;
+#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
+ if (beg[0] == '/')
+ {
+ if (!freerdp_client_add_option(file, line))
+ return FALSE;
+
+ return TRUE; /* FreeRDP option */
+ }
+#endif
+
+ char* d1 = strchr(line, ':');
+
+ if (!d1)
+ return TRUE; /* not first delimiter */
+
+ const char* type = &d1[1];
+ char* d2 = strchr(type, ':');
+
+ if (!d2)
+ return TRUE; /* no second delimiter */
+
+ if ((d2 - d1) != 2)
+ return TRUE; /* improper type length */
+
+ *d1 = 0;
+ *d2 = 0;
+ const char* name = beg;
+ const char* value = &d2[1];
+
+ if (parse && parse(file->context, name, *type, value))
+ return TRUE;
+
+ if (*type == 'i')
+ {
+ /* integer type */
+ return freerdp_client_parse_rdp_file_integer(file, name, value);
+ }
+ if (*type == 's')
+ {
+ /* string type */
+ return freerdp_client_rdp_file_set_string(file, name, value);
+ }
+ if (*type == 'b')
+ {
+ /* binary type */
+ WLog_ERR(TAG, "Unsupported RDP file binary option %s [value=%s]", name, value);
+ }
+
+ return TRUE;
+}
+
BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer, size_t size,
rdp_file_fkt_parse parse)
{
BOOL rc = FALSE;
- size_t length = 0;
- char* line = NULL;
- char* type = NULL;
- char* context = NULL;
- char* d1 = NULL;
- char* d2 = NULL;
- char* beg = NULL;
- char* name = NULL;
- char* value = NULL;
char* copy = NULL;
if (!file)
@@ -907,9 +945,8 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
if ((buffer[0] == BOM_UTF16_LE[0]) && (buffer[1] == BOM_UTF16_LE[1]))
{
LPCWSTR uc = (LPCWSTR)(&buffer[2]);
- size = size / sizeof(WCHAR) - 1;
-
- copy = ConvertWCharNToUtf8Alloc(uc, size, NULL);
+ const size_t charlen = size / sizeof(WCHAR) - 1;
+ copy = ConvertWCharNToUtf8Alloc(uc, charlen, &size);
if (!copy)
{
WLog_ERR(TAG, "Failed to convert RDP file from UCS2 to UTF8");
@@ -926,65 +963,16 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
memcpy(copy, buffer, size);
}
- line = strtok_s(copy, "\r\n", &context);
+ char* context = NULL;
+ char* line = strtok_s(copy, "\r\n", &context);
while (line)
{
- length = strnlen(line, size);
-
- if (length > 1)
- {
- beg = line;
- if (beg[0] == '/')
- {
- if (!freerdp_client_parse_rdp_file_option(file, line))
- goto fail;
-
- goto next_line; /* FreeRDP option */
- }
-
- d1 = strchr(line, ':');
-
- if (!d1)
- goto next_line; /* not first delimiter */
+ const size_t length = strnlen(line, size);
- type = &d1[1];
- d2 = strchr(type, ':');
-
- if (!d2)
- goto next_line; /* no second delimiter */
-
- if ((d2 - d1) != 2)
- goto next_line; /* improper type length */
-
- *d1 = 0;
- *d2 = 0;
- name = beg;
- value = &d2[1];
-
- if (parse && parse(file->context, name, *type, value))
- {
- }
- else if (*type == 'i')
- {
- /* integer type */
- if (!freerdp_client_parse_rdp_file_integer(file, name, value))
- goto fail;
- }
- else if (*type == 's')
- {
- /* string type */
- if (!freerdp_client_parse_rdp_file_string(file, name, value))
- goto fail;
- }
- else if (*type == 'b')
- {
- /* binary type */
- WLog_ERR(TAG, "Unsupported RDP file binary option %s [value=%s]", name, value);
- }
- }
+ if (!parse_line(file, line, length, parse))
+ goto fail;
- next_line:
line = strtok_s(NULL, "\r\n", &context);
}

View File

@ -0,0 +1,21 @@
From 005c5abacbb569f0f66deee710e1f1a87794f368 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Wed, 1 Jul 2026 23:03:29 +0200
Subject: [PATCH] [client,common] fix double free
---
client/common/file.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/client/common/file.c b/client/common/file.c
index 35e8643f2..606aab0f7 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -2457,7 +2457,6 @@ BOOL freerdp_client_populate_settings_from_rdp_file(const rdpFile* file, rdpSett
if ((val >= UINT32_MAX) && (errno != 0))
{
CommandLineParserFree(ptr);
- free(list);
return FALSE;
}
list[x] = (UINT32)val;

View File

@ -0,0 +1,60 @@
From 5bf8d59e880336d5a22d7f40e3bb1d74dc81d87b Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 7 Jul 2026 12:58:11 +0200
Subject: [PATCH] [core,message] fix update_message_WindowIcon
---
libfreerdp/core/message.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/libfreerdp/core/message.c b/libfreerdp/core/message.c
index 73961a026..a3f376026 100644
--- a/libfreerdp/core/message.c
+++ b/libfreerdp/core/message.c
@@ -1293,30 +1293,29 @@ static BOOL update_message_WindowUpdate(rdpContext* context, const WINDOW_ORDER_
static BOOL update_message_WindowIcon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_ICON_ORDER* windowIcon)
{
- WINDOW_ORDER_INFO* wParam = NULL;
- WINDOW_ICON_ORDER* lParam = NULL;
- rdp_update_internal* up = NULL;
-
if (!context || !context->update || !orderInfo || !windowIcon)
return FALSE;
- wParam = (WINDOW_ORDER_INFO*)malloc(sizeof(WINDOW_ORDER_INFO));
+ WINDOW_ORDER_INFO* wParam = (WINDOW_ORDER_INFO*)malloc(sizeof(WINDOW_ORDER_INFO));
if (!wParam)
return FALSE;
- CopyMemory(wParam, orderInfo, sizeof(WINDOW_ORDER_INFO));
- lParam = (WINDOW_ICON_ORDER*)calloc(1, sizeof(WINDOW_ICON_ORDER));
+ *wParam = *orderInfo;
+
+ WINDOW_ICON_ORDER* lParam = (WINDOW_ICON_ORDER*)calloc(1, sizeof(WINDOW_ICON_ORDER));
if (!lParam)
goto out_fail;
+ *lParam = *windowIcon;
lParam->iconInfo = calloc(1, sizeof(ICON_INFO));
if (!lParam->iconInfo)
goto out_fail;
- CopyMemory(lParam, windowIcon, sizeof(WINDOW_ICON_ORDER));
+ *lParam->iconInfo = *windowIcon->iconInfo;
+
WLog_VRB(TAG, "update_message_WindowIcon");
if (windowIcon->iconInfo->cbBitsColor > 0)
@@ -1352,7 +1351,7 @@ static BOOL update_message_WindowIcon(rdpContext* context, const WINDOW_ORDER_IN
windowIcon->iconInfo->cbColorTable);
}
- up = update_cast(context->update);
+ rdp_update_internal* up = update_cast(context->update);
return MessageQueue_Post(up->queue, (void*)context, MakeMessageId(WindowUpdate, WindowIcon),
(void*)wParam, (void*)lParam);
out_fail:

View File

@ -0,0 +1,201 @@
From 81747fa9100fdb3fde2aae71cda2a79c82f6e363 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:24 +0200
Subject: [PATCH] [core,redirection] check redirection values for validity
Add some basic validity checks for data provided by server.
---
libfreerdp/core/gateway/arm.c | 11 +++++++++++
libfreerdp/core/redirection.c | 25 ++++++++++++++++++++-----
libfreerdp/core/utils.c | 26 ++++++++++++++++++++++++++
libfreerdp/core/utils.h | 2 ++
4 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/core/gateway/arm.c b/libfreerdp/core/gateway/arm.c
index 4433f6ac9..0e82cf775 100644
--- a/libfreerdp/core/gateway/arm.c
+++ b/libfreerdp/core/gateway/arm.c
@@ -26,6 +26,7 @@
#include <winpr/assert.h>
#include <winpr/crt.h>
+#include <winpr/string.h>
#include <winpr/synch.h>
#include <winpr/print.h>
#include <winpr/stream.h>
@@ -654,6 +655,10 @@ static BOOL arm_parse_ipv6(rdpSettings* settings, WINPR_JSON* ipv6, size_t* pAdd
const char* addr = WINPR_JSON_GetStringValue(adressN);
if (utils_str_is_empty(addr))
continue;
+
+ if (!winpr_str_is_valid_url(addr))
+ return FALSE;
+
if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
(*pAddressIdx)++, addr))
return FALSE;
@@ -684,6 +689,9 @@ static BOOL arm_parse_ipv4(rdpSettings* settings, WINPR_JSON* ipv4, size_t* pAdd
const char* publicIp = WINPR_JSON_GetStringValue(publicIpNode);
if (!utils_str_is_empty(publicIp))
{
+ if (!utils_is_valid_ip(publicIp))
+ return FALSE;
+
if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
(*pAddressIdx)++, publicIp))
return FALSE;
@@ -696,6 +704,9 @@ static BOOL arm_parse_ipv4(rdpSettings* settings, WINPR_JSON* ipv4, size_t* pAdd
const char* privateIp = WINPR_JSON_GetStringValue(privateIpNode);
if (!utils_str_is_empty(privateIp))
{
+ if (!utils_is_valid_ip(privateIp))
+ return FALSE;
+
if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
(*pAddressIdx)++, privateIp))
return FALSE;
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index 286976929..6f7dcdb57 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -699,6 +699,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
return STATE_RUN_FAILED;
+ if (!utils_is_valid_ip(redirection->TargetNetAddress))
+ return STATE_RUN_FAILED;
}
if (redirection->flags & LB_LOAD_BALANCE_INFO)
@@ -712,13 +714,17 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return STATE_RUN_FAILED;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
+ redirection->LoadBalanceInfoLength))
+ return STATE_RUN_FAILED;
}
if (redirection->flags & LB_USERNAME)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
return STATE_RUN_FAILED;
-
+ if (winpr_str_has_newlines(redirection->Username))
+ return STATE_RUN_FAILED;
WLog_DBG(TAG, "Username: %s", redirection->Username);
}
@@ -726,7 +732,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
return STATE_RUN_FAILED;
-
+ if (winpr_str_has_newlines(redirection->Domain))
+ return STATE_RUN_FAILED;
WLog_DBG(TAG, "Domain: %s", redirection->Domain);
}
@@ -784,7 +791,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
return STATE_RUN_FAILED;
-
+ if (!winpr_str_is_valid_url(redirection->TargetFQDN))
+ return STATE_RUN_FAILED;
WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
}
@@ -792,7 +800,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
return STATE_RUN_FAILED;
-
+ if (!winpr_str_is_valid_url(redirection->TargetNetBiosName))
+ return STATE_RUN_FAILED;
WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
}
@@ -801,6 +810,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return STATE_RUN_FAILED;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
+ return STATE_RUN_FAILED;
}
if (redirection->flags & LB_REDIRECTION_GUID)
@@ -862,7 +873,8 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
return STATE_RUN_FAILED;
-
+ if (!utils_is_valid_ip(redirection->TargetNetAddresses[i]))
+ return STATE_RUN_FAILED;
WLog_DBG(TAG, "TargetNetAddresses[%" PRIu32 "]: %s", i,
redirection->TargetNetAddresses[i]);
}
@@ -890,7 +902,10 @@ state_run_t rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream*
status = rdp_recv_server_redirection_pdu(rdp, s);
if (state_run_failed(status))
+ {
+ WLog_Print(rdp->log, WLOG_ERROR, "redirection packet invalid, aborting");
return status;
+ }
if (Stream_GetRemainingLength(s) >= 1)
{
diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c
index 0f395d893..c46c21e23 100644
--- a/libfreerdp/core/utils.c
+++ b/libfreerdp/core/utils.c
@@ -28,6 +28,13 @@
#include <freerdp/channels/cliprdr.h>
#include <freerdp/channels/rdpdr.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+
#include <freerdp/log.h>
#define TAG FREERDP_TAG("core.gateway.utils")
@@ -480,3 +487,22 @@ BOOL utils_reload_channels(rdpContext* context)
return freerdp_channels_pre_connect(context->channels, context->instance) == CHANNEL_RC_OK;
return rc;
}
+
+static BOOL isValidIPv4(const char* ipAddress)
+{
+ struct sockaddr_in sa = { 0 };
+ int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
+ return result != 0;
+}
+
+static BOOL isValidIPv6(const char* ipAddress)
+{
+ struct sockaddr_in6 sa = { 0 };
+ int result = inet_pton(AF_INET6, ipAddress, &(sa.sin6_addr));
+ return result != 0;
+}
+
+BOOL utils_is_valid_ip(const char* ipAddress)
+{
+ return isValidIPv4(ipAddress) || isValidIPv6(ipAddress);
+}
diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h
index a42dcad94..6b25c580c 100644
--- a/libfreerdp/core/utils.h
+++ b/libfreerdp/core/utils.h
@@ -61,4 +61,6 @@ char* utils_redir_flags_to_string(UINT32 flags, char* buffer, size_t size);
BOOL utils_reload_channels(rdpContext* context);
+FREERDP_LOCAL BOOL utils_is_valid_ip(const char* ipAddress);
+
#endif /* FREERDP_LIB_CORE_UTILS_H */

View File

@ -0,0 +1,34 @@
From 1c4b028928be8f38e968d8bfb7d3fb6864540b42 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 14 Jul 2026 12:30:27 +0200
Subject: [PATCH] [core,redirection] relax checks
* TsvUrl is opaque, do not assume any format
* LoadBalanceInfo is opaque, do not assume any format
---
libfreerdp/core/redirection.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index 6f7dcdb57..f9690567d 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -714,9 +714,6 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return STATE_RUN_FAILED;
- if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
- redirection->LoadBalanceInfoLength))
- return STATE_RUN_FAILED;
}
if (redirection->flags & LB_USERNAME)
@@ -810,8 +807,6 @@ static state_run_t rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return STATE_RUN_FAILED;
- if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
- return STATE_RUN_FAILED;
}
if (redirection->flags & LB_REDIRECTION_GUID)

View File

@ -0,0 +1,31 @@
From 86472e2d967e0b211eeb0405955ba146c7a97f5f Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 2 Jul 2026 08:02:26 +0200
Subject: [PATCH] [crypto] fix out buffer check
---
libfreerdp/crypto/crypto.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c
index 806f9b986..e6f156494 100644
--- a/libfreerdp/crypto/crypto.c
+++ b/libfreerdp/crypto/crypto.c
@@ -103,11 +103,14 @@ static SSIZE_T crypto_rsa_common(const BYTE* input, size_t length, UINT32 key_le
goto fail;
if (BN_mod_exp(y, x, exp, mod, ctx) != 1)
goto fail;
- output_length = BN_bn2bin(y, output);
+ {
+ const int len = BN_num_bytes(y);
+ if ((len < 0) || ((size_t)len > out_length))
+ goto fail;
+ output_length = BN_bn2bin(y, output);
+ }
if (output_length < 0)
goto fail;
- if ((size_t)output_length > out_length)
- goto fail;
crypto_reverse(output, output_length);
if ((size_t)output_length < key_length)

View File

@ -30,7 +30,7 @@
Name: freerdp
Epoch: 2
Version: 3.10.3
Release: 12%{?dist}.7
Release: 12%{?dist}.8
Summary: Free implementation of the Remote Desktop Protocol (RDP)
# The effective license is Apache-2.0 but:
@ -277,6 +277,38 @@ Patch: channels-rdpear-disable-ndr-pointer-aliasing.patch
Patch: core-orders-add-codecID-checks.patch
Patch: gdi-graphics-fix-gdi_Bitmap_Decompress.patch
# CVE-2026-64624
# https://github.com/FreeRDP/FreeRDP/commit/e551807abfb87a8a1890c8b7e36fe421cd5596be
# https://github.com/FreeRDP/FreeRDP/commit/22c5deea52404f51a13276b3abda44e1e60704cf
Patch: client-common-deprecate-cli-options-in-rdp-files.patch
Patch: client-common-deactivate-cli-parsing-in-rdp-files.patch
# CVE-2026-64621
# https://github.com/FreeRDP/FreeRDP/commit/7696267929ae8ba045c1bbe275b3915653828313
Patch: client-common-fix-double-free.patch
# CVE-2026-64620
# https://github.com/FreeRDP/FreeRDP/commit/27014741a8c22a34d3040c559c82f4ed82841bef
Patch: crypto-fix-out-buffer-check.patch
# CVE-2026-68580
# https://github.com/FreeRDP/FreeRDP/commit/3bf07d4470bbe42faf4ef8197c8fe1b6b759b00f
# https://github.com/FreeRDP/FreeRDP/commit/9ca0aa69c4679b2be274ac59f30e76d0fa81aaf4
Patch: channels-audin-limit-FramesPerPacket.patch
Patch: channels-audin-tighten-parameter-checks.patch
# CVE-2026-67299
# https://github.com/FreeRDP/FreeRDP/commit/172edbddd24f33033b9eafae6ea1fff0641a24f4
Patch: core-message-fix-update_message_WindowIcon.patch
# CVE-2026-67289
# https://github.com/FreeRDP/FreeRDP/commit/f163b2d2080d922cb725800781f43f16bc2882c0
# https://github.com/FreeRDP/FreeRDP/commit/ab5905cbada0d34cf85ffd32881cacaf8275744f
# https://github.com/FreeRDP/FreeRDP/commit/1db16b4b121a1221c0454f7d223096cac87bf244
Patch: winpr-crt-add-functions-to-test-string.patch
Patch: core-redirection-check-redirection-values-for-validity.patch
Patch: core-redirection-relax-checks.patch
BuildRequires: gcc
BuildRequires: gcc-c++
BuildRequires: alsa-lib-devel
@ -600,6 +632,12 @@ find %{buildroot} -name "*.a" -delete
%{_libdir}/pkgconfig/winpr-tools3.pc
%changelog
* Tue Aug 04 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:3.10.3-12.8
- Backport several CVE fixes (CVE-2026-64620, CVE-2026-64621, CVE-2026-64624,
CVE-2026-67289, CVE-2026-67299, CVE-2026-68580)
Resolves: RHEL-212621, RHEL-212978, RHEL-213168, RHEL-222783, RHEL-222983,
Resolves: RHEL-223604
* Sat Jul 11 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:3.10.3-12.7
- Add codecID checks for CACHE_BITMAP_V3_ORDER (CVE-2026-55827)
- Fix boundary checks in gdi_Bitmap_Decompress (CVE-2026-55827)

View File

@ -0,0 +1,313 @@
From 65d51c15af42a4d6cce83eac416fa25af0c2ca93 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:23 +0200
Subject: [PATCH] [winpr,crt] add functions to test string
* winpr_str_has_newlines to check for newlines in a string
* winpr_str_is_valid_url and winpr_str_is_valid_urlN to check if a
string is a valid url
---
winpr/include/winpr/string.h | 25 ++++++
winpr/libwinpr/crt/CMakeLists.txt | 8 ++
winpr/libwinpr/crt/string.c | 115 +++++++++++++++++++++++++++
winpr/libwinpr/crt/test/TestString.c | 80 +++++++++++++++++++
4 files changed, 228 insertions(+)
diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h
index 45a4098cc..0821293ec 100644
--- a/winpr/include/winpr/string.h
+++ b/winpr/include/winpr/string.h
@@ -39,6 +39,31 @@ extern "C"
WINPR_API char* winpr_str_url_encode(const char* str, size_t len);
WINPR_API char* winpr_str_url_decode(const char* str, size_t len);
+ /** @brief checks if a string is a valid URL
+ *
+ * @param str The string to check
+ * @return \b TRUE if valid, \b FALSE otherwise
+ * @since version 3.29.0
+ */
+ WINPR_API BOOL winpr_str_is_valid_url(const char* str);
+
+ /** @brief checks if a string is a valid URL
+ *
+ * @param str The string to check
+ * @param len The length of the string in bytes
+ * @return \b TRUE if valid, \b FALSE otherwise
+ * @since version 3.29.0
+ */
+ WINPR_API BOOL winpr_str_is_valid_urlN(const char* str, size_t len);
+
+ /** @brief checks if a string contains newlines
+ *
+ * @param str The string to check
+ * @return \b TRUE if it has newlines, \b FALSE otherwise
+ * @since version 3.29.0
+ */
+ WINPR_API BOOL winpr_str_has_newlines(const char* str);
+
WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size,
const char* separator);
diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt
index 18f6ad139..b2cc1cf94 100644
--- a/winpr/libwinpr/crt/CMakeLists.txt
+++ b/winpr/libwinpr/crt/CMakeLists.txt
@@ -17,6 +17,14 @@
set(CRT_FILES alignment.c conversion.c buffer.c memory.c unicode.c string.c)
+include(CheckSymbolExists)
+check_symbol_exists(regcomp regex.h WINPR_HAVE_REGCOMP)
+check_symbol_exists(regexec regex.h WINPR_HAVE_REGEXEC)
+check_symbol_exists(regfree regex.h WINPR_HAVE_REGFREE)
+if(WINPR_HAVE_REGCOMP AND WINPR_HAVE_REGEXEC AND WINPR_HAVE_REGFREE)
+ winpr_definition_add(WINPR_HAVE_REGCOMP)
+endif()
+
if(WITH_UNICODE_BUILTIN)
list(APPEND CRT_FILES unicode_builtin.c)
else()
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index 4dec0b141..b78bbdc2e 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -33,6 +33,10 @@
#include <uriparser/Uri.h>
#endif
+#if defined(WINPR_HAVE_REGCOMP)
+#include <regex.h>
+#endif
+
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
#include "../log.h"
@@ -43,6 +47,28 @@
#endif
#if defined(WITH_URIPARSER)
+BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
+{
+ if (!str || (len == 0))
+ return FALSE;
+
+ const char* errorPos = NULL;
+ UriUriA uri = { 0 };
+ const int rc = uriParseSingleUriExA(&uri, str, &str[len - 1], &errorPos);
+ return rc == URI_SUCCESS;
+}
+
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ if (!str)
+ return FALSE;
+
+ const char* errorPos = NULL;
+ UriUriA uri = { 0 };
+ const int rc = uriParseSingleUriA(&uri, str, &errorPos);
+ return rc == URI_SUCCESS;
+}
+
char* winpr_str_url_decode(const char* str, size_t len)
{
char* dst = strndup(str, len);
@@ -155,6 +181,59 @@ char* winpr_str_url_encode(const char* str, size_t len)
}
return dst;
}
+
+BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
+{
+ if (!str || (len == 0))
+ return FALSE;
+
+ char* url = strndup(str, len);
+ if (!url)
+ return FALSE;
+
+ const BOOL rc = winpr_str_is_valid_url(url);
+ free(url);
+ return rc;
+}
+
+#if defined(_WIN32)
+#include <shlwapi.h>
+
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ if (!str)
+ return FALSE;
+ return PathIsURLA(str);
+}
+
+#elif defined(WINPR_HAVE_REGCOMP)
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ if (!str)
+ return FALSE;
+ regex_t regex = { 0 };
+ const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
+
+ int ret = regcomp(&regex, pattern, REG_EXTENDED);
+ if (ret)
+ {
+ printf("Could not compile regex\n");
+ return FALSE;
+ }
+
+ ret = regexec(&regex, str, 0, NULL, 0);
+ regfree(&regex);
+ return ret == 0;
+}
+
+#else
+BOOL winpr_str_is_valid_url(WINPR_ATTR_UNUSED const char* str)
+{
+ WLog_WARN(TAG, "URL validation not supported by your build, not performing check. Build with "
+ "-DWITH_URIPARSER=ON to fix.");
+ return TRUE;
+}
+#endif
#endif
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
@@ -845,3 +924,39 @@ WCHAR* wcsndup(const WCHAR* s, size_t n)
memcpy(copy, s, n * sizeof(WCHAR));
return copy;
}
+
+BOOL winpr_str_has_newlines(const char* str)
+{
+ if (!str)
+ return FALSE;
+ do
+ {
+ char c = *str++;
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ case 0x0b: /* VT vertical tab */
+ case 0x0c: /* FF form feed */
+ case (char)0x85: /* NEL next line */
+ return TRUE;
+ case 0x20:
+ {
+ switch (*str)
+ {
+ case 0x28: /* LS line separator */
+ case 0x29: /* PS paragraph separator */
+ return TRUE;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case '\0':
+ return FALSE;
+ default:
+ break;
+ }
+ } while (1);
+}
diff --git a/winpr/libwinpr/crt/test/TestString.c b/winpr/libwinpr/crt/test/TestString.c
index e77bf02da..0ab06ab78 100644
--- a/winpr/libwinpr/crt/test/TestString.c
+++ b/winpr/libwinpr/crt/test/TestString.c
@@ -104,6 +104,80 @@ fail:
return rc;
}
+
+static BOOL test_valid_url(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL isUrl;
+ };
+
+ static const struct test_t tests[] = { { "foo.bar.blabla", 15, TRUE },
+ { "somehostname", 12, TRUE },
+ { "somehostname:1234", 17, TRUE },
+ { "tsv://somehostname", 18, TRUE },
+ { "tsv://somehostname/path/foo/gaga", 32, TRUE },
+ { "tsv://somehostname:1234", 23, TRUE },
+ { "tsv://somehostname:1234/path/foo", 33, TRUE },
+ { "lala\rgaga\n", 12, FALSE },
+ { "192.168.0.1", 11, TRUE },
+ //{ "192.168.0.1:1234", 16, FALSE },
+ //{ "192.168.0.1:1234/foo/", 21, FALSE },
+ { "192.168.0.1/bar/foo/", 20, TRUE },
+ { "[::1]", 5, FALSE },
+ { "[::1]:1234", 8, FALSE },
+ { "[2001:db8:3333:4444:5555:6666:7777:8888]", 40,
+ FALSE },
+ { "[2001:db8::1]", 13, FALSE } };
+ BOOL rc = TRUE;
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_is_valid_url(cur->string);
+ const BOOL rc2 = winpr_str_is_valid_urlN(cur->string, cur->len);
+
+#if defined(WINPR_HAVE_REGCOMP) || defined(WITH_URIPARSER)
+ if (rc1 != rc2)
+ rc = FALSE;
+ if (rc1 != cur->isUrl)
+ rc = FALSE;
+#else
+ fprintf(stderr, "[%s] TODO: !defined(WINPR_HAVE_REGCOMP) && !defined(WITH_URIPARSER)\n",
+ __func__);
+#endif
+ }
+
+ return rc;
+}
+
+static BOOL test_newline(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL hasNewlines;
+ };
+
+ const struct test_t tests[] = { { "foo.bar.blabla", 15, FALSE },
+ { "somehostname", 12, FALSE },
+ { "lala\rgaga\n", 13, TRUE } };
+
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_has_newlines(cur->string);
+ if (rc1 != cur->hasNewlines)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int TestString(int argc, char* argv[])
{
const WCHAR* p = NULL;
@@ -114,6 +188,12 @@ int TestString(int argc, char* argv[])
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
+ if (!test_valid_url())
+ return -1;
+
+ if (!test_newline())
+ return -1;
+
if (!test_winpr_asprintf())
return -1;