You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2025/08/01 03:32:34 UTC

(kudu) branch master updated: KUDU-1457 [2/n] add IPv6 support to squeasel function

This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new f8ccb5656 KUDU-1457 [2/n] add IPv6 support to squeasel function
f8ccb5656 is described below

commit f8ccb5656925115de0aed4c80f524f3d9d7c02a0
Author: Ashwani Raina <ar...@cloudera.com>
AuthorDate: Thu Jun 12 16:53:57 2025 +0530

    KUDU-1457 [2/n] add IPv6 support to squeasel function
    
    As part of adding IPv6 support, this patch enables squeasel to
    return a generic address structure for bound addresses instead of IPv4
    address structure in all cases. Caller can typecast returned address
    structure to either IPv4 (i.e. sockaddr_in) or IPv6 (i.e. sockaddr_in6)
    depending on the address family.
    
    The patch fixes the following issues:
    1. Check the config for null before converting to integer using atoi
       to avoid crash due to SIGSEGV. This is possible when using a fake
       context that doesn’t bear valid config for request timeout.
    2. Fix test_parse_port_string test to correct the expectation that
       wrongly considers 0 as invalid port. 0 has special meaning i.e.
       'any port'.
    3. Add 4 new test cases for different scenarios applicable to
       se_get_bound_addresses changes.
    4. Add runtime library for successful compilation of unit_test.
       Without these libraries, unit_test binary creation fails due to
       undefined reference errors during linking.
    5. Add negative test scenarios for IPv6 to test_parse_port_string.
    6. Remove wrong test case from test_should_keep_alive. The test case
       incorrectly expects HTTP/1.1 Connection keep-alive request to fail
       'keep-alive' check. The expectation to fail the check applies to
       previous case i.e. HTTP/1.1 with 'Connection close' request.
    7. Remove setting of conn.status_code as it serves no purpose.
    
    Change-Id: Iee175a00a7ac9e20a31ddb8396468cfcd8f3b6f9
    Reviewed-on: http://gerrit.cloudera.org:8080/23016
    Tested-by: Alexey Serbin <al...@apache.org>
    Reviewed-by: Alexey Serbin <al...@apache.org>
---
 src/kudu/server/webserver.cc                       |   4 +-
 thirdparty/download-thirdparty.sh                  |   5 +-
 ...asel-support-get-bound-addresses-for-ipv6.patch | 346 +++++++++++++++++++++
 3 files changed, 351 insertions(+), 4 deletions(-)

diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index cda67f7b5..49bef8cf1 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -506,7 +506,7 @@ Status Webserver::GetBoundAddresses(std::vector<Sockaddr>* addrs) const {
     return Status::ServiceUnavailable("Not started");
   }
 
