]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Merge commit from fork
authorDmitry Verenitsin <morbit85@gmail.com>
Tue, 26 May 2026 19:02:42 +0000 (00:02 +0500)
committerGitHub <noreply@github.com>
Tue, 26 May 2026 19:02:42 +0000 (22:02 +0300)
Cap `Content-Length` at `HTTP_POST_MAX_BODY` (10 MiB) and size the
allocation to the actual body length (`content_length + 1` for
the trailing NUL).

Also fix `WS_BLOCK` units — `kws_raw_read` takes ms, set to 10000.

src/mod/endpoints/mod_verto/mod_verto.c
tests/unit/Makefile.am
tests/unit/conf_verto/freeswitch.xml [new file with mode: 0644]
tests/unit/conf_verto/verto.conf.xml [new file with mode: 0644]
tests/unit/test_mod_verto.c [new file with mode: 0644]

index 113a21c57916872c14f6df4e30d73a5a0aa6bb26..e4a9dc3fdad22f81cecd9aaf8bffc74e653f3a70 100644 (file)
@@ -41,6 +41,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime);
 SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime);
 
 #define HTTP_CHUNK_SIZE 1024 * 32
+#define HTTP_POST_MAX_BODY (10 * 1024 * 1024)   /* max accepted Content-Length for form-urlencoded POST */
 #define EP_NAME "verto.rtc"
 //#define WSS_STANDALONE 1
 #include "libks/ks.h"
@@ -1824,7 +1825,7 @@ new_req:
                char *buffer = NULL;
                switch_ssize_t len = 0, bytes = 0;
 
-               if (request->content_length && request->content_length > 10 * 1024 * 1024 - 1) {
+               if (request->content_length && request->content_length >= HTTP_POST_MAX_BODY) {
                        char *data = "HTTP/1.1 413 Request Entity Too Large\r\n"
                                "Content-Length: 0\r\n\r\n";
                        kws_raw_write(jsock->ws, data, strlen(data));
@@ -1832,16 +1833,16 @@ new_req:
                        goto done;
                }
 
-               if (!(buffer = malloc(2 * 1024 * 1024))) {
+               if (!(buffer = malloc(request->content_length + 1))) {
                        goto request_err;
                }
 
                while(bytes < (switch_ssize_t)request->content_length) {
                        len = request->content_length - bytes;
 
-#define WS_BLOCK 1
+#define WS_BLOCK 10000   /* ms; matches libks's internal default */
 
-                       if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) < 0) {
+                       if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) <= 0) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Read error %" SWITCH_SSIZE_T_FMT"\n", len);
                                goto done;
                        }
index 2f83bca68f6fad9201234969bad38f795c7e440a..47542e34eae194cfc0fb54a72da0853f0b8bc510 100644 (file)
@@ -6,6 +6,7 @@ noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer
 noinst_PROGRAMS += switch_stun
 noinst_PROGRAMS += test_tts_format
 noinst_PROGRAMS+= switch_hold switch_sip
+noinst_PROGRAMS += test_mod_verto
 
 if HAVE_PCAP
 noinst_PROGRAMS += switch_rtp_pcap
