Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 267 additions & 0 deletions src/ngx_stream_lua_socket_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static int ngx_stream_lua_socket_tcp_bind(lua_State *L);
static int ngx_stream_lua_socket_tcp_connect(lua_State *L);
#if (NGX_STREAM_SSL)
static int ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L);
static int ngx_stream_lua_socket_tcp_serversslhandshake(lua_State *L);
#endif
static int ngx_stream_lua_socket_tcp_receive(lua_State *L);
static int ngx_stream_lua_socket_tcp_receiveany(lua_State *L);
Expand Down Expand Up @@ -181,6 +182,12 @@ static int ngx_stream_lua_ssl_handshake_retval_handler(
ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
lua_State *L);
static void ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c);
static int ngx_stream_lua_server_ssl_handshake_retval_handler(
ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u,
lua_State *L);
static void ngx_stream_lua_ssl_handshake_session_info(ngx_connection_t *c,
lua_State *L);
static void ngx_stream_lua_server_ssl_handshake_handler(ngx_connection_t *c);
static int ngx_stream_lua_ssl_free_session(lua_State *L);
#endif
static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c);
Expand Down Expand Up @@ -327,6 +334,13 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L)
lua_pushcfunction(L, ngx_stream_lua_socket_tcp_shutdown);
lua_setfield(L, -2, "shutdown");

#if (NGX_STREAM_SSL)

lua_pushcfunction(L, ngx_stream_lua_socket_tcp_serversslhandshake);
lua_setfield(L, -2, "serversslhandshake");

#endif

lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");

Expand Down Expand Up @@ -2036,6 +2050,259 @@ ngx_stream_lua_ssl_handshake_retval_handler(ngx_stream_lua_request_t *r,
return 1;
}


static int
ngx_stream_lua_socket_tcp_serversslhandshake(lua_State *L)
{
int n, top;
ngx_int_t rc;
ngx_connection_t *c;

ngx_stream_lua_request_t *r;
ngx_stream_lua_ctx_t *ctx;
ngx_stream_lua_co_ctx_t *coctx;
ngx_stream_lua_socket_tcp_upstream_t *u;
ngx_stream_ssl_srv_conf_t *sscf;

/* Lua function arguments: self */

n = lua_gettop(L);
if (n != 1) {
return luaL_error(L, "ngx.socket serversslhandshake: expecting 1 "
"argument (the object), but seen %d", n);
}

r = ngx_stream_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}

ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
"stream lua tcp socket server ssl handshake");

luaL_checktype(L, 1, LUA_TTABLE);

lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
u = lua_touserdata(L, -1);

if (u == NULL
|| u->peer.connection == NULL
|| u->read_closed
|| u->write_closed)
{
lua_pushnil(L);
lua_pushliteral(L, "closed");
return 2;
}

if (u->request != r) {
return luaL_error(L, "bad request");
}

ngx_stream_lua_socket_check_busy_connecting(r, u, L);
ngx_stream_lua_socket_check_busy_reading(r, u, L);
ngx_stream_lua_socket_check_busy_writing(r, u, L);

if (!u->raw_downstream && !u->body_downstream) {
lua_pushnil(L);
lua_pushliteral(L, "only supported for downstream socket");
return 2;
}

/* For downstream sockets, the connection is r->connection */
c = r->connection;

if (c->ssl && c->ssl->handshaked) {
/* SSL handshake already completed, return session info */
ngx_stream_lua_ssl_handshake_session_info(c, L);
return 1;
}

sscf = ngx_stream_get_module_srv_conf(r->session, ngx_stream_ssl_module);

if (sscf == NULL || sscf->ssl.ctx == NULL) {
lua_pushnil(L);
lua_pushliteral(L, "ssl not configured for this server");
return 2;
}

if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {
lua_pushnil(L);
lua_pushliteral(L, "failed to create ssl connection");
return 2;
}

ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module);
if (ctx == NULL) {
return luaL_error(L, "no ctx found");
}

coctx = ctx->cur_co_ctx;

c->sendfile = 0;

u->write_co_ctx = coctx;

rc = ngx_ssl_handshake(c);

dd("ngx_ssl_handshake returned %d", (int) rc);

if (rc == NGX_AGAIN) {
if (c->write->timer_set) {
ngx_del_timer(c->write);
}

ngx_add_timer(c->read, u->read_timeout);

u->conn_waiting = 1;
u->write_prepare_retvals = ngx_stream_lua_server_ssl_handshake_retval_handler;

ngx_stream_lua_cleanup_pending_operation(coctx);
coctx->cleanup = ngx_stream_lua_coctx_cleanup;
coctx->data = u;

c->ssl->handler = ngx_stream_lua_server_ssl_handshake_handler;

if (ctx->entered_content_phase) {
r->write_event_handler = ngx_stream_lua_content_wev_handler;

} else {
r->write_event_handler = ngx_stream_lua_core_run_phases;
}

return lua_yield(L, 0);
}