-  struct sockaddr_in** sockaddrs;
+  struct sockaddr_storage** sockaddrs;
   int num_addrs;
 
   if (sq_get_bound_addresses(context_, &sockaddrs, &num_addrs)) {
@@ -516,7 +516,7 @@ Status Webserver::GetBoundAddresses(std::vector<Sockaddr>* addrs) const {
   addrs->reserve(num_addrs);
 
   for (int i = 0; i < num_addrs; i++) {
-    addrs->push_back(Sockaddr(*sockaddrs[i]));
+    addrs->emplace_back(*reinterpret_cast<struct sockaddr_in*>(sockaddrs[i]));
     free(sockaddrs[i]);
   }
   free(sockaddrs);
diff --git a/thirdparty/download-thirdparty.sh b/thirdparty/download-thirdparty.sh
index 9a7706651..994be1f10 100755
--- a/thirdparty/download-thirdparty.sh
+++ b/thirdparty/download-thirdparty.sh
@@ -278,13 +278,14 @@ fetch_and_patch \
  "patch -p1 < $TP_DIR/patches/rapidjson-document-assignment-operator-00.patch" \
  "patch -p1 < $TP_DIR/patches/rapidjson-document-assignment-operator-01.patch"
 
-SQUEASEL_PATCHLEVEL=2
+SQUEASEL_PATCHLEVEL=3
 fetch_and_patch \
  squeasel-${SQUEASEL_VERSION}.tar.gz \
  $SQUEASEL_SOURCE \
  $SQUEASEL_PATCHLEVEL \
  "patch -p1 < $TP_DIR/patches/squeasel-handle-openssl-errors.patch" \
- "patch -p1 < $TP_DIR/patches/squeasel-tls-min-version.patch"
+ "patch -p1 < $TP_DIR/patches/squeasel-tls-min-version.patch" \
+ "patch -p1 < $TP_DIR/patches/squeasel-support-get-bound-addresses-for-ipv6.patch"
 
 MUSTACHE_PATCHLEVEL=0
 fetch_and_patch \
diff --git a/thirdparty/patches/squeasel-support-get-bound-addresses-for-ipv6.patch b/thirdparty/patches/squeasel-support-get-bound-addresses-for-ipv6.patch
new file mode 100644
index 000000000..9879f5894
--- /dev/null
+++ b/thirdparty/patches/squeasel-support-get-bound-addresses-for-ipv6.patch
@@ -0,0 +1,346 @@
+diff --git a/Makefile b/Makefile
+index d5bf8e9..d699050 100644
+--- a/Makefile
++++ b/Makefile
+@@ -17,6 +17,9 @@
+ # -DUSE_LUA               - embed Lua in Squeasel (+100kb)
+ 
+ PROG        = squeasel
++
++# TODO: To build target on macOS, additional changes are required,
++#       that include providing appropriate ssl header file path.
+ CFLAGS      = -std=c99 -O2 -W -Wall -pedantic -pthread -pipe $(COPT)
+ 
+ # To build with Lua, download and unzip Lua 5.2.1 source code into the
+@@ -111,6 +114,8 @@ squeasel.o: mod_lua.c
+ linux:
+ 	$(CC) squeasel.c main.c -o $(PROG) -ldl -lrt $(CFLAGS) -lcrypto -lssl
+ 
++# TODO: To build squeasel on macOS, additional changes are required,
++#       that include providing appropriate path to ssl library.
+ mac: bsd
+ bsd:
+ 	$(CC) squeasel.c main.c -o $(PROG) $(CFLAGS)
+@@ -139,9 +144,11 @@ Squeasel: squeasel.c main.c
+ cocoa: Squeasel
+ 	V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' squeasel.c`; DIR=dmg/Squeasel.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/squeasel_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 Squeasel $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Squeasel_$$V.dmg -volname "Squeasel $$V" -srcfolder dmg -ov #; rm -rf dmg
+ 
++# TODO: To build unit test on macOS, additional changes are required,
++#       that include providing appropriate path to ssl library.
+ un:
+ 	$(CC) test/unit_test.c -o unit_test -I. -I$(LUA) $(LUA_SOURCES) \
+-          $(CFLAGS) -g -O0
++          $(CFLAGS) -g -O0 -ldl -lcrypto -lssl -lm
+ 	./unit_test
+ 
+ wi:
+diff --git a/squeasel.c b/squeasel.c
+index d716783..56b062b 100644
+--- a/squeasel.c
++++ b/squeasel.c
+@@ -862,13 +862,13 @@ static int set_non_blocking_mode(SOCKET sock) {
+   return 0;
+ }
+ 
+-int sq_get_bound_addresses(const struct sq_context *ctx, struct sockaddr_in ***addrs,
++int sq_get_bound_addresses(const struct sq_context *ctx, struct sockaddr_storage ***addrs,
+                            int *num_addrs) {
+   int n = ctx->num_listening_sockets;
+   int rc = 1;
+   int i;
+ 
+-  struct sockaddr_in **addr_array = calloc(n, sizeof(struct sockaddr_in *));
++  struct sockaddr_storage **addr_array = calloc(n, sizeof(struct sockaddr_storage *));
+   if (addr_array == NULL) {
+     cry(fc(ctx), "%s: cannot allocate memory", __func__);
+     goto cleanup;
+@@ -876,13 +876,14 @@ int sq_get_bound_addresses(const struct sq_context *ctx, struct sockaddr_in ***a
+   *addrs = addr_array;
+ 
+   for (i = 0; i < n; i++) {
+-    addr_array[i] = malloc(sizeof(struct sockaddr_storage));
++    // Initialize addr_array to 0 to ensure there is no garbage in padding bytes.
++    addr_array[i] = calloc(1, sizeof(struct sockaddr_storage));
+     if (addr_array[i] == NULL) {
+       cry(fc(ctx), "%s: cannot allocate memory", __func__);
+       goto cleanup;
+     }
+ 
+-    socklen_t len = sizeof(struct sockaddr_in *);
++    socklen_t len = sizeof(struct sockaddr_storage);
+     if (getsockname(ctx->listening_sockets[i].sock, (struct sockaddr*)addr_array[i],
+                     &len) != 0) {
+       cry(fc(ctx), "%s: cannot get socket name: %s", __func__, strerror(errno));
+@@ -2380,7 +2381,7 @@ static int read_request(FILE *fp, struct sq_connection *conn,
+     // wait for either the beginning of the request, or for the shutdown
+     // signal.
+     wait_for_readable_or_wakeup(conn->ctx, fp ? fileno(fp) : conn->client.sock,
+-        atoi(conn->ctx->config[REQUEST_TIMEOUT]));
++        atoi((conn->ctx->config[REQUEST_TIMEOUT]) ? conn->ctx->config[REQUEST_TIMEOUT] : "0"));
+   }
+ 
+   while (conn->ctx->stop_flag == 0 &&
+diff --git a/squeasel.h b/squeasel.h
+index d4ca155..c852ea3 100644
+--- a/squeasel.h
++++ b/squeasel.h
+@@ -226,7 +226,6 @@ const char *sq_get_option(const struct sq_context *ctx, const char *name);
+ // Array is NULL terminated.
+ const char **sq_get_valid_option_names(void);
+ 
+-
+ // Return the addresses that the given context is bound to. *addrs is allocated
+ // using malloc to be an array of sockaddr*, each of which is itself malloced.
+ // *num_addrs is set to the number of returned addresses.
+@@ -234,7 +233,7 @@ const char **sq_get_valid_option_names(void);
+ // 'addrs' array itself, unless an error occurs.
+ //
+ // Returns 0 on success, non-zero if an error occurred.
+-int sq_get_bound_addresses(const struct sq_context *ctx, struct sockaddr_in ***addrs,
++int sq_get_bound_addresses(const struct sq_context *ctx, struct sockaddr_storage ***addrs,
+                            int *num_addrs);
+ 
+ // Add, edit or delete the entry in the passwords file.
+diff --git a/test/unit_test.c b/test/unit_test.c
+index 572e885..bf7c474 100644
+--- a/test/unit_test.c
++++ b/test/unit_test.c
+@@ -49,11 +49,27 @@ static int s_failed_tests = 0;
+ #define HTTP_PORT "56789"
+ #define HTTPS_PORT "56790"
+ #define HTTP_PORT2 "56791"
+-#define LISTENING_ADDR          \
+-  "127.0.0.1:" HTTP_PORT "r"    \
+-  ",127.0.0.1:" HTTPS_PORT "s"  \
++#define LISTENING_ADDR_IPv4_LOOPBACK  \
++  "127.0.0.1:" HTTP_PORT "r"          \
++  ",127.0.0.1:" HTTPS_PORT "s"        \
+   ",127.0.0.1:" HTTP_PORT2
+ 
++#define LISTENING_ADDR_IPv4_UNSPEC    \
++  "0.0.0.0:" HTTP_PORT "r"            \
++  ",0.0.0.0:" HTTPS_PORT "s"          \
++  ",0.0.0.0:" HTTP_PORT2
++
++#define LISTENING_ADDR_IPv6_LOOPBACK  \
++  "[::1]:" HTTP_PORT "r"              \
++  ",[::1]:" HTTPS_PORT "s"            \
++  ",[::1]:" HTTP_PORT2
++
++#define LISTENING_ADDR_IPv6_UNSPEC    \
++  "[::]:" HTTP_PORT "r"               \
++  ",[::]:" HTTPS_PORT "s"             \
++  ",[::]:" HTTP_PORT2
++
++
+ static void test_parse_http_message() {
+   struct sq_request_info ri;
+   char req1[] = "GET / HTTP/1.1\r\n\r\n";
+@@ -128,10 +144,6 @@ static void test_should_keep_alive(void) {
+   parse_http_message(req4, sizeof(req4), &conn.request_info);
+   ASSERT(should_keep_alive(&conn) == 1);
+ 
+-  conn.status_code = 401;
+-  ASSERT(should_keep_alive(&conn) == 0);
+-
+-  conn.status_code = 200;
+   conn.must_close = 1;
+   ASSERT(should_keep_alive(&conn) == 0);
+ }
+@@ -286,14 +298,36 @@ static const struct sq_callbacks CALLBACKS = {
+   &open_file_cb, NULL, &upload_cb, NULL
+ };
+ 
+-static const char *OPTIONS[] = {
++static const char *OPTIONS_IPv4_LOOPBACK[] = {
++  "document_root", ".",
++  "listening_ports", LISTENING_ADDR_IPv4_LOOPBACK,
++  "enable_keep_alive", "yes",
++  "ssl_certificate", "build/ssl_cert.pem",
++  NULL,
++};
++
++static const char *OPTIONS_IPv4_UNSPEC[] = {
+   "document_root", ".",
+-  "listening_ports", LISTENING_ADDR,
++  "listening_ports", LISTENING_ADDR_IPv4_UNSPEC,
+   "enable_keep_alive", "yes",
+   "ssl_certificate", "build/ssl_cert.pem",
+   NULL,
+ };
+ 
++static const char *OPTIONS_IPv6_LOOPBACK[] = {
++  "document_root", ".",
++  "listening_ports", LISTENING_ADDR_IPv6_LOOPBACK,
++  "ssl_certificate", "build/ssl_cert.pem",
++  NULL,
++};
++
++static const char *OPTIONS_IPv6_UNSPEC[] = {
++  "document_root", ".",
++  "listening_ports", LISTENING_ADDR_IPv6_UNSPEC,
++  "ssl_certificate", "build/ssl_cert.pem",
++  NULL,
++};
++
+ static char *read_conn(struct sq_connection *conn, int *size) {
+   char buf[100], *data = NULL;
+   int len;
+@@ -312,7 +346,7 @@ static void test_sq_download(void) {
+   struct sq_connection *conn;
+   struct sq_context *ctx;
+ 
+-  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv4_LOOPBACK)) != NULL);
+ 
+   ASSERT(sq_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
+   ASSERT(sq_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
+@@ -393,7 +427,7 @@ static void test_sq_upload(void) {
+   char ebuf[100], buf[20], *file_data, *file2_data, *post_data;
+   int file_len, file2_len, post_data_len;
+ 
+-  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv4_LOOPBACK)) != NULL);
+ 
+   // Upload one file
+   ASSERT((file_data = read_file("squeasel.c", &file_len)) != NULL);
+@@ -627,7 +661,7 @@ static void test_request_replies(void) {
+     {NULL, NULL},
+   };
+ 
+-  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv4_LOOPBACK)) != NULL);
+   for (i = 0; tests[i].request != NULL; i++) {
+     ASSERT((conn = sq_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
+                                tests[i].request)) != NULL);
+@@ -666,7 +700,7 @@ static void test_api_calls(void) {
+ 
+   memset(&callbacks, 0, sizeof(callbacks));
+   callbacks.begin_request = api_callback;
+-  ASSERT((ctx = sq_start(&callbacks, (void *) 123, OPTIONS)) != NULL);
++  ASSERT((ctx = sq_start(&callbacks, (void *) 123, OPTIONS_IPv4_LOOPBACK)) != NULL);
+   ASSERT((conn = sq_download("localhost", atoi(HTTPS_PORT), 1,
+                              ebuf, sizeof(ebuf), "%s", request)) != NULL);
+   sq_close_connection(conn);
+@@ -728,6 +762,98 @@ static void test_strtoll(void) {
+   ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
+ }
+ 
++static char *port_num[] = {HTTP_PORT, HTTPS_PORT, HTTP_PORT2};
++
++static void test_sq_get_bound_addresses_ipv4_loopback(void) {
++  struct sq_context *ctx;
++  struct sockaddr_storage** sockaddrs;
++  int num_addrs;
++  char addr_str[INET_ADDRSTRLEN];
++
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv4_LOOPBACK)) != NULL);
++  ASSERT(sq_get_bound_addresses(ctx, &sockaddrs, &num_addrs) == 0);
++  ASSERT(num_addrs == 3);
++  for (int i = 0; i < num_addrs; i++) {
++    ASSERT(sockaddrs[i]->ss_family == AF_INET);
++    struct sockaddr_in *s_in = (struct sockaddr_in *)sockaddrs[i];
++    ASSERT(inet_ntop(AF_INET, &s_in->sin_addr.s_addr, addr_str, INET_ADDRSTRLEN));
++    ASSERT(strncmp("127.0.0.1", addr_str, INET_ADDRSTRLEN) == 0);
++    uint16_t port = ntohs(s_in->sin_port);
++    ASSERT(atoi(port_num[i]) == port);
++    free(sockaddrs[i]);
++  }
++  free(sockaddrs);
++  sq_stop(ctx);
++}
++
++static void test_sq_get_bound_addresses_ipv4_unspec(void) {
++  struct sq_context *ctx;
++  struct sockaddr_storage** sockaddrs;
++  int num_addrs;
++  char addr_str[INET_ADDRSTRLEN];
++
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv4_UNSPEC)) != NULL);
++  ASSERT(sq_get_bound_addresses(ctx, &sockaddrs, &num_addrs) == 0);
++  ASSERT(num_addrs == 3);
++  for (int i = 0; i < num_addrs; i++) {
++    ASSERT(sockaddrs[i]->ss_family == AF_INET);
++    struct sockaddr_in *s_in = (struct sockaddr_in *)sockaddrs[i];
++    ASSERT(inet_ntop(AF_INET, &s_in->sin_addr.s_addr, addr_str, INET_ADDRSTRLEN));
++    ASSERT(strncmp("0.0.0.0", addr_str, INET_ADDRSTRLEN) == 0);
++    uint16_t port = ntohs(s_in->sin_port);
++    ASSERT(atoi(port_num[i]) == port);
++    free(sockaddrs[i]);
++  }
++  free(sockaddrs);
++  sq_stop(ctx);
++}
++
++static void test_sq_get_bound_addresses_ipv6_loopback(void) {
++  struct sq_context *ctx;
++  struct sockaddr_storage** sockaddrs;
++  int num_addrs;
++  char addr_str[INET6_ADDRSTRLEN];
++
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv6_LOOPBACK)) != NULL);
++  ASSERT(sq_get_bound_addresses(ctx, &sockaddrs, &num_addrs) == 0);
++  ASSERT(num_addrs == 3);
++  for (int i = 0; i < num_addrs; i++) {
++    memset(addr_str, 0, INET6_ADDRSTRLEN);
++    ASSERT(sockaddrs[i]->ss_family == AF_INET6);
++    struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *)sockaddrs[i];
++    ASSERT(inet_ntop(AF_INET6, &s_in6->sin6_addr.s6_addr, addr_str, INET6_ADDRSTRLEN));
++    ASSERT(strncmp("::1", addr_str, INET6_ADDRSTRLEN) == 0);
++    uint16_t port = ntohs(s_in6->sin6_port);
++    ASSERT(atoi(port_num[i]) == port);
++    free(sockaddrs[i]);
++  }
++  free(sockaddrs);
++  sq_stop(ctx);
++}
++
++static void test_sq_get_bound_addresses_ipv6_unspec(void) {
++  struct sq_context *ctx;
++  struct sockaddr_storage** sockaddrs;
++  int num_addrs;
++  char addr_str[INET6_ADDRSTRLEN];
++
++  ASSERT((ctx = sq_start(&CALLBACKS, NULL, OPTIONS_IPv6_UNSPEC)) != NULL);
++  ASSERT(sq_get_bound_addresses(ctx, &sockaddrs, &num_addrs) == 0);
++  ASSERT(num_addrs == 3);
++  for (int i = 0; i < num_addrs; i++) {
++    memset(addr_str, 0, INET6_ADDRSTRLEN);
++    ASSERT(sockaddrs[i]->ss_family == AF_INET6);
++    struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *)sockaddrs[i];
++    ASSERT(inet_ntop(AF_INET6, &s_in6->sin6_addr.s6_addr, addr_str, INET6_ADDRSTRLEN));
++    ASSERT(strncmp("::", addr_str, INET6_ADDRSTRLEN) == 0);
++    uint16_t port = ntohs(s_in6->sin6_port);
++    ASSERT(atoi(port_num[i]) == port);
++    free(sockaddrs[i]);
++  }
++  free(sockaddrs);
++  sq_stop(ctx);
++}
++
+ static void test_parse_port_string(void) {
+   static const char *valid[] = {
+     "1", "1s", "1r", "1.2.3.4:1", "1.2.3.4:1s", "1.2.3.4:1r",
+@@ -737,7 +863,10 @@ static void test_parse_port_string(void) {
+     NULL
+   };
+   static const char *invalid[] = {
+-    "0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
++    "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
++#if defined(USE_IPV6)
++    "::1::123", "[::1]", "[3ffe::1]:", "[3ffe:2a00:100:7031::1]:900p",
++#endif
+     NULL
+   };
+   struct socket so;
+@@ -778,6 +907,10 @@ int __cdecl main(void) {
+   test_url_decode();
+   test_sq_get_cookie();
+   test_strtoll();
++  test_sq_get_bound_addresses_ipv4_loopback();
++  test_sq_get_bound_addresses_ipv4_unspec();
++  test_sq_get_bound_addresses_ipv6_loopback();
++  test_sq_get_bound_addresses_ipv6_unspec();
+ #ifdef USE_LUA
+   test_lua();
+ #endif