diff options
Diffstat (limited to 'libs/luci-lib-nixio/src/tls-socket.c')
-rw-r--r-- | libs/luci-lib-nixio/src/tls-socket.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/libs/luci-lib-nixio/src/tls-socket.c b/libs/luci-lib-nixio/src/tls-socket.c new file mode 100644 index 000000000..fe4cb6075 --- /dev/null +++ b/libs/luci-lib-nixio/src/tls-socket.c @@ -0,0 +1,263 @@ + /* + * nixio - Linux I/O library for lua + * + * Copyright (C) 2009 Steven Barth <steven@midlink.org> + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nixio-tls.h" +#include <string.h> +#include <stdlib.h> + +static int nixio__tls_sock_perror(lua_State *L, SSL *sock, int code) { + lua_pushnil(L); + lua_pushinteger(L, SSL_get_error(sock, code)); + return 2; +} + +static int nixio__tls_sock_pstatus(lua_State *L, SSL *sock, int code) { + if (code > 0) { + lua_pushboolean(L, 1); + return 1; + } else { + return nixio__tls_sock_perror(L, sock, code); + } +} + +static SSL* nixio__checktlssock(lua_State *L) { + if (lua_istable(L, 1)) { + lua_getfield(L, 1, "connection"); + lua_replace(L, 1); + } + nixio_tls_sock *sock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META); + luaL_argcheck(L, sock->socket, 1, "invalid context"); + return sock->socket; +} + +#ifndef WITH_AXTLS +#define nixio_tls__check_connected(L) ; + +#define nixio_tls__set_connected(L, val) ; +#else +#define nixio_tls__check_connected(L) \ + nixio_tls_sock *ctsock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META); \ + if (!ctsock->connected) { \ + lua_pushnil(L); \ + lua_pushinteger(L, 1); \ + return 2; \ + } + +#define nixio_tls__set_connected(L, val) \ +((nixio_tls_sock*)luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META))->connected = val; +#endif /* WITH_AXTLS */ + +static int nixio_tls_sock_recv(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + nixio_tls__check_connected(L); + uint req = luaL_checkinteger(L, 2); + + luaL_argcheck(L, req >= 0, 2, "out of range"); + + /* We limit the readsize to NIXIO_BUFFERSIZE */ + req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req; + +#ifndef WITH_AXTLS + + char buffer[NIXIO_BUFFERSIZE]; + int readc = SSL_read(sock, buffer, req); + + if (readc < 0) { + return nixio__tls_sock_pstatus(L, sock, readc); + } else { + lua_pushlstring(L, buffer, readc); + return 1; + } + +#else + + if (!req) { + lua_pushliteral(L, ""); + return 1; + } + + nixio_tls_sock *t = lua_touserdata(L, 1); + + /* AXTLS doesn't handle buffering for us, so we have to hack around*/ + if (req < t->pbufsiz) { + lua_pushlstring(L, t->pbufpos, req); + t->pbufpos += req; + t->pbufsiz -= req; + return 1; + } else { + uint8_t *axbuf; + int axread; + + /* while handshake pending */ + while ((axread = ssl_read(sock, &axbuf)) == SSL_OK); + + if (t->pbufsiz) { + lua_pushlstring(L, t->pbufpos, t->pbufsiz); + } + + if (axread < 0) { + /* There is an error */ + free(t->pbuffer); + t->pbuffer = t->pbufpos = NULL; + + if (axread != SSL_ERROR_CONN_LOST) { + t->pbufsiz = 0; + return nixio__tls_sock_perror(L, sock, axread); + } else { + if (!t->pbufsiz) { + lua_pushliteral(L, ""); + } else { + t->pbufsiz = 0; + } + } + } else { + int stillwant = req - t->pbufsiz; + if (stillwant < axread) { + /* we got more data than we need */ + lua_pushlstring(L, (char *)axbuf, stillwant); + if(t->pbufsiz) { + lua_concat(L, 2); + } + + /* remaining data goes into the buffer */ + t->pbufpos = t->pbuffer; + t->pbufsiz = axread - stillwant; + t->pbuffer = realloc(t->pbuffer, t->pbufsiz); + if (!t->pbuffer) { + free(t->pbufpos); + t->pbufpos = NULL; + t->pbufsiz = 0; + return luaL_error(L, "out of memory"); + } + + t->pbufpos = t->pbuffer; + memcpy(t->pbufpos, axbuf + stillwant, t->pbufsiz); + } else { + lua_pushlstring(L, (char *)axbuf, axread); + if(t->pbufsiz) { + lua_concat(L, 2); + } + + /* free buffer */ + free(t->pbuffer); + t->pbuffer = t->pbufpos = NULL; + t->pbufsiz = 0; + } + } + return 1; + } + +#endif /* WITH_AXTLS */ + +} + +static int nixio_tls_sock_send(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + nixio_tls__check_connected(L); + size_t len; + ssize_t sent; + const char *data = luaL_checklstring(L, 2, &len); + + if (lua_gettop(L) > 2) { + int offset = luaL_optint(L, 3, 0); + if (offset) { + if (offset < len) { + data += offset; + len -= offset; + } else { + len = 0; + } + } + + unsigned int wlen = luaL_optint(L, 4, len); + if (wlen < len) { + len = wlen; + } + } + + sent = SSL_write(sock, data, len); + if (sent > 0) { + lua_pushinteger(L, sent); + return 1; + } else { + return nixio__tls_sock_pstatus(L, sock, sent); + } +} + +static int nixio_tls_sock_accept(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + const int stat = SSL_accept(sock); + nixio_tls__set_connected(L, stat == 1); + return nixio__tls_sock_pstatus(L, sock, stat); +} + +static int nixio_tls_sock_connect(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + const int stat = SSL_connect(sock); + nixio_tls__set_connected(L, stat == 1); + return nixio__tls_sock_pstatus(L, sock, stat); +} + +static int nixio_tls_sock_shutdown(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + nixio_tls__set_connected(L, 0); + return nixio__tls_sock_pstatus(L, sock, SSL_shutdown(sock)); +} + +static int nixio_tls_sock__gc(lua_State *L) { + nixio_tls_sock *sock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META); + if (sock->socket) { + SSL_free(sock->socket); + sock->socket = NULL; +#ifdef WITH_AXTLS + free(sock->pbuffer); +#endif + } + return 0; +} + +static int nixio_tls_sock__tostring(lua_State *L) { + SSL *sock = nixio__checktlssock(L); + lua_pushfstring(L, "nixio TLS connection: %p", sock); + return 1; +} + + +/* ctx function table */ +static const luaL_reg M[] = { + {"recv", nixio_tls_sock_recv}, + {"send", nixio_tls_sock_send}, + {"read", nixio_tls_sock_recv}, + {"write", nixio_tls_sock_send}, + {"accept", nixio_tls_sock_accept}, + {"connect", nixio_tls_sock_connect}, + {"shutdown", nixio_tls_sock_shutdown}, + {"__gc", nixio_tls_sock__gc}, + {"__tostring", nixio_tls_sock__tostring}, + {NULL, NULL} +}; + + +void nixio_open_tls_socket(lua_State *L) { + /* create socket metatable */ + luaL_newmetatable(L, NIXIO_TLS_SOCK_META); + luaL_register(L, NULL, M); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_setfield(L, -2, "meta_tls_socket"); +} |