4 Commits

Author SHA1 Message Date
c3176c5678 Add shutdown example 2025-05-08 15:51:38 -03:00
bccb1f74f0 Fix IO_DONE error, remove shutdown field 2025-05-08 15:50:30 -03:00
fff43542f9 Fix missing return 2025-05-07 08:32:34 -03:00
7cfb91d478 Add :shutdown() method for ending the TLS session before :close()
This allows applications to separate ending the TLS session from closing the
underlying socket. In particular, it enables us to avoid needing to set the
socket to blocking mode during close().
2025-02-06 15:23:27 +00:00
7 changed files with 195 additions and 6 deletions

View File

@ -6,7 +6,7 @@ LUACPATH ?= /usr/lib/lua/5.1
# Comment this lines if you will link with non-internal LuaSocket's help files
# and edit INCDIR and LIBDIR properly.
EXTRA = luasocket
DEFS = -DWITH_LUASOCKET -DOPENSSL_API_COMPAT=0x10101000L
DEFS = -DWITH_LUASOCKET
# Edit the lines below to inform new path, if necessary.
# Path below points to internal LuaSocket's help files.

View File

@ -46,7 +46,7 @@ build = {
modules = {
ssl = {
defines = {
"WITH_LUASOCKET", "LUASOCKET_DEBUG", "OPENSSL_API_COMPAT=0x10101000L"
"WITH_LUASOCKET", "LUASOCKET_DEBUG",
},
incdirs = {
"$(OPENSSL_INCDIR)", "src/", "src/luasocket",
@ -80,8 +80,7 @@ build = {
defines = {
"WIN32", "NDEBUG", "_WINDOWS", "_USRDLL", "LSEC_EXPORTS", "BUFFER_DEBUG", "LSEC_API=__declspec(dllexport)",
"WITH_LUASOCKET", "LUASOCKET_DEBUG",
"LUASEC_INET_NTOP", "WINVER=0x0501", "_WIN32_WINNT=0x0501", "NTDDI_VERSION=0x05010300",
"OPENSSL_API_COMPAT=0x10101000L"
"LUASEC_INET_NTOP", "WINVER=0x0501", "_WIN32_WINNT=0x0501", "NTDDI_VERSION=0x05010300"
},
libdirs = {
"$(OPENSSL_LIBDIR)",

View File

@ -0,0 +1,85 @@
--
-- Test the conn:shutdown() function
--
-- Public domain
--
local socket = require("socket")
local ssl = require("ssl")
local params = {
mode = "client",
protocol = "tlsv1_2",
key = "../certs/clientAkey.pem",
certificate = "../certs/clientA.pem",
cafile = "../certs/rootA.pem",
verify = {"peer", "fail_if_no_peer_cert"},
options = "all",
}
-- Wait until socket is ready (for reading or writing)
local function wait(peer)
-- What event blocked us?
local err = peer:want()
print("Want? ", err)
if err == "read" then
socket.select({peer}, nil)
elseif err == "write" then
socket.select(nil, {peer})
elseif err == "nothing" then
return
else
peer:close()
os.exit(1)
end
end
-- Send data
local function send(peer, data)
local offset = 1
while true do
local succ, msg, part = peer:send(data, offset)
if succ then break end
if part then
offset = offset + part
wait(peer)
end
end
end
-- Start the TCP connection
local peer = socket.tcp()
peer:setoption('tcp-nodelay', true)
assert(peer:connect("127.0.0.1", 8888))
peer = assert(ssl.wrap(peer, params))
local ctx = assert(ssl.newcontext(params))
peer:settimeout(0.3)
print("*** Handshake")
while true do
local succ, msg = peer:dohandshake()
if succ then break end
wait(peer)
end
print("*** Send data")
for i = 1, 10 do
send(peer, string.rep('1', 8192))
end
print("*** Shutdown")
while true do
local succ, msg = peer:shutdown()
if succ then break end
print(succ, msg)
if msg ~= "inprogress" then
wait(peer)
end
end
print("*** Done")
peer:close()

View File

@ -0,0 +1,47 @@
--
-- Public domain
--
local socket = require("socket")
local ssl = require("ssl")
local params = {
mode = "server",
protocol = "any",
key = "../certs/serverAkey.pem",
certificate = "../certs/serverA.pem",
cafile = "../certs/rootA.pem",
verify = {"peer", "fail_if_no_peer_cert"},
options = "all",
}
local ctx = assert(ssl.newcontext(params))
local server = socket.tcp()
server:setoption('reuseaddr', true)
assert(server:bind("127.0.0.1", 8888))
server:listen()
while true do
local peer = server:accept()
peer:setoption('tcp-nodelay', true)
print("*** New connection")
peer = assert( ssl.wrap(peer, ctx) )
print("*** Handshake")
assert( peer:dohandshake() )
print("*** Receive")
while true do
local str = peer:receive(1024)
if not str then break end
socket.sleep(0.1)
end
print("*** Done")
peer:close()
end
server:close()

View File

@ -146,7 +146,7 @@ int timeout_open(lua_State *L) {
#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
luaL_setfuncs(L, func, 0);
#else
luaL_register(L, NULL, func);
luaL_openlib(L, NULL, func, 0);
#endif
return 0;
}

View File

@ -82,7 +82,6 @@ int socket_close(void) {
\*-------------------------------------------------------------------------*/
void socket_destroy(p_socket ps) {
if (*ps != SOCKET_INVALID) {
socket_setblocking(ps);
close(*ps);
*ps = SOCKET_INVALID;
}

View File

@ -11,6 +11,9 @@
#if defined(WIN32)
#include <winsock2.h>
#define LSEC_ERR_INPROGRESS WSAEINPROGRESS
#else
#define LSEC_ERR_INPROGRESS EINPROGRESS
#endif
#include <openssl/ssl.h>
@ -152,6 +155,46 @@ static int handshake(p_ssl ssl)
return IO_UNKNOWN;
}
/**
* Perform the TLS/SSL shutdown
*/
static int low_shutdown(p_ssl ssl)
{
int err;
p_timeout tm = timeout_markstart(&ssl->tm);
if (ssl->state == LSEC_STATE_CLOSED)
return IO_CLOSED;
for ( ; ; ) {
ERR_clear_error();
err = SSL_shutdown(ssl->ssl);
if (err == 0) return LSEC_ERR_INPROGRESS;
if (err == 1) return IO_DONE;
ssl->error = SSL_get_error(ssl->ssl, err);
switch (ssl->error) {
case SSL_ERROR_WANT_READ:
err = socket_waitfd(&ssl->sock, WAITFD_R, tm);
if (err == IO_TIMEOUT) return LSEC_IO_SSL;
if (err != IO_DONE) return err;
break;
case SSL_ERROR_WANT_WRITE:
err = socket_waitfd(&ssl->sock, WAITFD_W, tm);
if (err == IO_TIMEOUT) return LSEC_IO_SSL;
if (err != IO_DONE) return err;
break;
case SSL_ERROR_SYSCALL:
if (ERR_peek_error()) {
ssl->error = SSL_ERROR_SSL;
return LSEC_IO_SSL;
}
if (err == 0) return IO_CLOSED;
return lsec_socket_error();
default:
return LSEC_IO_SSL;
}
}
return IO_UNKNOWN;
}
/**
* Send data
*/
@ -407,6 +450,21 @@ static int meth_handshake(lua_State *L)
return 2;
}
/**
* Lua shutdown function.
*/
static int meth_shutdown(lua_State *L) {
p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection");
int err = low_shutdown(ssl);
if (err == IO_DONE) {
lua_pushboolean(L, 1);
return 1;
}
lua_pushboolean(L, 0);
lua_pushstring(L, (err == LSEC_ERR_INPROGRESS) ? "inprogress" : ssl_ioerror((void*)ssl, err));
return 2;
}
/**
* Close the connection.
*/
@ -990,6 +1048,7 @@ static int meth_tlsa(lua_State *L)
*/
static luaL_Reg methods[] = {
{"close", meth_close},
{"shutdown", meth_shutdown},
{"getalpn", meth_getalpn},
{"getfd", meth_getfd},
{"getfinished", meth_getfinished},