top = lua_gettop(L);
ngx_stream_lua_server_ssl_handshake_handler(c);
return lua_gettop(L) - top;
}


static void
ngx_stream_lua_server_ssl_handshake_handler(ngx_connection_t *c)
{
int waiting;
lua_State *L;
ngx_stream_lua_request_t *r;
ngx_stream_session_t *s;

ngx_stream_lua_ctx_t *ctx;
ngx_stream_lua_socket_tcp_upstream_t *u;

/* For downstream sockets, c->data points to the session. */
s = c->data;

/* Get the context from the session */
ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
if (ctx == NULL) {
return;
}

r = ctx->request;
/* For downstream sockets, u is stored in ctx->downstream */
u = ctx->downstream;

c->read->handler = ngx_stream_lua_request_handler;
c->write->handler = ngx_stream_lua_request_handler;

waiting = u->conn_waiting;

L = u->write_co_ctx->co;

if (c->read->timedout) {
lua_pushnil(L);
lua_pushliteral(L, "timeout");
goto failed;
}

if (c->read->timer_set) {
ngx_del_timer(c->read);
}

r = u->request;
if (r == NULL) {
return;
}

if (c->ssl->handshaked) {
if (waiting) {
ngx_stream_lua_socket_handle_conn_success(r, u);

} else {
(void) ngx_stream_lua_server_ssl_handshake_retval_handler(r, u, L);
}

return;
}

lua_pushnil(L);
lua_pushliteral(L, "handshake failed");

failed:

if (waiting) {
ngx_stream_lua_socket_handle_conn_error(r, u,
NGX_STREAM_LUA_SOCKET_FT_SSL);

} else {
(void) ngx_stream_lua_socket_write_error_retval_handler(r, u, L);
}
}


static void
ngx_stream_lua_ssl_handshake_session_info(ngx_connection_t *c, lua_State *L)
{
const char *protocol;
SSL_CIPHER *cipher;
const char *cipher_name;

/* Create a table with SSL session information */
lua_createtable(L, 0, 3);

/* Add protocol version */
protocol = SSL_get_version(c->ssl->connection);
if (protocol) {
lua_pushstring(L, protocol);
lua_setfield(L, -2, "protocol");
}

/* Add cipher name */
cipher = (SSL_CIPHER *) SSL_get_current_cipher(c->ssl->connection);
if (cipher) {
cipher_name = SSL_CIPHER_get_name(cipher);
if (cipher_name) {
lua_pushstring(L, cipher_name);
lua_setfield(L, -2, "cipher");
}
}

/* Add session reused flag */
lua_pushboolean(L, SSL_session_reused(c->ssl->connection));
lua_setfield(L, -2, "session_reused");
}


static int
ngx_stream_lua_server_ssl_handshake_retval_handler(ngx_stream_lua_request_t *r,
ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L)
{
ngx_connection_t *c;

/* Check if an error occurred during the handshake */
if (u->ft_type) {
return ngx_stream_lua_socket_conn_error_retval_handler(r, u, L);
}

/* For downstream sockets, the connection is r->connection */
c = r->connection;

ngx_stream_lua_ssl_handshake_session_info(c, L);

return 1;
}

#endif /* NGX_STREAM_SSL */


Expand Down
64 changes: 64 additions & 0 deletions t/165-serversslhandshake.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:

use Test::Nginx::Socket::Lua::Stream;

repeat_each(2);

plan tests => repeat_each() * 7;

#log_level 'warn';
log_level 'debug';

no_long_string();
#no_diff();

run_tests();

__DATA__

=== TEST 1: serversslhandshake without SSL configured should fail
--- stream_server_config
content_by_lua_block {
local sock, err = ngx.req.socket(true)
if not sock then
ngx.say("failed to get socket: ", err)
return
end

-- Consume the initial test line
local line, err = sock:receive()
if not line then
ngx.say("failed to receive: ", err)
return
end

ngx.say("method exists: ", type(sock.serversslhandshake) == "function")

local session, err = sock:serversslhandshake()
ngx.say("error: ", err or "unexpected success")
}

--- stream_request
test
--- stream_response
method exists: true
error: ssl not configured for this server
--- no_error_log
[alert]



=== TEST 2: serversslhandshake method doesn't exist on non-downstream socket
--- stream_server_config
content_by_lua_block {
local sock = ngx.socket.tcp()
ngx.say("method exists: ", type(sock.serversslhandshake) == "function")
}

--- stream_response
method exists: false
--- no_error_log
[error]
[alert]


Loading