From ff0b56cb2cdc5720eb37c60e92ab21fe06423cb7 Mon Sep 17 00:00:00 2001 From: Lourival Vieira Neto Date: Fri, 13 Mar 2026 22:14:53 +0000 Subject: [PATCH 1/4] socket: accept data object in send/receive to reuse buffers sock:send(data [, addr]) -- sends data->ptr/size, no string alloc sock:receive(data [, flags [, from]]) -- receives into data->ptr, returns bytes String path is preserved for backward compatibility. luadata_t is moved to luadata.h and luadata_arg() added for cross-module use. Co-Authored-By: Claude Sonnet 4.6 --- lib/luadata.c | 7 ------- lib/luadata.h | 7 +++++++ lib/luasocket.c | 52 +++++++++++++++++++++++++++---------------------- lunatik.h | 14 +++++++++++-- lunatik_obj.c | 4 ++-- 5 files changed, 50 insertions(+), 34 deletions(-) diff --git a/lib/luadata.c b/lib/luadata.c index 0644e41c7..50af77986 100644 --- a/lib/luadata.c +++ b/lib/luadata.c @@ -16,13 +16,6 @@ #include "luadata.h" - -typedef struct luadata_s { - void *ptr; - size_t size; - uint8_t opt; -} luadata_t; - #define LUADATA_NUMBER_SZ (sizeof(lua_Integer)) static int luadata_lnew(lua_State *L); diff --git a/lib/luadata.h b/lib/luadata.h index 0c76b5b18..08347c230 100644 --- a/lib/luadata.h +++ b/lib/luadata.h @@ -26,5 +26,12 @@ static inline void luadata_close(lunatik_object_t *object) #define luadata_attach(L, obj, field, opt) lunatik_attach(L, obj, field, luadata_new, opt) +typedef struct luadata_s { + void *ptr; + size_t size; + uint8_t opt; +} luadata_t; + + #endif diff --git a/lib/luasocket.c b/lib/luasocket.c index d7d08db1f..b99377403 100644 --- a/lib/luasocket.c +++ b/lib/luasocket.c @@ -32,6 +32,7 @@ #endif #include +#include "luadata.h" #define luasocket_msgaddr(msg, addr, size) \ do { \ @@ -117,7 +118,7 @@ LUNATIK_PRIVATECHECKER(luasocket_check, struct socket *); * address family) specify the destination. * * @function send -* @tparam string message The string message to send. +* @tparam string|data message The message to send; a data object avoids string allocation. * @tparam[opt] integer|string addr The destination address. * * - For `AF_INET` (IPv4) sockets: An integer representing the IPv4 address (e.g., from `net.aton()`). @@ -146,7 +147,14 @@ static int luasocket_send(lua_State *L) luasocket_setmsg(msg); - vec.iov_base = (void *)luaL_checklstring(L, 2, &len); + if (lua_isuserdata(L, 2)) { + luadata_t *data = lunatik_checkclass(L, 2, "data", LUNATIK_OPT_SINGLE)->private; + lunatik_argchecknull(L, data->ptr, 2); + vec.iov_base = data->ptr; + len = data->size; + } + else + vec.iov_base = (void *)luaL_checklstring(L, 2, &len); vec.iov_len = len; if (unlikely(nargs >= 3)) { @@ -163,32 +171,21 @@ static int luasocket_send(lua_State *L) * Receives a message from the socket. * * @function receive -* @tparam integer length The maximum number of bytes to receive. -* @tparam[opt=0] integer flags Optional message flags (e.g., `socket.msg.PEEK`). -* See the `socket.msg` table for available flags. These can be OR'd together. -* @tparam[opt=false] boolean from If `true`, the function also returns the sender's address -* and port (for `AF_INET`). This is typically used with connectionless sockets (`SOCK_DGRAM`). -* @treturn string The received message (as a string of bytes). -* @treturn[opt] integer|string addr If `from` is true, the sender's address. -* - For `AF_INET`: An integer representing the IPv4 address (can be converted with `net.ntoa()`). -* - For other families: A packed string representing the sender's address. -* @treturn[opt] integer port If `from` is true and the family is `AF_INET`, the sender's port number. +* @tparam integer|data buffer Maximum bytes to receive, or a data object to receive into. +* @tparam[opt=0] integer flags Message flags (e.g., `socket.msg.PEEK`). +* @tparam[opt=false] boolean from If `true`, also returns sender address and port (`AF_INET`). +* @treturn string|integer Received bytes as a string, or byte count when a data object is passed. +* @treturn[opt] integer|string addr Sender address if `from` is true. +* @treturn[opt] integer port Sender port if `from` is true and family is `AF_INET`. * @raise Error if the receive operation fails. -* @usage -* -- For a connected TCP socket: -* local data = tcp_conn_sock:receive(1024) -* if data then print("Received:", data) end -* -* -- For a UDP socket, getting sender info: -* local data, sender_ip_int, sender_port = udp_sock:receive(1500, 0, true) -* if data then print("Received from " .. net.ntoa(sender_ip_int) .. ":" .. sender_port .. ": " .. data) end * @see socket.msg * @see net.ntoa */ static int luasocket_receive(lua_State *L) { struct socket *socket = luasocket_check(L, 1); - size_t len = (size_t)luaL_checkinteger(L, 2); + int isdata = lua_isuserdata(L, 2); + size_t len; luaL_Buffer B; struct kvec vec; struct msghdr msg; @@ -199,14 +196,23 @@ static int luasocket_receive(lua_State *L) luasocket_setmsg(msg); - vec.iov_base = (void *)luaL_buffinitsize(L, &B, len); + if (isdata) { + luadata_t *data = lunatik_checkclass(L, 2, "data", LUNATIK_OPT_SINGLE)->private; + lunatik_argchecknull(L, data->ptr, 2); + vec.iov_base = data->ptr; + len = data->size; + } + else { + len = (size_t)luaL_checkinteger(L, 2); + vec.iov_base = (void *)luaL_buffinitsize(L, &B, len); + } vec.iov_len = len; if (unlikely(from)) luasocket_msgaddr(msg, addr, sizeof(addr)); lunatik_tryret(L, ret, kernel_recvmsg, socket, &msg, &vec, 1, len, flags); - luaL_pushresultsize(&B, ret); + isdata ? lua_pushinteger(L, ret) : luaL_pushresultsize(&B, ret); return unlikely(from) ? luasocket_pushaddr(L, (struct sockaddr_storage *)msg.msg_name) + 1 : 1; } diff --git a/lunatik.h b/lunatik.h index c55f9f334..b15c77b9b 100644 --- a/lunatik.h +++ b/lunatik.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -194,7 +195,7 @@ static inline lunatik_object_t *lunatik_checkruntime(lua_State *L, lunatik_opt_t #define lunatik_setruntime(L, libname, priv) ((priv)->runtime = lunatik_checkruntime((L), lua##libname##_class.opt)) #define lunatik_monitormt(class, monitor) ((monitor) ? (void *)&(class)->opt : (void *)(class)) -static inline void lunatik_checkclass(lua_State *L, const lunatik_class_t *class) +static inline void lunatik_checkcontext(lua_State *L, const lunatik_class_t *class) { if (lunatik_cannotsleep(L, !lunatik_issoftirq(class->opt))) luaL_error(L, "'%s': %s", class->name, LUNATIK_ERR_CONTEXT); @@ -292,6 +293,15 @@ static inline lunatik_object_t **lunatik_testobject(lua_State *L, int ix) return (pobject && lunatik_isobject(L, ix, *pobject)) ? pobject : NULL; } +static inline lunatik_object_t *lunatik_checkclass(lua_State *L, int ix, + const char *name, lunatik_opt_t opt) +{ + lunatik_object_t *object = lunatik_checkobject(L, ix); + luaL_argcheck(L, strcmp(object->class->name, name) == 0, ix, name); + luaL_argcheck(L, !opt || (object->opt & opt) == opt, ix, name); + return object; +} + static inline lunatik_object_t **lunatik_checkpobject(lua_State *L, int ix) { lunatik_object_t **pobject = lunatik_testobject(L, ix); @@ -320,7 +330,7 @@ int luaopen_##libname(lua_State *L) \ const lunatik_namespace_t *nss = namespaces; /* avoid -Waddress */ \ luaL_newlib(L, funcs); \ if (cls) { \ - lunatik_checkclass(L, cls); \ + lunatik_checkcontext(L, cls); \ if (lunatik_ismonitor(cls->opt)) \ lunatik_newclass(L, cls, true); \ lunatik_newclass(L, cls, false); \ diff --git a/lunatik_obj.c b/lunatik_obj.c index 37bf932a8..448ce2a67 100644 --- a/lunatik_obj.c +++ b/lunatik_obj.c @@ -22,7 +22,7 @@ lunatik_object_t *lunatik_newobject(lua_State *L, const lunatik_class_t *class, lunatik_object_t *object = lunatik_checkalloc(L, sizeof(lunatik_object_t)); /* SOFTIRQ runtime requires a SOFTIRQ class */ - lunatik_checkclass(L, class); + lunatik_checkcontext(L, class); lunatik_setobject(object, class, opt); lunatik_setclass(L, class, lunatik_ismonitor(object->opt)); @@ -62,7 +62,7 @@ void lunatik_cloneobject(lua_State *L, lunatik_object_t *object) lunatik_require(L, class->name); lunatik_object_t **pobject = lunatik_newpobject(L, 1); - lunatik_checkclass(L, class); + lunatik_checkcontext(L, class); lunatik_setclass(L, class, lunatik_ismonitor(object->opt)); *pobject = object; } From 1c92b5ece1313752cdd1cae1b4f7d2b6a240150d Mon Sep 17 00:00:00 2001 From: Lourival Vieira Neto Date: Fri, 13 Mar 2026 22:30:53 +0000 Subject: [PATCH 2/4] tests/runtime: add socket_data to test send/receive with data objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifies all send/receive combinations via UDP loopback: data → data, string → data, data → string. Co-Authored-By: Claude Sonnet 4.6 --- tests/runtime/run.sh | 2 +- tests/runtime/socket_data.lua | 43 ++++++++++++++++++++++++++++ tests/runtime/socket_data.sh | 37 ++++++++++++++++++++++++ tests/runtime/socket_data_reject.lua | 37 ++++++++++++++++++++++++ tests/runtime/socket_data_reject.sh | 37 ++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 tests/runtime/socket_data.lua create mode 100644 tests/runtime/socket_data.sh create mode 100644 tests/runtime/socket_data_reject.lua create mode 100644 tests/runtime/socket_data_reject.sh diff --git a/tests/runtime/run.sh b/tests/runtime/run.sh index c19d07f0d..f362e0196 100644 --- a/tests/runtime/run.sh +++ b/tests/runtime/run.sh @@ -11,7 +11,7 @@ DIR="$(dirname "$(readlink -f "$0")")" FAILED=0 SEP="" -for t in "$DIR"/refcnt_leak.sh "$DIR"/resume_shared.sh "$DIR"/resume_mailbox.sh "$DIR"/rcu_shared.sh "$DIR"/opt_guards.sh "$DIR"/opt_skb_single.sh; do +for t in "$DIR"/refcnt_leak.sh "$DIR"/resume_shared.sh "$DIR"/resume_mailbox.sh "$DIR"/rcu_shared.sh "$DIR"/opt_guards.sh "$DIR"/opt_skb_single.sh "$DIR"/socket_data.sh "$DIR"/socket_data_reject.sh; do echo "${SEP}# --- $(basename "$t") ---" SEP=$'\n' bash "$t" || FAILED=$((FAILED+1)) diff --git a/tests/runtime/socket_data.lua b/tests/runtime/socket_data.lua new file mode 100644 index 000000000..f41abc0ed --- /dev/null +++ b/tests/runtime/socket_data.lua @@ -0,0 +1,43 @@ +-- +-- SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +-- SPDX-License-Identifier: MIT OR GPL-2.0-only +-- +-- Kernel-side script for the socket_data regression test (see socket_data.sh). + +local socket = require("socket") +local net = require("net") +local data = require("data") + +local af = socket.af +local sock = socket.sock +local ip = socket.ipproto +local PORT = 19876 +local MSG = "hello socket data!" +local SIZE = #MSG + +local s = socket.new(af.INET, sock.DGRAM, ip.UDP) +s:bind(net.aton("127.0.0.1"), PORT) + +local sbuf = data.new(SIZE, "single") +sbuf:setstring(0, MSG) +local rbuf = data.new(SIZE, "single") + +-- data → data +s:send(sbuf, net.aton("127.0.0.1"), PORT) +local n = s:receive(rbuf) +assert(n == SIZE, "data→data: expected " .. SIZE .. " bytes, got " .. tostring(n)) +assert(rbuf:getstring(0, n) == MSG, "data→data: content mismatch") + +-- string → data +s:send(MSG, net.aton("127.0.0.1"), PORT) +local n2 = s:receive(rbuf) +assert(n2 == SIZE, "string→data: expected " .. SIZE .. " bytes, got " .. tostring(n2)) +assert(rbuf:getstring(0, n2) == MSG, "string→data: content mismatch") + +-- data → string +s:send(sbuf, net.aton("127.0.0.1"), PORT) +local str = s:receive(SIZE) +assert(str == MSG, "data→string: content mismatch") + +s:close() + diff --git a/tests/runtime/socket_data.sh b/tests/runtime/socket_data.sh new file mode 100644 index 000000000..a2133afed --- /dev/null +++ b/tests/runtime/socket_data.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +# SPDX-License-Identifier: MIT OR GPL-2.0-only +# +# Tests socket send/receive with data objects via a UDP loopback socket. +# Verifies valid combinations: data→data, string→data, data→string. +# +# Usage: sudo bash tests/runtime/socket_data.sh + +SCRIPT="tests/runtime/socket_data" +MODULE="luasocket" + +source "$(dirname "$(readlink -f "$0")")/../lib.sh" + +cleanup() { lunatik stop "$SCRIPT" 2>/dev/null; } +trap cleanup EXIT +cleanup + +ktap_header +ktap_plan 1 + +cat /sys/module/$MODULE/refcnt > /dev/null 2>&1 || { + echo "# SKIP: $MODULE not loaded" + ktap_totals + exit 0 +} + +mark_dmesg + +run_script "$SCRIPT" + +check_dmesg || { ktap_totals; exit 1; } +ktap_pass "socket send/receive with data objects: data→data, string→data, data→string" + +ktap_totals + diff --git a/tests/runtime/socket_data_reject.lua b/tests/runtime/socket_data_reject.lua new file mode 100644 index 000000000..16dd3b90d --- /dev/null +++ b/tests/runtime/socket_data_reject.lua @@ -0,0 +1,37 @@ +-- +-- SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +-- SPDX-License-Identifier: MIT OR GPL-2.0-only +-- +-- Kernel-side script for the socket_data_reject regression test (see socket_data_reject.sh). + +local socket = require("socket") +local net = require("net") +local data = require("data") + +local af = socket.af +local sock = socket.sock +local ip = socket.ipproto +local PORT = 19877 +local SIZE = 16 + +local s = socket.new(af.INET, sock.DGRAM, ip.UDP) +s:bind(net.aton("127.0.0.1"), PORT) + +-- rejection: wrong-type userdata (socket instead of data) +local ok, err = pcall(function() s:send(s, net.aton("127.0.0.1"), PORT) end) +assert(not ok and err:find("bad argument"), "send: wrong type not rejected: " .. tostring(err)) + +local ok2, err2 = pcall(function() s:receive(s) end) +assert(not ok2 and err2:find("bad argument"), "receive: wrong type not rejected: " .. tostring(err2)) + +-- rejection: non-SINGLE (shared) data +local shared = data.new(SIZE, "shared") + +local ok3, err3 = pcall(function() s:send(shared, net.aton("127.0.0.1"), PORT) end) +assert(not ok3 and err3:find("bad argument"), "send: non-SINGLE data not rejected: " .. tostring(err3)) + +local ok4, err4 = pcall(function() s:receive(shared) end) +assert(not ok4 and err4:find("bad argument"), "receive: non-SINGLE data not rejected: " .. tostring(err4)) + +s:close() + diff --git a/tests/runtime/socket_data_reject.sh b/tests/runtime/socket_data_reject.sh new file mode 100644 index 000000000..fd7b978b9 --- /dev/null +++ b/tests/runtime/socket_data_reject.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# SPDX-FileCopyrightText: (c) 2026 Ring Zero Desenvolvimento de Software LTDA +# SPDX-License-Identifier: MIT OR GPL-2.0-only +# +# Verifies that socket send/receive reject wrong-type userdata and non-SINGLE +# data objects with a proper Lua error rather than crashing the kernel. +# +# Usage: sudo bash tests/runtime/socket_data_reject.sh + +SCRIPT="tests/runtime/socket_data_reject" +MODULE="luasocket" + +source "$(dirname "$(readlink -f "$0")")/../lib.sh" + +cleanup() { lunatik stop "$SCRIPT" 2>/dev/null; } +trap cleanup EXIT +cleanup + +ktap_header +ktap_plan 1 + +cat /sys/module/$MODULE/refcnt > /dev/null 2>&1 || { + echo "# SKIP: $MODULE not loaded" + ktap_totals + exit 0 +} + +mark_dmesg + +run_script "$SCRIPT" + +check_dmesg || { ktap_totals; exit 1; } +ktap_pass "socket send/receive correctly reject wrong-type and non-SINGLE data" + +ktap_totals + From 5db772fdea3da13405144de80796d1f5e2ef1407 Mon Sep 17 00:00:00 2001 From: Lourival Vieira Neto Date: Sun, 15 Mar 2026 13:13:13 +0000 Subject: [PATCH 3/4] socket: sanitize LDoc and fix C99 declarations Trim verbose LDoc in module header, send, receive, bind, listen, connect, getsockname, getpeername, close, accept, and new to concise descriptions. Fix C99 issues: remove uninitialized size_t len from send/receive by scoping it inside the else branch and using vec.iov_len directly in the kernel calls; refactor pushaddr to use early returns and eliminate int n; fix double semicolon in checkaddr AF_PACKET branch. Co-Authored-By: Claude Sonnet 4.6 --- lib/luasocket.c | 170 ++++++++++++------------------------------------ 1 file changed, 40 insertions(+), 130 deletions(-) diff --git a/lib/luasocket.c b/lib/luasocket.c index b99377403..609c5ab61 100644 --- a/lib/luasocket.c +++ b/lib/luasocket.c @@ -5,18 +5,10 @@ /*** * Low-level Lua interface for kernel networking sockets. -* This library provides support for creating and managing various types of -* sockets within the Linux kernel, enabling network communication directly -* from Lua scripts running in kernel space. It is inspired by -* [Chengzhi Tan](https://github.com/tcz717)'s +* Inspired by [Chengzhi Tan](https://github.com/tcz717)'s * [GSoC project](https://summerofcode.withgoogle.com/archive/2018/projects/5993341447569408). * -* It allows operations such as creating sockets, binding, listening, connecting, -* sending, and receiving data. The library also exposes constants for address -* families, socket types, IP protocols, and message flags. -* -* For higher-level IPv4 TCP/UDP socket operations with string-based IP addresses -* (e.g., "127.0.0.1"), consider using the `socket.inet` library. +* For higher-level IPv4 TCP/UDP socket operations, consider using the `socket.inet` library. * * @module socket * @see socket.inet @@ -70,7 +62,7 @@ static size_t luasocket_checkaddr(lua_State *L, struct socket *socket, struct so else if (addr->ss_family == AF_PACKET) { struct sockaddr_ll *addr_ll = (struct sockaddr_ll *)addr; addr_ll->sll_protocol = htons((u16)lunatik_checkinteger(L, ix, 0, U16_MAX)); - addr_ll->sll_ifindex = (int)lunatik_checkinteger(L, ix + 1, 0, INT_MAX);; + addr_ll->sll_ifindex = (int)lunatik_checkinteger(L, ix + 1, 0, INT_MAX); return sizeof(struct sockaddr_ll); } else { @@ -84,25 +76,21 @@ static size_t luasocket_checkaddr(lua_State *L, struct socket *socket, struct so static int luasocket_pushaddr(lua_State *L, struct sockaddr_storage *addr) { - int n; if (addr->ss_family == AF_INET) { struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; lua_pushinteger(L, (lua_Integer)ntohl(addr_in->sin_addr.s_addr)); lua_pushinteger(L, (lua_Integer)ntohs(addr_in->sin_port)); - n = 2; + return 2; } #ifdef CONFIG_UNIX else if (LUASOCKET_ISUNIX(addr->ss_family)) { struct sockaddr_un *addr_un = (struct sockaddr_un *)addr; lua_pushstring(L, addr_un->sun_path); - n = 1; + return 1; } #endif - else { - lua_pushlstring(L, (const char *)addr->__data, LUASOCKET_ADDRMAX); - n = 1; - } - return n; + lua_pushlstring(L, (const char *)addr->__data, LUASOCKET_ADDRMAX); + return 1; } LUNATIK_PRIVATECHECKER(luasocket_check, struct socket *); @@ -112,33 +100,17 @@ LUNATIK_PRIVATECHECKER(luasocket_check, struct socket *); /*** * Sends a message through the socket. * -* For connection-oriented sockets (`SOCK_STREAM`), `addr` and `port` are usually omitted -* as the connection is already established. -* For connectionless sockets (`SOCK_DGRAM`), `addr` and `port` (if applicable for the -* address family) specify the destination. -* * @function send * @tparam string|data message The message to send; a data object avoids string allocation. -* @tparam[opt] integer|string addr The destination address. -* -* - For `AF_INET` (IPv4) sockets: An integer representing the IPv4 address (e.g., from `net.aton()`). -* - For other address families (e.g., `AF_PACKET`): A packed string representing the destination address -* (e.g., MAC address for `AF_PACKET`). The exact format depends on the family. -* @tparam[opt] integer port The destination port number (required if `addr` is an IPv4 address for `AF_INET`). +* @tparam[opt] integer|string addr Destination address (family-dependent). +* @tparam[opt] integer port Destination port (required for `AF_INET`). * @treturn integer The number of bytes sent. -* @raise Error if the send operation fails or if address parameters are incorrect for the socket type. -* @usage -* -- For a connected TCP socket: -* local bytes_sent = tcp_conn_sock:send("Hello, server!") -* -* -- For a UDP socket (sending to 192.168.1.100, port 1234): -* local bytes_sent = udp_sock:send("UDP packet", net.aton("192.168.1.100"), 1234) +* @raise Error if the send operation fails. * @see net.aton */ static int luasocket_send(lua_State *L) { struct socket *socket = luasocket_check(L, 1); - size_t len; struct kvec vec; struct msghdr msg; struct sockaddr_storage addr; @@ -151,18 +123,20 @@ static int luasocket_send(lua_State *L) luadata_t *data = lunatik_checkclass(L, 2, "data", LUNATIK_OPT_SINGLE)->private; lunatik_argchecknull(L, data->ptr, 2); vec.iov_base = data->ptr; - len = data->size; + vec.iov_len = data->size; } - else + else { + size_t len; vec.iov_base = (void *)luaL_checklstring(L, 2, &len); - vec.iov_len = len; + vec.iov_len = len; + } if (unlikely(nargs >= 3)) { size_t size = luasocket_checkaddr(L, socket, &addr, 3); luasocket_msgaddr(msg, addr, size); } - lunatik_tryret(L, ret, kernel_sendmsg, socket, &msg, &vec, 1, len); + lunatik_tryret(L, ret, kernel_sendmsg, socket, &msg, &vec, 1, vec.iov_len); lua_pushinteger(L, ret); return 1; } @@ -185,7 +159,6 @@ static int luasocket_receive(lua_State *L) { struct socket *socket = luasocket_check(L, 1); int isdata = lua_isuserdata(L, 2); - size_t len; luaL_Buffer B; struct kvec vec; struct msghdr msg; @@ -200,18 +173,18 @@ static int luasocket_receive(lua_State *L) luadata_t *data = lunatik_checkclass(L, 2, "data", LUNATIK_OPT_SINGLE)->private; lunatik_argchecknull(L, data->ptr, 2); vec.iov_base = data->ptr; - len = data->size; + vec.iov_len = data->size; } else { - len = (size_t)luaL_checkinteger(L, 2); + size_t len = (size_t)luaL_checkinteger(L, 2); vec.iov_base = (void *)luaL_buffinitsize(L, &B, len); + vec.iov_len = len; } - vec.iov_len = len; if (unlikely(from)) luasocket_msgaddr(msg, addr, sizeof(addr)); - lunatik_tryret(L, ret, kernel_recvmsg, socket, &msg, &vec, 1, len, flags); + lunatik_tryret(L, ret, kernel_recvmsg, socket, &msg, &vec, 1, vec.iov_len, flags); isdata ? lua_pushinteger(L, ret) : luaL_pushresultsize(&B, ret); return unlikely(from) ? luasocket_pushaddr(L, (struct sockaddr_storage *)msg.msg_name) + 1 : 1; @@ -219,32 +192,12 @@ static int luasocket_receive(lua_State *L) /*** * Binds the socket to a local address. -* This is typically used on the server side before calling `listen()` or on -* connectionless sockets to specify a local port/interface for receiving. * * @function bind -* @tparam integer|string addr The local address to bind to. The interpretation depends on `socket.sk.sk_family`: -* -* - `AF_INET` (IPv4): An integer representing the IPv4 address (e.g., from `net.aton()`). -* Use `0` (or `net.aton("0.0.0.0")`) to bind to all available interfaces. -* The `port` argument is also required. -* - `AF_PACKET`: An integer representing the ethernet protocol in host byte order -* (e.g., `0x0003` for `ETH_P_ALL`, `0x88CC` for `ETH_P_LLDP`) -* The `port` argument is also required. -* - Other families: A packed string directly representing parts of the family-specific address structure. -* -* @tparam[opt] integer port The local port or interface index. -* - `AF_INET`: TCP/UDP port number. -* - `AF_PACKET`: Network interface index (e.g., from `linux.ifindex("eth0")`). -* +* @tparam integer|string addr Local address to bind to (family-dependent). +* @tparam[opt] integer port Local port or interface index (family-dependent). * @treturn nil -* @raise Error if the bind operation fails (e.g., address already in use, invalid address). -* @usage -* -- Bind TCP/IPv4 socket to localhost, port 8080 -* tcp_server_sock:bind(net.aton("127.0.0.1"), 8080) -* -* -- Bind AF_PACKET socket to protocol `ETH_P_LLDP` on a specific interface -* af_packet_sock:bind(0x88CC, linux.ifindex("eth0")) +* @raise Error if the bind operation fails. * @see net.aton * @see linux.ifindex */ @@ -263,16 +216,11 @@ static int luasocket_bind(lua_State *L) /*** * Puts a connection-oriented socket into the listening state. -* This is required for server sockets (e.g., `SOCK_STREAM`) to be able to -* accept incoming connections. * * @function listen -* @tparam[opt] integer backlog The maximum length of the queue for pending connections. -* If omitted, a system-dependent default (e.g., `SOMAXCONN`) is used. +* @tparam[opt] integer backlog Maximum pending connection queue length (default: `SOMAXCONN`). * @treturn nil -* @raise Error if the listen operation fails (e.g., socket not bound, invalid state). -* @usage -* tcp_server_sock:listen(10) +* @raise Error if the listen operation fails. */ static int luasocket_listen(lua_State *L) { @@ -285,23 +233,13 @@ static int luasocket_listen(lua_State *L) /*** * Initiates a connection on a socket. -* This is typically used by client sockets to establish a connection to a server. -* For datagram sockets, this sets the default destination address for `send` and -* the only address from which datagrams are received. * * @function connect -* @tparam integer|string addr The destination address to connect to. -* Interpretation depends on `socket.sk.sk_family`: -* -* - `AF_INET` (IPv4): An integer representing the IPv4 address (e.g., from `net.aton()`). -* The `port` argument is also required. -* - Other families: A packed string representing the family-specific destination address. -* @tparam[opt] integer port The destination port number (required and used only if the family is `AF_INET`). -* @tparam[opt=0] integer flags Optional connection flags. +* @tparam integer|string addr Destination address (family-dependent). +* @tparam[opt] integer port Destination port (required for `AF_INET`). +* @tparam[opt=0] integer flags Connection flags. * @treturn nil -* @raise Error if the connect operation fails (e.g., connection refused, host unreachable). -* @usage -* tcp_client_sock:connect(net.aton("192.168.1.100"), 80) +* @raise Error if the connect operation fails. */ static int luasocket_connect(lua_State *L) { @@ -332,43 +270,25 @@ static int luasocket_get##what(lua_State *L) \ * Gets the local address to which the socket is bound. * * @function getsockname -* @treturn integer|string addr The local address. -* -* - For `AF_INET`: An integer representing the IPv4 address (can be converted with `net.ntoa()`). -* - For other families: A packed string representing the local address. -* @treturn[opt] integer port If the family is `AF_INET`, the local port number. +* @treturn integer|string addr Local address (family-dependent). +* @treturn[opt] integer port Local port for `AF_INET`. * @raise Error if the operation fails. -* @usage -* local local_ip_int, local_port = my_socket:getsockname() -* if my_socket.sk.sk_family == socket.af.INET then print("Bound to " .. net.ntoa(local_ip_int) .. ":" .. local_port) end */ LUASOCKET_NEWGETTER(sockname); /*** * Gets the address of the peer to which the socket is connected. -* This is typically used with connection-oriented sockets after a connection -* has been established, or with connectionless sockets after `connect()` has -* been called to set a default peer. * * @function getpeername -* @treturn integer|string addr The peer's address. -* -* - For `AF_INET`: An integer representing the IPv4 address (can be converted with `net.ntoa()`). -* - For other families: A packed string representing the peer's address. -* @treturn[opt] integer port If the family is `AF_INET`, the peer's port number. -* @raise Error if the operation fails (e.g., socket not connected). -* @usage -* local peer_ip_int, peer_port = connected_socket:getpeername() -* if connected_socket.sk.sk_family == socket.af.INET then print("Connected to " .. net.ntoa(peer_ip_int) .. ":" .. peer_port) end +* @treturn integer|string addr Peer address (family-dependent). +* @treturn[opt] integer port Peer port for `AF_INET`. +* @raise Error if the operation fails. */ LUASOCKET_NEWGETTER(peername); /*** -* Closes the socket. -* This shuts down the socket for both reading and writing and releases -* associated kernel resources. -* This method is also called automatically when the socket object is garbage collected -* or via Lua 5.4's to-be-closed mechanism. +* Closes the socket, shutting it down and releasing kernel resources. +* Also called on garbage collection or via Lua 5.4's to-be-closed mechanism. * * @function close * @treturn nil @@ -704,13 +624,9 @@ static const lunatik_class_t luasocket_class = { /*** * Accepts a connection on a listening socket. -* This function is used with connection-oriented sockets (e.g., `SOCK_STREAM`) -* that have been put into the listening state by `sock:listen()`. * * @function accept -* @tparam socket self The listening socket object. -* @tparam[opt=0] integer flags Optional flags to apply to the newly accepted socket -* (e.g., `socket.sock.NONBLOCK`, `socket.sock.CLOEXEC`). +* @tparam[opt=0] integer flags Flags for the new socket (e.g., `socket.sock.NONBLOCK`). * @treturn socket A new socket object representing the accepted connection. * @raise Error if the accept operation fails. */ @@ -726,19 +642,13 @@ static int luasocket_accept(lua_State *L) /*** * Creates a new socket object. -* This function is the primary way to create a socket. * * @function new -* @tparam integer family The address family for the socket (e.g., `socket.af.INET`). -* @tparam integer type The type of the socket (e.g., `socket.sock.STREAM`). -* @tparam integer protocol The protocol to be used (e.g., `socket.ipproto.TCP`). -* For `AF_PACKET` sockets, `protocol` is typically an `ETH_P_*` value in network byte order -* (e.g., `linux.hton16(0x0003)` for `ETH_P_ALL`). +* @tparam integer family Address family (e.g., `socket.af.INET`). +* @tparam integer type Socket type (e.g., `socket.sock.STREAM`). +* @tparam integer protocol Protocol (e.g., `socket.ipproto.TCP`). * @treturn socket A new socket object. * @raise Error if socket creation fails. -* @usage -* -- TCP/IPv4 socket -* local tcp_sock = socket.new(socket.af.INET, socket.sock.STREAM, socket.ipproto.TCP) * @see socket.af * @see socket.sock * @see socket.ipproto From cc65b818e2208d0e85e33b14e5c5aaf870ab32a5 Mon Sep 17 00:00:00 2001 From: Lourival Vieira Neto Date: Sun, 15 Mar 2026 20:47:48 +0000 Subject: [PATCH 4/4] fixup! socket: accept data object in send/receive to reuse buffers --- lunatik.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lunatik.h b/lunatik.h index b15c77b9b..9be8ea578 100644 --- a/lunatik.h +++ b/lunatik.h @@ -293,6 +293,13 @@ static inline lunatik_object_t **lunatik_testobject(lua_State *L, int ix) return (pobject && lunatik_isobject(L, ix, *pobject)) ? pobject : NULL; } +static inline lunatik_object_t **lunatik_checkpobject(lua_State *L, int ix) +{ + lunatik_object_t **pobject = lunatik_testobject(L, ix); + luaL_argcheck(L, pobject, ix, "invalid object"); + return pobject; +} + static inline lunatik_object_t *lunatik_checkclass(lua_State *L, int ix, const char *name, lunatik_opt_t opt) { @@ -302,13 +309,6 @@ static inline lunatik_object_t *lunatik_checkclass(lua_State *L, int ix, return object; } -static inline lunatik_object_t **lunatik_checkpobject(lua_State *L, int ix) -{ - lunatik_object_t **pobject = lunatik_testobject(L, ix); - luaL_argcheck(L, pobject, ix, "invalid object"); - return pobject; -} - static inline void lunatik_newnamespaces(lua_State *L, const lunatik_namespace_t *namespaces) { for (; namespaces->name; namespaces++) {