diff --git a/tests/unit/conf_verto/freeswitch.xml b/tests/unit/conf_verto/freeswitch.xml
new file mode 100644 (file)
index 0000000..fb316ff
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<document type="freeswitch/xml">
+  <X-PRE-PROCESS cmd="set" data="local_ip_v4=127.0.0.1"/>
+  <X-PRE-PROCESS cmd="set" data="domain=127.0.0.1"/>
+
+  <section name="configuration" description="Configuration">
+
+    <configuration name="modules.conf" description="Modules">
+      <modules>
+        <load module="mod_console"/>
+        <load module="mod_loopback"/>
+        <load module="mod_dptools"/>
+        <load module="mod_dialplan_xml"/>
+        <load module="mod_sndfile"/>
+        <load module="mod_verto"/>
+      </modules>
+    </configuration>
+
+    <configuration name="switch.conf" description="Core Configuration">
+      <settings>
+        <param name="colorize-console" value="false"/>
+        <param name="loglevel" value="debug"/>
+        <param name="rtp-start-port" value="16384"/>
+        <param name="rtp-end-port" value="16484"/>
+      </settings>
+    </configuration>
+
+    <configuration name="console.conf" description="Console Logger">
+      <mappings>
+        <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
+      </mappings>
+      <settings>
+        <param name="colorize" value="false"/>
+        <param name="loglevel" value="debug"/>
+      </settings>
+    </configuration>
+
+    <configuration name="timezones.conf" description="Timezones">
+      <timezones>
+        <zone name="GMT" value="GMT0"/>
+      </timezones>
+    </configuration>
+
+    <X-PRE-PROCESS cmd="include" data="verto.conf.xml"/>
+
+  </section>
+</document>
diff --git a/tests/unit/conf_verto/verto.conf.xml b/tests/unit/conf_verto/verto.conf.xml
new file mode 100644 (file)
index 0000000..ece1229
--- /dev/null
@@ -0,0 +1,36 @@
+<configuration name="verto.conf" description="HTML5 Verto Endpoint (test)">
+
+  <settings>
+    <param name="debug" value="0"/>
+  </settings>
+
+  <profiles>
+    <profile name="test-v4">
+      <!--
+        bind-local without "secure" → plain TCP. Port 33081 chosen to avoid
+        clashing with a default vanilla install on 8081.
+      -->
+      <param name="bind-local" value="127.0.0.1:33081"/>
+      <param name="force-register-domain" value="127.0.0.1"/>
+      <param name="userauth" value="true"/>
+      <param name="blind-reg" value="false"/>
+      <param name="rtp-ip" value="127.0.0.1"/>
+      <param name="timer-name" value="soft"/>
+
+      <!--
+        vhosts block is REQUIRED for http_run() to be invoked
+        (see mod_verto.c:2041,2061 — KWS_HTTP flag depends on it).
+        No auth-realm here, so requests bypass the 401 challenge and
+        reach the body-read path (which is itself pre-auth anyway).
+      -->
+      <vhosts>
+        <vhost domain="127.0.0.1">
+          <param name="alias" value="localhost"/>
+          <param name="root" value="."/>
+          <param name="index" value="index.html"/>
+        </vhost>
+      </vhosts>
+    </profile>
+  </profiles>
+
+</configuration>
diff --git a/tests/unit/test_mod_verto.c b/tests/unit/test_mod_verto.c
new file mode 100644 (file)
index 0000000..c3f414a
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2026, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dmitry Verenitsin <dmitry.verenitsin@signalwire.com>
+ *
+ *
+ * test_mod_verto.c -- Tests for mod_verto
+ *
+ */
+
+#include <switch.h>
+#include <test/switch_test.h>
+
+#define VERTO_TEST_HOST "127.0.0.1"
+#define VERTO_TEST_PORT 33081
+
+/* Must match HTTP_POST_MAX_BODY in src/mod/endpoints/mod_verto/mod_verto.c */
+#define VERTO_POST_MAX_BODY (10 * 1024 * 1024)
+
+static switch_status_t verto_connect(switch_socket_t **sock_out, switch_memory_pool_t *pool)
+{
+       switch_sockaddr_t *addr = NULL;
+       switch_socket_t *sock = NULL;
+       int attempts;
+
+       if (switch_sockaddr_info_get(&addr, VERTO_TEST_HOST, SWITCH_UNSPEC,
+                                                                VERTO_TEST_PORT, 0, pool) != SWITCH_STATUS_SUCCESS) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       for (attempts = 0; attempts < 50; attempts++) {
+               if (switch_socket_create(&sock, switch_sockaddr_get_family(addr),
+                                                                SOCK_STREAM, SWITCH_PROTO_TCP, pool) != SWITCH_STATUS_SUCCESS) {
+                       return SWITCH_STATUS_FALSE;
+               }
+               switch_socket_opt_set(sock, SWITCH_SO_TCP_NODELAY, 1);
+
+               if (switch_socket_connect(sock, addr) == SWITCH_STATUS_SUCCESS) {
+                       *sock_out = sock;
+                       return SWITCH_STATUS_SUCCESS;
+               }
+
+               switch_socket_close(sock);
+               sock = NULL;
+               switch_yield(100000);
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+static switch_status_t send_all(switch_socket_t *sock, const char *buf, switch_size_t len)
+{
+       switch_size_t remaining = len;
+       const char *p = buf;
+
+       while (remaining > 0) {
+               switch_size_t n = remaining;
+               if (switch_socket_send(sock, p, &n) != SWITCH_STATUS_SUCCESS) {
+                       return SWITCH_STATUS_FALSE;
+               }
+               if (n == 0) {
+                       return SWITCH_STATUS_FALSE;
+               }
+               p += n;
+               remaining -= n;
+       }
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_size_t read_status_line(switch_socket_t *sock, char *out, switch_size_t cap)
+{
+       switch_size_t got = 0;
+
+       while (got < cap - 1) {
+               switch_size_t want = cap - 1 - got;
+               if (switch_socket_recv(sock, out + got, &want) != SWITCH_STATUS_SUCCESS || want == 0) {
+                       break;
+               }
+               got += want;
+               if (memchr(out, '\n', got)) break;
+       }
+       out[got] = '\0';
+       return got;
+}
+
+FST_CORE_DB_BEGIN("./conf_verto")
+{
+       FST_SUITE_BEGIN(test_mod_verto)
+       {
+               FST_SETUP_BEGIN()
+               {
+                       fst_requires_module("mod_verto");
+                       switch_yield(500000);
+               }
+               FST_SETUP_END()
+
+               FST_TEARDOWN_BEGIN()
+               {
+               }
+               FST_TEARDOWN_END()
+
+               FST_TEST_BEGIN(post_at_cap_returns_413)
+               {
+                       switch_memory_pool_t *pool = NULL;
+                       switch_socket_t *sock = NULL;
+                       char req[256];
+                       char resp[64] = { 0 };
+                       switch_size_t req_len;
+
+                       do {
+                               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not allocate memory pool");
+                                       break;
+                               }
+                               if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not connect to verto listener");
+                                       break;
+                               }
+
+                               req_len = switch_snprintf(req, sizeof(req),
+                                       "POST / HTTP/1.1\r\n"
+                                       "Host: " VERTO_TEST_HOST "\r\n"
+                                       "Content-Type: application/x-www-form-urlencoded\r\n"
+                                       "Content-Length: %d\r\n"
+                                       "\r\n",
+                                       VERTO_POST_MAX_BODY);
+
+                               if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send request");
+                                       break;
+                               }
+
+                               read_status_line(sock, resp, sizeof(resp));
+                               fst_check_string_starts_with(resp, "HTTP/1.1 413");
+                       } while (0);
+
+                       if (sock) switch_socket_close(sock);
+                       if (pool) switch_core_destroy_memory_pool(&pool);
+               }
+               FST_TEST_END()
+
+               FST_TEST_BEGIN(post_small_body_parsed)
+               {
+                       switch_memory_pool_t *pool = NULL;
+                       switch_socket_t *sock = NULL;
+                       const switch_size_t body_len = 32 * 1024;
+                       char *body = NULL;
+                       char req[256];
+                       char resp[64] = { 0 };
+                       switch_size_t req_len;
+
+                       do {
+                               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not allocate memory pool");
+                                       break;
+                               }
+                               if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not connect to verto listener");
+                                       break;
+                               }
+
+                               body = malloc(body_len);
+                               if (!body) {
+                                       fst_fail("could not allocate body buffer");
+                                       break;
+                               }
+                               memset(body, 'x', body_len);
+
+                               req_len = switch_snprintf(req, sizeof(req),
+                                       "POST / HTTP/1.1\r\n"
+                                       "Host: " VERTO_TEST_HOST "\r\n"
+                                       "Content-Type: application/x-www-form-urlencoded\r\n"
+                                       "Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
+                                       "\r\n",
+                                       body_len);
+
+                               if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send headers");
+                                       break;
+                               }
+                               if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send body");
+                                       break;
+                               }
+
+                               read_status_line(sock, resp, sizeof(resp));
+                               fst_check_string_starts_with(resp, "HTTP/1.1 ");
+                               fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
+                                       "server returned 413 below cap");
+                       } while (0);
+
+                       free(body);
+                       if (sock) switch_socket_close(sock);
+                       if (pool) switch_core_destroy_memory_pool(&pool);
+               }
+               FST_TEST_END()
+
+               FST_TEST_BEGIN(post_large_body_no_overflow)
+               {
+                       switch_memory_pool_t *pool = NULL;
+                       switch_socket_t *sock = NULL;
+                       const switch_size_t body_len = 8 * 1024 * 1024;
+                       char *body = NULL;
+                       char req[256];
+                       char resp[64] = { 0 };
+                       switch_size_t req_len;
+
+                       do {
+                               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not allocate memory pool");
+                                       break;
+                               }
+                               if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not connect to verto listener");
+                                       break;
+                               }
+
+                               body = malloc(body_len);
+                               if (!body) {
+                                       fst_fail("could not allocate body buffer");
+                                       break;
+                               }
+                               memset(body, 'x', body_len);
+
+                               req_len = switch_snprintf(req, sizeof(req),
+                                       "POST / HTTP/1.1\r\n"
+                                       "Host: " VERTO_TEST_HOST "\r\n"
+                                       "Content-Type: application/x-www-form-urlencoded\r\n"
+                                       "Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
+                                       "\r\n",
+                                       body_len);
+
+                               if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send headers");
+                                       break;
+                               }
+                               if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send body");
+                                       break;
+                               }
+
+                               read_status_line(sock, resp, sizeof(resp));
+                               fst_check_string_starts_with(resp, "HTTP/1.1 ");
+                               fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
+                                       "server returned 413 below cap");
+                       } while (0);
+
+                       free(body);
+                       if (sock) switch_socket_close(sock);
+                       if (pool) switch_core_destroy_memory_pool(&pool);
+               }
+               FST_TEST_END()
+
+               FST_TEST_BEGIN(post_overflow_length_returns_413)
+               {
+                       switch_memory_pool_t *pool = NULL;
+                       switch_socket_t *sock = NULL;
+                       char req[256];
+                       char resp[64] = { 0 };
+                       switch_size_t req_len;
+
+                       do {
+                               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not allocate memory pool");
+                                       break;
+                               }
+                               if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not connect to verto listener");
+                                       break;
+                               }
+
+                               req_len = switch_snprintf(req, sizeof(req),
+                                       "POST / HTTP/1.1\r\n"
+                                       "Host: " VERTO_TEST_HOST "\r\n"
+                                       "Content-Type: application/x-www-form-urlencoded\r\n"
+                                       "Content-Length: 9999999999\r\n"
+                                       "\r\n");
+
+                               if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
+                                       fst_fail("could not send request");
+                                       break;
+                               }
+
+                               read_status_line(sock, resp, sizeof(resp));
+                               fst_check_string_starts_with(resp, "HTTP/1.1 413");
+                       } while (0);
+
+                       if (sock) switch_socket_close(sock);
+                       if (pool) switch_core_destroy_memory_pool(&pool);
+               }
+               FST_TEST_END()
+       }
+       FST_SUITE_END()
+}
+FST_CORE_END()