From cc887b9b25571e9ce0afa0cd5e4a4a2cbf130d3a Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Tue, 4 Mar 2025 02:19:37 -0300 Subject: [wip] vendor in dependencies --- vendor/lua-bz2/LICENSE | 13 + vendor/lua-bz2/README | 27 + vendor/lua-bz2/bz2/ltn12.lua | 93 ++ vendor/lua-bz2/compat-5.3.c | 948 +++++++++++++++ vendor/lua-bz2/compat-5.3.h | 424 +++++++ vendor/lua-bz2/lbz.c | 52 + vendor/lua-bz2/lbz2_common.c | 57 + vendor/lua-bz2/lbz2_common.h | 25 + vendor/lua-bz2/lbz2_file_reader.c | 164 +++ vendor/lua-bz2/lbz2_file_reader.h | 25 + vendor/lua-bz2/lbz2_file_writer.c | 141 +++ vendor/lua-bz2/lbz2_file_writer.h | 25 + vendor/lua-bz2/lbz2_stream.c | 263 +++++ vendor/lua-bz2/lbz2_stream.h | 25 + vendor/lua-bz2/lua-bz2-0.2.1-1.rockspec | 48 + vendor/lua-zlib/.gitattributes | 1 + vendor/lua-zlib/README | 163 +++ vendor/lua-zlib/lua-zlib-1.1-0.rockspec | 42 + vendor/lua-zlib/lua_zlib.c | 1295 +++++++++++++++++++++ vendor/luafilesystem/.gitignore | 2 + vendor/luafilesystem/.travis.yml | 34 + vendor/luafilesystem/LICENSE | 21 + vendor/luafilesystem/README.md | 28 + vendor/luafilesystem/luafilesystem-scm-1.rockspec | 28 + vendor/luafilesystem/src/.gitignore | 2 + vendor/luafilesystem/src/lfs.c | 1182 +++++++++++++++++++ vendor/luafilesystem/src/lfs.def | 4 + vendor/luafilesystem/src/lfs.h | 35 + vendor/luasec/.gitignore | 3 + vendor/luasec/CHANGELOG | 257 ++++ vendor/luasec/INSTALL | 39 + vendor/luasec/LICENSE | 21 + vendor/luasec/README.md | 6 + vendor/luasec/luasec-1.3.2-1.rockspec | 105 ++ vendor/luasec/src/Makefile | 66 ++ vendor/luasec/src/compat.h | 63 + vendor/luasec/src/config.c | 108 ++ vendor/luasec/src/context.c | 1099 +++++++++++++++++ vendor/luasec/src/context.h | 47 + vendor/luasec/src/ec.c | 116 ++ vendor/luasec/src/ec.h | 22 + vendor/luasec/src/https.lua | 147 +++ vendor/luasec/src/luasocket/LICENSE | 21 + vendor/luasec/src/luasocket/Makefile | 26 + vendor/luasec/src/luasocket/buffer.c | 278 +++++ vendor/luasec/src/luasocket/buffer.h | 45 + vendor/luasec/src/luasocket/io.c | 30 + vendor/luasec/src/luasocket/io.h | 65 ++ vendor/luasec/src/luasocket/socket.h | 78 ++ vendor/luasec/src/luasocket/timeout.c | 220 ++++ vendor/luasec/src/luasocket/timeout.h | 28 + vendor/luasec/src/luasocket/usocket.c | 441 +++++++ vendor/luasec/src/luasocket/usocket.h | 70 ++ vendor/luasec/src/luasocket/wsocket.c | 429 +++++++ vendor/luasec/src/luasocket/wsocket.h | 38 + vendor/luasec/src/options.c | 185 +++ vendor/luasec/src/options.h | 22 + vendor/luasec/src/options.lua | 93 ++ vendor/luasec/src/ssl.c | 1092 +++++++++++++++++ vendor/luasec/src/ssl.h | 41 + vendor/luasec/src/ssl.lua | 313 +++++ vendor/luasec/src/x509.c | 750 ++++++++++++ vendor/luasec/src/x509.h | 31 + vendor/luasocket/.editorconfig | 23 + vendor/luasocket/.gitignore | 15 + vendor/luasocket/.luacheckrc | 31 + vendor/luasocket/CHANGELOG.md | 65 ++ vendor/luasocket/FIX | 28 + vendor/luasocket/LICENSE | 19 + vendor/luasocket/README.md | 12 + vendor/luasocket/TODO | 81 ++ vendor/luasocket/WISH | 22 + vendor/luasocket/logo.ps | 210 ++++ vendor/luasocket/luasocket-scm-3.rockspec | 135 +++ vendor/luasocket/luasocket.sln | 35 + vendor/luasocket/src/auxiliar.c | 154 +++ vendor/luasocket/src/auxiliar.h | 54 + vendor/luasocket/src/buffer.c | 273 +++++ vendor/luasocket/src/buffer.h | 52 + vendor/luasocket/src/compat.c | 39 + vendor/luasocket/src/compat.h | 22 + vendor/luasocket/src/except.c | 129 ++ vendor/luasocket/src/except.h | 46 + vendor/luasocket/src/ftp.lua | 329 ++++++ vendor/luasocket/src/headers.lua | 104 ++ vendor/luasocket/src/http.lua | 424 +++++++ vendor/luasocket/src/inet.c | 537 +++++++++ vendor/luasocket/src/inet.h | 56 + vendor/luasocket/src/io.c | 28 + vendor/luasocket/src/io.h | 70 ++ vendor/luasocket/src/ltn12.lua | 318 +++++ vendor/luasocket/src/luasocket.c | 104 ++ vendor/luasocket/src/luasocket.h | 36 + vendor/luasocket/src/makefile | 461 ++++++++ vendor/luasocket/src/mbox.lua | 93 ++ vendor/luasocket/src/mime.c | 852 ++++++++++++++ vendor/luasocket/src/mime.h | 22 + vendor/luasocket/src/mime.lua | 81 ++ vendor/luasocket/src/options.c | 480 ++++++++ vendor/luasocket/src/options.h | 113 ++ vendor/luasocket/src/pierror.h | 28 + vendor/luasocket/src/select.c | 214 ++++ vendor/luasocket/src/select.h | 23 + vendor/luasocket/src/serial.c | 171 +++ vendor/luasocket/src/smtp.lua | 256 ++++ vendor/luasocket/src/socket.h | 75 ++ vendor/luasocket/src/socket.lua | 149 +++ vendor/luasocket/src/tcp.c | 480 ++++++++ vendor/luasocket/src/tcp.h | 43 + vendor/luasocket/src/timeout.c | 226 ++++ vendor/luasocket/src/timeout.h | 40 + vendor/luasocket/src/tp.lua | 134 +++ vendor/luasocket/src/udp.c | 488 ++++++++ vendor/luasocket/src/udp.h | 39 + vendor/luasocket/src/unix.c | 69 ++ vendor/luasocket/src/unix.h | 26 + vendor/luasocket/src/unixdgram.c | 405 +++++++ vendor/luasocket/src/unixdgram.h | 28 + vendor/luasocket/src/unixstream.c | 355 ++++++ vendor/luasocket/src/unixstream.h | 29 + vendor/luasocket/src/url.lua | 331 ++++++ vendor/luasocket/src/usocket.c | 454 ++++++++ vendor/luasocket/src/usocket.h | 59 + vendor/luasocket/src/wsocket.c | 434 +++++++ vendor/luasocket/src/wsocket.h | 33 + vendor/md5/.gitignore | 4 + vendor/md5/README.md | 47 + vendor/md5/rockspec/md5-1.2-1.rockspec | 35 + vendor/md5/src/compat-5.2.c | 21 + vendor/md5/src/compat-5.2.h | 15 + vendor/md5/src/des56.c | 548 +++++++++ vendor/md5/src/des56.def | 5 + vendor/md5/src/des56.h | 77 ++ vendor/md5/src/ldes56.c | 152 +++ vendor/md5/src/ldes56.h | 1 + vendor/md5/src/md5.c | 262 +++++ vendor/md5/src/md5.def | 5 + vendor/md5/src/md5.h | 40 + vendor/md5/src/md5.lua | 26 + vendor/md5/src/md5lib.c | 200 ++++ 140 files changed, 23142 insertions(+) create mode 100644 vendor/lua-bz2/LICENSE create mode 100644 vendor/lua-bz2/README create mode 100644 vendor/lua-bz2/bz2/ltn12.lua create mode 100644 vendor/lua-bz2/compat-5.3.c create mode 100644 vendor/lua-bz2/compat-5.3.h create mode 100644 vendor/lua-bz2/lbz.c create mode 100644 vendor/lua-bz2/lbz2_common.c create mode 100644 vendor/lua-bz2/lbz2_common.h create mode 100644 vendor/lua-bz2/lbz2_file_reader.c create mode 100644 vendor/lua-bz2/lbz2_file_reader.h create mode 100644 vendor/lua-bz2/lbz2_file_writer.c create mode 100644 vendor/lua-bz2/lbz2_file_writer.h create mode 100644 vendor/lua-bz2/lbz2_stream.c create mode 100644 vendor/lua-bz2/lbz2_stream.h create mode 100644 vendor/lua-bz2/lua-bz2-0.2.1-1.rockspec create mode 100644 vendor/lua-zlib/.gitattributes create mode 100644 vendor/lua-zlib/README create mode 100644 vendor/lua-zlib/lua-zlib-1.1-0.rockspec create mode 100644 vendor/lua-zlib/lua_zlib.c create mode 100644 vendor/luafilesystem/.gitignore create mode 100644 vendor/luafilesystem/.travis.yml create mode 100644 vendor/luafilesystem/LICENSE create mode 100644 vendor/luafilesystem/README.md create mode 100644 vendor/luafilesystem/luafilesystem-scm-1.rockspec create mode 100644 vendor/luafilesystem/src/.gitignore create mode 100644 vendor/luafilesystem/src/lfs.c create mode 100644 vendor/luafilesystem/src/lfs.def create mode 100644 vendor/luafilesystem/src/lfs.h create mode 100644 vendor/luasec/.gitignore create mode 100644 vendor/luasec/CHANGELOG create mode 100644 vendor/luasec/INSTALL create mode 100644 vendor/luasec/LICENSE create mode 100644 vendor/luasec/README.md create mode 100644 vendor/luasec/luasec-1.3.2-1.rockspec create mode 100644 vendor/luasec/src/Makefile create mode 100644 vendor/luasec/src/compat.h create mode 100644 vendor/luasec/src/config.c create mode 100644 vendor/luasec/src/context.c create mode 100644 vendor/luasec/src/context.h create mode 100644 vendor/luasec/src/ec.c create mode 100644 vendor/luasec/src/ec.h create mode 100644 vendor/luasec/src/https.lua create mode 100644 vendor/luasec/src/luasocket/LICENSE create mode 100644 vendor/luasec/src/luasocket/Makefile create mode 100644 vendor/luasec/src/luasocket/buffer.c create mode 100644 vendor/luasec/src/luasocket/buffer.h create mode 100644 vendor/luasec/src/luasocket/io.c create mode 100644 vendor/luasec/src/luasocket/io.h create mode 100644 vendor/luasec/src/luasocket/socket.h create mode 100644 vendor/luasec/src/luasocket/timeout.c create mode 100644 vendor/luasec/src/luasocket/timeout.h create mode 100644 vendor/luasec/src/luasocket/usocket.c create mode 100644 vendor/luasec/src/luasocket/usocket.h create mode 100644 vendor/luasec/src/luasocket/wsocket.c create mode 100644 vendor/luasec/src/luasocket/wsocket.h create mode 100644 vendor/luasec/src/options.c create mode 100644 vendor/luasec/src/options.h create mode 100644 vendor/luasec/src/options.lua create mode 100644 vendor/luasec/src/ssl.c create mode 100644 vendor/luasec/src/ssl.h create mode 100644 vendor/luasec/src/ssl.lua create mode 100644 vendor/luasec/src/x509.c create mode 100644 vendor/luasec/src/x509.h create mode 100644 vendor/luasocket/.editorconfig create mode 100644 vendor/luasocket/.gitignore create mode 100644 vendor/luasocket/.luacheckrc create mode 100644 vendor/luasocket/CHANGELOG.md create mode 100644 vendor/luasocket/FIX create mode 100644 vendor/luasocket/LICENSE create mode 100644 vendor/luasocket/README.md create mode 100644 vendor/luasocket/TODO create mode 100644 vendor/luasocket/WISH create mode 100644 vendor/luasocket/logo.ps create mode 100644 vendor/luasocket/luasocket-scm-3.rockspec create mode 100644 vendor/luasocket/luasocket.sln create mode 100644 vendor/luasocket/src/auxiliar.c create mode 100644 vendor/luasocket/src/auxiliar.h create mode 100644 vendor/luasocket/src/buffer.c create mode 100644 vendor/luasocket/src/buffer.h create mode 100644 vendor/luasocket/src/compat.c create mode 100644 vendor/luasocket/src/compat.h create mode 100644 vendor/luasocket/src/except.c create mode 100644 vendor/luasocket/src/except.h create mode 100644 vendor/luasocket/src/ftp.lua create mode 100644 vendor/luasocket/src/headers.lua create mode 100644 vendor/luasocket/src/http.lua create mode 100755 vendor/luasocket/src/inet.c create mode 100644 vendor/luasocket/src/inet.h create mode 100644 vendor/luasocket/src/io.c create mode 100644 vendor/luasocket/src/io.h create mode 100644 vendor/luasocket/src/ltn12.lua create mode 100755 vendor/luasocket/src/luasocket.c create mode 100644 vendor/luasocket/src/luasocket.h create mode 100755 vendor/luasocket/src/makefile create mode 100644 vendor/luasocket/src/mbox.lua create mode 100755 vendor/luasocket/src/mime.c create mode 100644 vendor/luasocket/src/mime.h create mode 100644 vendor/luasocket/src/mime.lua create mode 100644 vendor/luasocket/src/options.c create mode 100644 vendor/luasocket/src/options.h create mode 100644 vendor/luasocket/src/pierror.h create mode 100644 vendor/luasocket/src/select.c create mode 100644 vendor/luasocket/src/select.h create mode 100644 vendor/luasocket/src/serial.c create mode 100644 vendor/luasocket/src/smtp.lua create mode 100755 vendor/luasocket/src/socket.h create mode 100644 vendor/luasocket/src/socket.lua create mode 100644 vendor/luasocket/src/tcp.c create mode 100644 vendor/luasocket/src/tcp.h create mode 100644 vendor/luasocket/src/timeout.c create mode 100644 vendor/luasocket/src/timeout.h create mode 100644 vendor/luasocket/src/tp.lua create mode 100755 vendor/luasocket/src/udp.c create mode 100644 vendor/luasocket/src/udp.h create mode 100644 vendor/luasocket/src/unix.c create mode 100644 vendor/luasocket/src/unix.h create mode 100644 vendor/luasocket/src/unixdgram.c create mode 100644 vendor/luasocket/src/unixdgram.h create mode 100644 vendor/luasocket/src/unixstream.c create mode 100644 vendor/luasocket/src/unixstream.h create mode 100644 vendor/luasocket/src/url.lua create mode 100644 vendor/luasocket/src/usocket.c create mode 100644 vendor/luasocket/src/usocket.h create mode 100755 vendor/luasocket/src/wsocket.c create mode 100644 vendor/luasocket/src/wsocket.h create mode 100644 vendor/md5/.gitignore create mode 100644 vendor/md5/README.md create mode 100644 vendor/md5/rockspec/md5-1.2-1.rockspec create mode 100644 vendor/md5/src/compat-5.2.c create mode 100644 vendor/md5/src/compat-5.2.h create mode 100755 vendor/md5/src/des56.c create mode 100644 vendor/md5/src/des56.def create mode 100755 vendor/md5/src/des56.h create mode 100644 vendor/md5/src/ldes56.c create mode 100755 vendor/md5/src/ldes56.h create mode 100755 vendor/md5/src/md5.c create mode 100644 vendor/md5/src/md5.def create mode 100755 vendor/md5/src/md5.h create mode 100644 vendor/md5/src/md5.lua create mode 100644 vendor/md5/src/md5lib.c diff --git a/vendor/lua-bz2/LICENSE b/vendor/lua-bz2/LICENSE new file mode 100644 index 00000000..cc1b14dc --- /dev/null +++ b/vendor/lua-bz2/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2008, Evan Klitzke + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/lua-bz2/README b/vendor/lua-bz2/README new file mode 100644 index 00000000..a5361ad6 --- /dev/null +++ b/vendor/lua-bz2/README @@ -0,0 +1,27 @@ +A Lua binding to Julian Seward's libbzip2. + +Current maintainer: harningt@gmail.com +Previous maintainer: evan@yelp.com + +This software is released under a BSD-style license (to be exact, the ISC +license). The full license can be found in the LICENSE file included with this +source code. + +Requirements: + * libbzip2 + * Lua >= 5.1 + +Tested Operating Systems: + * Mac OSX 10.5 + * Linux + * Ubuntu + * Gentoo + +Patches to make this work fully on Windows and other OSes are welcome. + +More information about Lua: http://www.lua.org/ +More information about bzip2: http://www.bzip.org/ + +Current Repository Details + Project : https://github.com/harningt/lua-bz2/tree + Public Git : git://github.com/harningt/lua-bz2.git diff --git a/vendor/lua-bz2/bz2/ltn12.lua b/vendor/lua-bz2/bz2/ltn12.lua new file mode 100644 index 00000000..d2da675d --- /dev/null +++ b/vendor/lua-bz2/bz2/ltn12.lua @@ -0,0 +1,93 @@ +local _M = {} +local bz2 = require("bz2") + +_M.filter = {} +_M.source = {} +_M.sink = {} + +local function buildProcessor(filter, err) + if not filter then + return nil, err + end + return function(chunk) + if not filter then + return nil, "closed" + end + -- Skip empty chunks due to unexpected 'flushing' + if chunk and #chunk == 0 then + return "" + end + -- On nil, update closes the stream out as ltn12 expects + local ret, err = filter:update(chunk) + if not chunk then + filter:close() + filter = nil + end + if not ret then + return nil, err + end + return ret + end + +end + +function _M.filter.compress(blockSize100k, verbosity, workFactor) + return buildProcessor(bz2.initCompress(blockSize100k, verbosity, workFactor)) +end + +function _M.filter.decompress(verbosity, small) + return buildProcessor(bz2.initDecompress(verbosity, small)) +end + +function _M.sink.file(name, blockSize100k, verbosity, workFactor) + local writer, err = bz2.openWrite(name, blockSize100k, verbosity, workFactor) + if not writer then + return nil, err + end + return function(data) + if not writer then + return nil, "closed" + end + if not data then + writer:close() + writer = nil + return + end + if #data == 0 then + return 1 + end + local ret, err = writer:write(data) + if not ret then + return nil, err + end + return 1 + end +end + +function _M.source.file(name, verbosity, small) + local reader, err = bz2.openRead(name, verbosity, small) + if not reader then + return nil, err + end + return function() + if not reader then + return + end + local ret, err = reader:read(true) + if ret and #ret == 0 then + reader:close() + reader = nil + return + end + if not ret then + return nil, err + end + if err then + reader:close() + reader = nil + end + return ret + end +end + +return _M diff --git a/vendor/lua-bz2/compat-5.3.c b/vendor/lua-bz2/compat-5.3.c new file mode 100644 index 00000000..42b0a4bb --- /dev/null +++ b/vendor/lua-bz2/compat-5.3.c @@ -0,0 +1,948 @@ +#include +#include +#include +#include +#include +#include +#include "compat-5.3.h" + +/* don't compile it again if it already is included via compat53.h */ +#ifndef COMPAT53_C_ +#define COMPAT53_C_ + + + +/* definitions for Lua 5.1 only */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + +#ifndef COMPAT53_FOPEN_NO_LOCK +# if defined(_MSC_VER) +# define COMPAT53_FOPEN_NO_LOCK 1 +# else /* otherwise */ +# define COMPAT53_FOPEN_NO_LOCK 0 +# endif /* VC++ only so far */ +#endif /* No-lock fopen_s usage if possible */ + +#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK +# include +#endif /* VC++ _fsopen for share-allowed file read */ + +#ifndef COMPAT53_HAVE_STRERROR_R +# if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ + (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \ + defined(__APPLE__) +# define COMPAT53_HAVE_STRERROR_R 1 +# else /* none of the defines matched: define to 0 */ +# define COMPAT53_HAVE_STRERROR_R 0 +# endif /* have strerror_r of some form */ +#endif /* strerror_r */ + +#ifndef COMPAT53_HAVE_STRERROR_S +# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ + defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__) +# define COMPAT53_HAVE_STRERROR_S 1 +# else /* not VC++ or C11 */ +# define COMPAT53_HAVE_STRERROR_S 0 +# endif /* strerror_s from VC++ or C11 */ +#endif /* strerror_s */ + +#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE +# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096 +#endif /* Lua File Buffer Size */ + + +static char* compat53_strerror (int en, char* buff, size_t sz) { +#if COMPAT53_HAVE_STRERROR_R + /* use strerror_r here, because it's available on these specific platforms */ + if (sz > 0) { + buff[0] = '\0'; + /* we don't care whether the GNU version or the XSI version is used: */ + if (strerror_r(en, buff, sz)) { + /* Yes, we really DO want to ignore the return value! + * GCC makes that extra hard, not even a (void) cast will do. */ + } + if (buff[0] == '\0') { + /* Buffer is unchanged, so we probably have called GNU strerror_r which + * returned a static constant string. Chances are that strerror will + * return the same static constant string and therefore be thread-safe. */ + return strerror(en); + } + } + return buff; /* sz is 0 *or* strerror_r wrote into the buffer */ +#elif COMPAT53_HAVE_STRERROR_S + /* for MSVC and other C11 implementations, use strerror_s since it's + * provided by default by the libraries */ + strerror_s(buff, sz, en); + return buff; +#else + /* fallback, but strerror is not guaranteed to be threadsafe due to modifying + * errno itself and some impls not locking a static buffer for it ... but most + * known systems have threadsafe errno: this might only change if the locale + * is changed out from under someone while this function is being called */ + (void)buff; + (void)sz; + return strerror(en); +#endif +} + + +COMPAT53_API int lua_absindex (lua_State *L, int i) { + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} + + +static void compat53_call_lua (lua_State *L, char const code[], size_t len, + int nargs, int nret) { + lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); + if (lua_type(L, -1) != LUA_TFUNCTION) { + lua_pop(L, 1); + if (luaL_loadbuffer(L, code, len, "=none")) + lua_error(L); + lua_pushvalue(L, -1); + lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); + } + lua_insert(L, -nargs-1); + lua_call(L, nargs, nret); +} + + +static const char compat53_arith_code[] = + "local op,a,b=...\n" + "if op==0 then return a+b\n" + "elseif op==1 then return a-b\n" + "elseif op==2 then return a*b\n" + "elseif op==3 then return a/b\n" + "elseif op==4 then return a%b\n" + "elseif op==5 then return a^b\n" + "elseif op==6 then return -a\n" + "end\n"; + +COMPAT53_API void lua_arith (lua_State *L, int op) { + if (op < LUA_OPADD || op > LUA_OPUNM) + luaL_error(L, "invalid 'op' argument for lua_arith"); + luaL_checkstack(L, 5, "not enough stack slots"); + if (op == LUA_OPUNM) + lua_pushvalue(L, -1); + lua_pushnumber(L, op); + lua_insert(L, -3); + compat53_call_lua(L, compat53_arith_code, + sizeof(compat53_arith_code)-1, 3, 1); +} + + +static const char compat53_compare_code[] = + "local a,b=...\n" + "return a<=b\n"; + +COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { + int result = 0; + switch (op) { + case LUA_OPEQ: + return lua_equal(L, idx1, idx2); + case LUA_OPLT: + return lua_lessthan(L, idx1, idx2); + case LUA_OPLE: + luaL_checkstack(L, 5, "not enough stack slots"); + idx1 = lua_absindex(L, idx1); + idx2 = lua_absindex(L, idx2); + lua_pushvalue(L, idx1); + lua_pushvalue(L, idx2); + compat53_call_lua(L, compat53_compare_code, + sizeof(compat53_compare_code)-1, 2, 1); + result = lua_toboolean(L, -1); + lua_pop(L, 1); + return result; + default: + luaL_error(L, "invalid 'op' argument for lua_compare"); + } + return 0; +} + + +COMPAT53_API void lua_copy (lua_State *L, int from, int to) { + int abs_to = lua_absindex(L, to); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushvalue(L, from); + lua_replace(L, abs_to); +} + + +COMPAT53_API void lua_len (lua_State *L, int i) { + switch (lua_type(L, i)) { + case LUA_TSTRING: + lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); + break; + case LUA_TTABLE: + if (!luaL_callmeta(L, i, "__len")) + lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); + break; + case LUA_TUSERDATA: + if (luaL_callmeta(L, i, "__len")) + break; + /* FALLTHROUGH */ + default: + luaL_error(L, "attempt to get length of a %s value", + lua_typename(L, lua_type(L, i))); + } +} + + +COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { + int abs_i = lua_absindex(L, i); + lua_pushlightuserdata(L, (void*)p); + lua_rawget(L, abs_i); + return lua_type(L, -1); +} + +COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushlightuserdata(L, (void*)p); + lua_insert(L, -2); + lua_rawset(L, abs_i); +} + + +COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { + lua_Number n = lua_tonumber(L, i); + if (isnum != NULL) { + *isnum = (n != 0 || lua_isnumber(L, i)); + } + return n; +} + + +COMPAT53_API void luaL_checkversion (lua_State *L) { + (void)L; +} + + +COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) { + if (!lua_checkstack(L, sp+LUA_MINSTACK)) { + if (msg != NULL) + luaL_error(L, "stack overflow (%s)", msg); + else { + lua_pushliteral(L, "stack overflow"); + lua_error(L); + } + } +} + + +COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 3, "not enough stack slots"); + lua_pushstring(L, name); + lua_gettable(L, abs_i); + if (lua_istable(L, -1)) + return 1; + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + lua_settable(L, abs_i); + return 0; +} + + +COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) { + lua_Integer res = 0; + int isnum = 0; + luaL_checkstack(L, 1, "not enough stack slots"); + lua_len(L, i); + res = lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + if (!isnum) + luaL_error(L, "object length is not an integer"); + return res; +} + + +COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup + 1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ + } + lua_pop(L, nup); /* remove upvalues */ +} + + +COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_checkstack(L, 1, "not enough stack slots"); + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { + void *p = lua_touserdata(L, i); + luaL_checkstack(L, 2, "not enough stack slots"); + if (p == NULL || !lua_getmetatable(L, i)) + return NULL; + else { + int res = 0; + luaL_getmetatable(L, tname); + res = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + if (!res) + p = NULL; + } + return p; +} + + +static int compat53_countlevels (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + +static int compat53_findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ + lua_remove(L, -2); /* remove table (but keep name) */ + lua_pushliteral(L, "."); + lua_insert(L, -2); /* place '.' between the two names */ + lua_concat(L, 3); + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + +static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + if (compat53_findfield(L, top + 1, 2)) { + lua_copy(L, -1, top + 1); /* move name to proper place */ + lua_pop(L, 2); /* remove pushed values */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + +static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { + if (*ar->namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, "function " LUA_QS, ar->name); + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what == 'C') { + if (compat53_pushglobalfuncname(L, ar)) { + lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else + lua_pushliteral(L, "?"); + } + else + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); +} + +#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ +#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ + +COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + lua_Debug ar; + int top = lua_gettop(L); + int numlevels = compat53_countlevels(L1); + int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; + if (msg) lua_pushfstring(L, "%s\n", msg); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level == mark) { /* too many levels? */ + lua_pushliteral(L, "\n\t..."); /* add a '...' */ + level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + lua_pushfstring(L, "\n\t%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + lua_pushliteral(L, " in "); + compat53_pushfuncname(L, &ar); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + + +COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { + const char *serr = NULL; + int en = errno; /* calls to Lua API may change this value */ + char buf[512] = { 0 }; + if (stat) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + serr = compat53_strerror(en, buf, sizeof(buf)); + if (fname) + lua_pushfstring(L, "%s: %s", fname, serr); + else + lua_pushstring(L, serr); + lua_pushnumber(L, (lua_Number)en); + return 3; + } +} + + +static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) { + if (mode && strchr(mode, modename[0]) == NULL) { + lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode); + return err; + } + return LUA_OK; +} + + +typedef struct { + lua_Reader reader; + void *ud; + int has_peeked_data; + const char *peeked_data; + size_t peeked_data_size; +} compat53_reader_data; + + +static const char *compat53_reader (lua_State *L, void *ud, size_t *size) { + compat53_reader_data *data = (compat53_reader_data *)ud; + if (data->has_peeked_data) { + data->has_peeked_data = 0; + *size = data->peeked_data_size; + return data->peeked_data; + } else + return data->reader(L, data->ud, size); +} + + +COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) { + int status = LUA_OK; + compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 }; + compat53_data.reader = reader; + compat53_data.ud = data; + compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size)); + if (compat53_data.peeked_data && compat53_data.peeked_data_size && + compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */ + status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); + else + status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); + if (status != LUA_OK) + return status; + /* we need to call the original 5.1 version of lua_load! */ +#undef lua_load + return lua_load(L, compat53_reader, &compat53_data, source); +#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) +} + + +typedef struct { + int n; /* number of pre-read characters */ + FILE *f; /* file being read */ + char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */ +} compat53_LoadF; + + +static const char *compat53_getF (lua_State *L, void *ud, size_t *size) { + compat53_LoadF *lf = (compat53_LoadF *)ud; + (void)L; /* not used */ + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } + else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'compat53_getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; +} + + +static int compat53_errfile (lua_State *L, const char *what, int fnameindex) { + char buf[512] = {0}; + const char *serr = compat53_strerror(errno, buf, sizeof(buf)); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +static int compat53_skipBOM (compat53_LoadF *lf) { + const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ + int c; + lf->n = 0; + do { + c = getc(lf->f); + if (c == EOF || c != *(const unsigned char *)p++) return c; + lf->buff[lf->n++] = (char)c; /* to be read by the parser */ + } while (*p != '\0'); + lf->n = 0; /* prefix matched; discard it */ + return getc(lf->f); /* return next character */ +} + + +/* +** reads the first character of file 'f' and skips an optional BOM mark +** in its beginning plus its first line if it starts with '#'. Returns +** true if it skipped the first line. In any case, '*cp' has the +** first "valid" character of the file (after the optional BOM and +** a first-line comment). +*/ +static int compat53_skipcomment (compat53_LoadF *lf, int *cp) { + int c = *cp = compat53_skipBOM(lf); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(lf->f); + } while (c != EOF && c != '\n'); + *cp = getc(lf->f); /* skip end-of-line, if present */ + return 1; /* there was a comment */ + } + else return 0; /* no comment */ +} + + +COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { + compat53_LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); +#if defined(_MSC_VER) + /* This code is here to stop a deprecation error that stops builds + * if a certain macro is defined. While normally not caring would + * be best, some header-only libraries and builds can't afford to + * dictate this to the user. A quick check shows that fopen_s this + * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET, + * possibly even before that so we don't need to do any version + * number checks, since this has been there since forever. */ + + /* TO USER: if you want the behavior of typical fopen_s/fopen, + * which does lock the file on VC++, define the macro used below to 0 */ +#if COMPAT53_FOPEN_NO_LOCK + lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */ + if (lf.f == NULL) + return compat53_errfile(L, "open", fnameindex); +#else /* use default locking version */ + if (fopen_s(&lf.f, filename, "r") != 0) + return compat53_errfile(L, "open", fnameindex); +#endif /* Locking vs. No-locking fopen variants */ +#else + lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */ + if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex); +#endif + } + if (compat53_skipcomment(&lf, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ +#if defined(_MSC_VER) + if (freopen_s(&lf.f, filename, "rb", lf.f) != 0) + return compat53_errfile(L, "reopen", fnameindex); +#else + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex); +#endif + compat53_skipcomment(&lf, &c); /* re-read initial portion */ + } + if (c != EOF) + lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */ + status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return compat53_errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { + int status = LUA_OK; + if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) { + status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); + } + else { + status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); + } + if (status != LUA_OK) + return status; + return luaL_loadbuffer(L, buff, sz, name); +} + + +#if !defined(l_inspectstat) && \ + (defined(unix) || defined(__unix) || defined(__unix__) || \ + defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ + (defined(__APPLE__) && defined(__MACH__))) +/* some form of unix; check feature macros in unistd.h for details */ +# include +/* check posix version; the relevant include files and macros probably + * were available before 2001, but I'm not sure */ +# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L +# include +# define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } +# endif +#endif + +/* provide default (no-op) version */ +#if !defined(l_inspectstat) +# define l_inspectstat(stat,what) ((void)0) +#endif + + +COMPAT53_API int luaL_execresult (lua_State *L, int stat) { + const char *what = "exit"; + if (stat == -1) + return luaL_fileresult(L, 0, NULL); + else { + l_inspectstat(stat, what); + if (*what == 'e' && stat == 0) + lua_pushboolean(L, 1); + else + lua_pushnil(L); + lua_pushstring(L, what); + lua_pushinteger(L, stat); + return 3; + } +} + + +COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { + /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ + B->b.p = NULL; + B->b.L = NULL; + B->b.lvl = 0; + /* reuse the buffer from the 5.1-style luaL_Buffer though! */ + B->ptr = B->b.buffer; + B->capacity = LUAL_BUFFERSIZE; + B->nelems = 0; + B->L2 = L; +} + + +COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { + if (B->capacity - B->nelems < s) { /* needs to grow */ + char* newptr = NULL; + size_t newcap = B->capacity * 2; + if (newcap - B->nelems < s) + newcap = B->nelems + s; + if (newcap < B->capacity) /* overflow */ + luaL_error(B->L2, "buffer too large"); + newptr = (char*)lua_newuserdata(B->L2, newcap); + memcpy(newptr, B->ptr, B->nelems); + if (B->ptr != B->b.buffer) + lua_replace(B->L2, -2); /* remove old buffer */ + B->ptr = newptr; + B->capacity = newcap; + } + return B->ptr+B->nelems; +} + + +COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { + memcpy(luaL_prepbuffsize(B, l), s, l); + luaL_addsize(B, l); +} + + +COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { + size_t len = 0; + const char *s = lua_tolstring(B->L2, -1, &len); + if (!s) + luaL_error(B->L2, "cannot convert value to string"); + if (B->ptr != B->b.buffer) + lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ + luaL_addlstring(B, s, len); + lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); +} + + +void luaL_pushresult (luaL_Buffer_53 *B) { + lua_pushlstring(B->L2, B->ptr, B->nelems); + if (B->ptr != B->b.buffer) + lua_replace(B->L2, -2); /* remove userdata buffer */ +} + + +#endif /* Lua 5.1 */ + + + +/* definitions for Lua 5.1 and Lua 5.2 */ +#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 + + +COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { + index = lua_absindex(L, index); + lua_pushinteger(L, i); + lua_gettable(L, index); + return lua_type(L, -1); +} + + +#ifndef LUA_EXTRASPACE +#define LUA_EXTRASPACE (sizeof(void*)) +#endif + +COMPAT53_API void *lua_getextraspace (lua_State *L) { + int is_main = 0; + void *ptr = NULL; + luaL_checkstack(L, 4, "not enough stack slots available"); + lua_pushliteral(L, "__compat53_extraspace"); + lua_pushvalue(L, -1); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + lua_replace(L, -2); + is_main = lua_pushthread(L); + lua_rawget(L, -2); + ptr = lua_touserdata(L, -1); + if (!ptr) { + lua_pop(L, 1); + ptr = lua_newuserdata(L, LUA_EXTRASPACE); + if (is_main) { + memset(ptr, '\0', LUA_EXTRASPACE); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + lua_pushboolean(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } else { + void* mptr = NULL; + lua_pushboolean(L, 1); + lua_rawget(L, -3); + mptr = lua_touserdata(L, -1); + if (mptr) + memcpy(ptr, mptr, LUA_EXTRASPACE); + else + memset(ptr, '\0', LUA_EXTRASPACE); + lua_pop(L, 1); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + } + lua_pop(L, 2); + return ptr; +} + + +COMPAT53_API int lua_isinteger (lua_State *L, int index) { + if (lua_type(L, index) == LUA_TNUMBER) { + lua_Number n = lua_tonumber(L, index); + lua_Integer i = lua_tointeger(L, index); + if (i == n) + return 1; + } + return 0; +} + + +COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { + int ok = 0; + lua_Number n = lua_tonumberx(L, i, &ok); + if (ok) { + if (n == (lua_Integer)n) { + if (isnum) + *isnum = 1; + return (lua_Integer)n; + } + } + if (isnum) + *isnum = 0; + return 0; +} + + +static void compat53_reverse (lua_State *L, int a, int b) { + for (; a < b; ++a, --b) { + lua_pushvalue(L, a); + lua_pushvalue(L, b); + lua_replace(L, a); + lua_replace(L, b); + } +} + + +COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { + int n_elems = 0; + idx = lua_absindex(L, idx); + n_elems = lua_gettop(L)-idx+1; + if (n < 0) + n += n_elems; + if ( n > 0 && n < n_elems) { + luaL_checkstack(L, 2, "not enough stack slots available"); + n = n_elems - n; + compat53_reverse(L, idx, idx+n-1); + compat53_reverse(L, idx+n, idx+n_elems-1); + compat53_reverse(L, idx, idx+n_elems-1); + } +} + + +COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { + luaL_checkstack(L, 1, "not enough stack slots available"); + index = lua_absindex(L, index); + lua_pushinteger(L, i); + lua_insert(L, -2); + lua_settable(L, index); +} + + +#if !defined(lua_str2number) +# define lua_str2number(s, p) strtod((s), (p)) +#endif + +COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { + char* endptr; + lua_Number n = lua_str2number(s, &endptr); + if (endptr != s) { + while (*endptr != '\0' && isspace((unsigned char)*endptr)) + ++endptr; + if (*endptr == '\0') { + lua_pushnumber(L, n); + return endptr - s + 1; + } + } + return 0; +} + + +COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + if (!luaL_callmeta(L, idx, "__tostring")) { + int t = lua_type(L, idx), tt = 0; + char const* name = NULL; + switch (t) { + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + case LUA_TSTRING: + case LUA_TNUMBER: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) + lua_pushliteral(L, "true"); + else + lua_pushliteral(L, "false"); + break; + default: + tt = luaL_getmetafield(L, idx, "__name"); + name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); + lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_replace(L, -2); + break; + } + } else { + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + return lua_tolstring(L, -1, len); +} + + +COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + luaL_checkstack(L, 3, "not enough stack slots available"); + luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); + if (lua_getfield(L, -1, modname) == LUA_TNIL) { + lua_pop(L, 1); + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); + lua_call(L, 1, 1); + lua_pushvalue(L, -1); + lua_setfield(L, -3, modname); + } + if (glb) { + lua_pushvalue(L, -1); + lua_setglobal(L, modname); + } + lua_replace(L, -2); +} + + +#endif /* Lua 5.1 and 5.2 */ + + +#endif /* COMPAT53_C_ */ + + +/********************************************************************* +* This file contains parts of Lua 5.2's and Lua 5.3's source code: +* +* Copyright (C) 1994-2014 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ + diff --git a/vendor/lua-bz2/compat-5.3.h b/vendor/lua-bz2/compat-5.3.h new file mode 100644 index 00000000..b730a4b8 --- /dev/null +++ b/vendor/lua-bz2/compat-5.3.h @@ -0,0 +1,424 @@ +#ifndef COMPAT53_H_ +#define COMPAT53_H_ + +#include +#include +#include +#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) +extern "C" { +#endif +#include +#include +#include +#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) +} +#endif + + +#undef COMPAT53_INCLUDE_SOURCE +#if defined(COMPAT53_PREFIX) +/* - change the symbol names of functions to avoid linker conflicts + * - compat-5.3.c needs to be compiled (and linked) separately + */ +# if !defined(COMPAT53_API) +# define COMPAT53_API extern +# endif +#else /* COMPAT53_PREFIX */ +/* - make all functions static and include the source. + * - compat-5.3.c doesn't need to be compiled (and linked) separately + */ +# define COMPAT53_PREFIX compat53 +# undef COMPAT53_API +# if defined(__GNUC__) || defined(__clang__) +# define COMPAT53_API __attribute__((__unused__)) static +# else +# define COMPAT53_API static +# endif +# define COMPAT53_INCLUDE_SOURCE +#endif /* COMPAT53_PREFIX */ + +#define COMPAT53_CONCAT_HELPER(a, b) a##b +#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) + + + +/* declarations for Lua 5.1 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + +/* XXX not implemented: + * lua_arith (new operators) + * lua_upvalueid + * lua_upvaluejoin + * lua_version + * lua_yieldk + */ + +#ifndef LUA_OK +# define LUA_OK 0 +#endif +#ifndef LUA_OPADD +# define LUA_OPADD 0 +#endif +#ifndef LUA_OPSUB +# define LUA_OPSUB 1 +#endif +#ifndef LUA_OPMUL +# define LUA_OPMUL 2 +#endif +#ifndef LUA_OPDIV +# define LUA_OPDIV 3 +#endif +#ifndef LUA_OPMOD +# define LUA_OPMOD 4 +#endif +#ifndef LUA_OPPOW +# define LUA_OPPOW 5 +#endif +#ifndef LUA_OPUNM +# define LUA_OPUNM 6 +#endif +#ifndef LUA_OPEQ +# define LUA_OPEQ 0 +#endif +#ifndef LUA_OPLT +# define LUA_OPLT 1 +#endif +#ifndef LUA_OPLE +# define LUA_OPLE 2 +#endif + +/* LuaJIT/Lua 5.1 does not have the updated + * error codes for thread status/function returns (but some patched versions do) + * define it only if it's not found + */ +#if !defined(LUA_ERRGCMM) +/* Use + 2 because in some versions of Lua (Lua 5.1) + * LUA_ERRFILE is defined as (LUA_ERRERR+1) + * so we need to avoid it (LuaJIT might have something at this + * integer value too) + */ +# define LUA_ERRGCMM (LUA_ERRERR + 2) +#endif /* LUA_ERRGCMM define */ + +typedef size_t lua_Unsigned; + +typedef struct luaL_Buffer_53 { + luaL_Buffer b; /* make incorrect code crash! */ + char *ptr; + size_t nelems; + size_t capacity; + lua_State *L2; +} luaL_Buffer_53; +#define luaL_Buffer luaL_Buffer_53 + +/* In PUC-Rio 5.1, userdata is a simple FILE* + * In LuaJIT, it's a struct where the first member is a FILE* + * We can't support the `closef` member + */ +typedef struct luaL_Stream { + FILE *f; +} luaL_Stream; + +#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) +COMPAT53_API int lua_absindex (lua_State *L, int i); + +#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) +COMPAT53_API void lua_arith (lua_State *L, int op); + +#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) +COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); + +#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) +COMPAT53_API void lua_copy (lua_State *L, int from, int to); + +#define lua_getuservalue(L, i) \ + (lua_getfenv((L), (i)), lua_type((L), -1)) +#define lua_setuservalue(L, i) \ + (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i))) + +#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) +COMPAT53_API void lua_len (lua_State *L, int i); + +#define lua_pushstring(L, s) \ + (lua_pushstring((L), (s)), lua_tostring((L), -1)) + +#define lua_pushlstring(L, s, len) \ + ((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1)) + +#ifndef luaL_newlibtable +# define luaL_newlibtable(L, l) \ + (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1)) +#endif +#ifndef luaL_newlib +# define luaL_newlib(L, l) \ + (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) +#endif + +#define lua_pushglobaltable(L) \ + lua_pushvalue((L), LUA_GLOBALSINDEX) + +#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) +COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); + +#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) +COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); + +#define lua_rawlen(L, i) lua_objlen((L), (i)) + +#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL) + +#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) +COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); + +#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) +COMPAT53_API void luaL_checkversion (lua_State *L); + +#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) +COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode); + +#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex) +COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode); + +#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx) +COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); + +#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53) +COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg); + +#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) +COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); + +#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) +COMPAT53_API lua_Integer luaL_len (lua_State *L, int i); + +#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) +COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); + +#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) +COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); + +#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) +COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); + +#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) +COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); + +#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) +COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); + +#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult) +COMPAT53_API int luaL_execresult (lua_State *L, int stat); + +#define lua_callk(L, na, nr, ctx, cont) \ + ((void)(ctx), (void)(cont), lua_call((L), (na), (nr))) +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err))) + +#define lua_resume(L, from, nargs) \ + ((void)(from), lua_resume((L), (nargs))) + +#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) +COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); + +#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) +COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); + +#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) +COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); + +#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) +COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); + +#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) +COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); + +#undef luaL_buffinitsize +#define luaL_buffinitsize(L, B, s) \ + (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s))) + +#undef luaL_prepbuffer +#define luaL_prepbuffer(B) \ + luaL_prepbuffsize((B), LUAL_BUFFERSIZE) + +#undef luaL_addchar +#define luaL_addchar(B, c) \ + ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \ + ((B)->ptr[(B)->nelems++] = (c))) + +#undef luaL_addsize +#define luaL_addsize(B, s) \ + ((B)->nelems += (s)) + +#undef luaL_addstring +#define luaL_addstring(B, s) \ + luaL_addlstring((B), (s), strlen((s))) + +#undef luaL_pushresultsize +#define luaL_pushresultsize(B, s) \ + (luaL_addsize((B), (s)), luaL_pushresult((B))) + +#if defined(LUA_COMPAT_APIINTCASTS) +#define lua_pushunsigned(L, n) \ + lua_pushinteger((L), (lua_Integer)(n)) +#define lua_tounsignedx(L, i, is) \ + ((lua_Unsigned)lua_tointegerx((L), (i), (is))) +#define lua_tounsigned(L, i) \ + lua_tounsignedx((L), (i), NULL) +#define luaL_checkunsigned(L, a) \ + ((lua_Unsigned)luaL_checkinteger((L), (a))) +#define luaL_optunsigned(L, a, d) \ + ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d))) +#endif + +#endif /* Lua 5.1 only */ + + + +/* declarations for Lua 5.1 and 5.2 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 + +typedef int lua_KContext; + +typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); + +#define lua_dump(L, w, d, s) \ + ((void)(s), lua_dump((L), (w), (d))) + +#define lua_getfield(L, i, k) \ + (lua_getfield((L), (i), (k)), lua_type((L), -1)) + +#define lua_gettable(L, i) \ + (lua_gettable((L), (i)), lua_type((L), -1)) + +#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) +COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); + +#define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace) +COMPAT53_API void *lua_getextraspace (lua_State *L); + +#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) +COMPAT53_API int lua_isinteger (lua_State *L, int index); + +#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53) +COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); + +#define lua_numbertointeger(n, p) \ + ((*(p) = (lua_Integer)(n)), 1) + +#define lua_rawget(L, i) \ + (lua_rawget((L), (i)), lua_type((L), -1)) + +#define lua_rawgeti(L, i, n) \ + (lua_rawgeti((L), (i), (n)), lua_type((L), -1)) + +#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) +COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); + +#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) +COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); + +#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) +COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); + +#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) +COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); + +#define luaL_getmetafield(L, o, e) \ + (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL) + +#define luaL_newmetatable(L, tn) \ + (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0) + +#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) +COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb ); + +#endif /* Lua 5.1 and Lua 5.2 */ + + + +/* declarations for Lua 5.2 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 + +/* XXX not implemented: + * lua_isyieldable + * lua_arith (new operators) + * lua_pushfstring (new formats) + */ + +#define lua_getglobal(L, n) \ + (lua_getglobal((L), (n)), lua_type((L), -1)) + +#define lua_getuservalue(L, i) \ + (lua_getuservalue((L), (i)), lua_type((L), -1)) + +#define lua_pushlstring(L, s, len) \ + (((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))) + +#define lua_rawgetp(L, i, p) \ + (lua_rawgetp((L), (i), (p)), lua_type((L), -1)) + +#define LUA_KFUNCTION(_name) \ + static int (_name)(lua_State *L, int status, lua_KContext ctx); \ + static int (_name ## _52)(lua_State *L) { \ + lua_KContext ctx; \ + int status = lua_getctx(L, &ctx); \ + return (_name)(L, status, ctx); \ + } \ + static int (_name)(lua_State *L, int status, lua_KContext ctx) + +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52) + +#define lua_callk(L, na, nr, ctx, cont) \ + lua_callk((L), (na), (nr), (ctx), cont ## _52) + +#define lua_yieldk(L, nr, ctx, cont) \ + lua_yieldk((L), (nr), (ctx), cont ## _52) + +#ifdef lua_call +# undef lua_call +# define lua_call(L, na, nr) \ + (lua_callk)((L), (na), (nr), 0, NULL) +#endif + +#ifdef lua_pcall +# undef lua_pcall +# define lua_pcall(L, na, nr, err) \ + (lua_pcallk)((L), (na), (nr), (err), 0, NULL) +#endif + +#ifdef lua_yield +# undef lua_yield +# define lua_yield(L, nr) \ + (lua_yieldk)((L), (nr), 0, NULL) +#endif + +#endif /* Lua 5.2 only */ + + + +/* other Lua versions */ +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504 + +# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)" + +#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */ + + + +/* helper macro for defining continuation functions (for every version + * *except* Lua 5.2) */ +#ifndef LUA_KFUNCTION +#define LUA_KFUNCTION(_name) \ + static int (_name)(lua_State *L, int status, lua_KContext ctx) +#endif + + +#if defined(COMPAT53_INCLUDE_SOURCE) +# include "compat-5.3.c" +#endif + + +#endif /* COMPAT53_H_ */ + diff --git a/vendor/lua-bz2/lbz.c b/vendor/lua-bz2/lbz.c new file mode 100644 index 00000000..6e0d66e2 --- /dev/null +++ b/vendor/lua-bz2/lbz.c @@ -0,0 +1,52 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* This explicit define prevents compat-5.3.h from loading compat-5.3.c */ +#define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + +#include "lbz2_file_reader.h" +#include "lbz2_file_writer.h" +#include "lbz2_stream.h" + +static luaL_Reg lbz2_global[] = { + { NULL, NULL } +}; + +int luaopen_bz2(lua_State *L) { + luaL_newlib(L, lbz2_global); + + lua_pushliteral(L, "bz2"); + lua_setfield(L, -2, "_NAME"); + lua_pushliteral(L, "0.1"); + lua_setfield(L, -2, "_VERSION"); + + register_lbz2_file_reader(L); + register_lbz2_file_writer(L); + register_lbz2_stream(L); + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + lua_pushvalue(L, -1); + lua_setglobal(L, "bz2"); +#endif + + return 1; +} diff --git a/vendor/lua-bz2/lbz2_common.c b/vendor/lua-bz2/lbz2_common.c new file mode 100644 index 00000000..2aa76cfb --- /dev/null +++ b/vendor/lua-bz2/lbz2_common.c @@ -0,0 +1,57 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include "lbz2_common.h" +#include + + +const char *lbz2_error(int bzerror) { + switch (bzerror) { + case BZ_OK: + return "OK"; + case BZ_RUN_OK: + return "RUN_OK"; + case BZ_FLUSH_OK: + return "FLUSH_OK"; + case BZ_FINISH_OK: + return "FINISH_OK"; + case BZ_STREAM_END: + return "STREAM_END"; + case BZ_SEQUENCE_ERROR: + return "SEQUENCE_ERROR"; + case BZ_PARAM_ERROR: + return "PARAM_ERROR"; + case BZ_MEM_ERROR: + return "MEM_ERROR"; + case BZ_DATA_ERROR: + return "DATA_ERROR"; + case BZ_DATA_ERROR_MAGIC: + return "DATA_ERROR_MAGIC"; + case BZ_IO_ERROR: + return "IO_ERROR"; + case BZ_UNEXPECTED_EOF: + return "UNEXPECTED_EOF"; + case BZ_OUTBUFF_FULL: + return "OUTBUFF_FULL"; + case BZ_CONFIG_ERROR: + return "CONFIG_ERROR"; + default: + return "UNKNOWN"; + } +} diff --git a/vendor/lua-bz2/lbz2_common.h b/vendor/lua-bz2/lbz2_common.h new file mode 100644 index 00000000..98e534bf --- /dev/null +++ b/vendor/lua-bz2/lbz2_common.h @@ -0,0 +1,25 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef LBZ2_COMMON_H +#define LBZ2_COMMON_H + +const char *lbz2_error(int bzerror); + +#endif diff --git a/vendor/lua-bz2/lbz2_file_reader.c b/vendor/lua-bz2/lbz2_file_reader.c new file mode 100644 index 00000000..c101a1ee --- /dev/null +++ b/vendor/lua-bz2/lbz2_file_reader.c @@ -0,0 +1,164 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +/* This explicit define prevents compat-5.3.h from loading compat-5.3.c */ +#define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + +#include "lbz2_file_reader.h" +#include "lbz2_common.h" + +#define LBZ2_FILE_READER_MT "LBZ2_FILE_READER_MT" + +typedef struct { + BZFILE *bz_stream; + FILE *f; +} lbz2_file_reader; + +static lbz2_file_reader *lbz2_check_file_reader(lua_State *L, int index) { + return (lbz2_file_reader *)luaL_checkudata(L, index, LBZ2_FILE_READER_MT); +} + +static int lbz2_file_reader_open(lua_State *L) { + lbz2_file_reader *reader; + int errorCode; + const char *fname = luaL_checkstring(L, 1); + int verbosity = luaL_optinteger(L, 3, 0); + int small = lua_toboolean(L, 4); + + reader = lua_newuserdata(L, sizeof(*reader)); + memset(reader, 0, sizeof(*reader)); + + luaL_getmetatable(L, LBZ2_FILE_READER_MT); + lua_setmetatable(L, -2); + + reader->f = fopen(fname, "rb"); + + if (reader->f == NULL) { + return luaL_error(L, "Failed to fopen %s", fname); + } + reader->bz_stream = BZ2_bzReadOpen(&errorCode, reader->f, verbosity, small, NULL, 0); + + if (BZ_OK != errorCode) { + fclose(reader->f); + reader->f = NULL; + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + return 1; +} + +static int lbz2_file_reader_close(lua_State *L) { + lbz2_file_reader *reader = lbz2_check_file_reader(L, 1); + int errorCode = BZ_OK; + + if (reader->bz_stream) { + BZ2_bzReadClose(&errorCode, reader->bz_stream); + reader->bz_stream = NULL; + } + if (reader->f) { + fclose(reader->f); + reader->f = NULL; + } + + lua_pushnil(L); + lua_setmetatable(L, 1); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +static int lbz2_file_reader_read(lua_State *L) { + lbz2_file_reader *reader = lbz2_check_file_reader(L, 1); + int errorCode = BZ_OK; + int dataLength; + luaL_Buffer B; + /* If passed a boolean, read a single *chunk* */ + if (lua_isboolean(L, 2)) { + dataLength = LUAL_BUFFERSIZE; + } else { + dataLength = luaL_optinteger(L, 2, -1); + } + + luaL_buffinit(L, &B); + + /* Pull in chunks until all data read */ + while(dataLength > 0 || dataLength == -1) { + char *buf = luaL_prepbuffer(&B); + int nextRead = (dataLength == -1 || dataLength > LUAL_BUFFERSIZE) ? LUAL_BUFFERSIZE : dataLength; + int read = BZ2_bzRead(&errorCode, reader->bz_stream, buf, nextRead); + if (read > 0) { + luaL_addsize(&B, read); + dataLength -= read; + } + if (BZ_OK != errorCode) { + goto handle_error; + } + } + luaL_pushresult(&B); + return 1; +handle_error: + if(BZ_STREAM_END == errorCode) { + luaL_pushresult(&B); + lua_pushboolean(L, 1); + return 2; + } else { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } +} + +static luaL_Reg lbz2_file_reader_ops[] = { + { "read", lbz2_file_reader_read }, + { "close", lbz2_file_reader_close }, + { NULL, NULL } +}; + +static luaL_Reg lbz2_file_reader_global[] = { + { "openRead", lbz2_file_reader_open }, + { NULL, NULL } +}; + + + +void register_lbz2_file_reader(lua_State *L) { + luaL_newmetatable(L, LBZ2_FILE_READER_MT); + lua_newtable(L); + luaL_setfuncs(L, lbz2_file_reader_ops, 0); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lbz2_file_reader_close); + lua_setfield(L, -2, "__gc"); + lua_pop(L, 1); + + luaL_setfuncs(L, lbz2_file_reader_global, 0); +} diff --git a/vendor/lua-bz2/lbz2_file_reader.h b/vendor/lua-bz2/lbz2_file_reader.h new file mode 100644 index 00000000..a183329d --- /dev/null +++ b/vendor/lua-bz2/lbz2_file_reader.h @@ -0,0 +1,25 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef LBZ2_FILE_READER_H +#define LBZ2_FILE_READER_H + +void register_lbz2_file_reader(lua_State *L); + +#endif diff --git a/vendor/lua-bz2/lbz2_file_writer.c b/vendor/lua-bz2/lbz2_file_writer.c new file mode 100644 index 00000000..f35beca3 --- /dev/null +++ b/vendor/lua-bz2/lbz2_file_writer.c @@ -0,0 +1,141 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* This explicit define prevents compat-5.3.h from loading compat-5.3.c */ +#define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + +#include "lbz2_file_writer.h" +#include "lbz2_common.h" + +#define LBZ2_FILE_WRITER_MT "LBZ2_FILE_WRITER_MT" + +typedef struct { + BZFILE *bz_stream; + FILE *f; +} lbz2_file_writer; + +static lbz2_file_writer *lbz2_check_file_writer(lua_State *L, int index) { + return (lbz2_file_writer *)luaL_checkudata(L, index, LBZ2_FILE_WRITER_MT); +} + +static int lbz2_file_writer_open(lua_State *L) { + lbz2_file_writer *writer; + int errorCode; + const char *fname = luaL_checkstring(L, 1); + int blockSize100k = luaL_optinteger(L, 2, 9); + int verbosity = luaL_optinteger(L, 3, 0); + int workFactor = luaL_optinteger(L, 4, 0); + + writer = lua_newuserdata(L, sizeof(*writer)); + memset(writer, 0, sizeof(*writer)); + + luaL_getmetatable(L, LBZ2_FILE_WRITER_MT); + lua_setmetatable(L, -2); + + writer->f = fopen(fname, "wb"); + + if (writer->f == NULL) { + return luaL_error(L, "Failed to fopen %s", fname); + } + writer->bz_stream = BZ2_bzWriteOpen(&errorCode, writer->f, blockSize100k, verbosity, workFactor); + + if (BZ_OK != errorCode) { + fclose(writer->f); + writer->f = NULL; + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + return 1; +} + +static int lbz2_file_writer_close(lua_State *L) { + lbz2_file_writer *writer = lbz2_check_file_writer(L, 1); + int errorCode = BZ_OK; + + if (writer->bz_stream) { + BZ2_bzWriteClose(&errorCode, writer->bz_stream, 0, NULL, NULL); + writer->bz_stream = NULL; + } + if (writer->f) { + fclose(writer->f); + writer->f = NULL; + } + + lua_pushnil(L); + lua_setmetatable(L, 1); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +static int lbz2_file_writer_write(lua_State *L) { + lbz2_file_writer *writer = lbz2_check_file_writer(L, 1); + int errorCode = BZ_OK; + size_t dataLength; + const char *data = luaL_checklstring(L, 2, &dataLength); + + BZ2_bzWrite(&errorCode, writer->bz_stream, (void *)data, dataLength); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +static luaL_Reg lbz2_file_writer_ops[] = { + { "write", lbz2_file_writer_write }, + { "close", lbz2_file_writer_close }, + { NULL, NULL } +}; + +static luaL_Reg lbz2_file_writer_global[] = { + { "openWrite", lbz2_file_writer_open }, + { NULL, NULL } +}; + +void register_lbz2_file_writer(lua_State *L) { + luaL_newmetatable(L, LBZ2_FILE_WRITER_MT); + lua_newtable(L); + luaL_setfuncs(L, lbz2_file_writer_ops, 0); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lbz2_file_writer_close); + lua_setfield(L, -2, "__gc"); + lua_pop(L, 1); + + luaL_setfuncs(L, lbz2_file_writer_global, 0); +} diff --git a/vendor/lua-bz2/lbz2_file_writer.h b/vendor/lua-bz2/lbz2_file_writer.h new file mode 100644 index 00000000..4f2e5e09 --- /dev/null +++ b/vendor/lua-bz2/lbz2_file_writer.h @@ -0,0 +1,25 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef LBZ2_FILE_WRITER_H +#define LBZ2_FILE_WRITER_H + +void register_lbz2_file_writer(lua_State *L); + +#endif diff --git a/vendor/lua-bz2/lbz2_stream.c b/vendor/lua-bz2/lbz2_stream.c new file mode 100644 index 00000000..9eb2ab2b --- /dev/null +++ b/vendor/lua-bz2/lbz2_stream.c @@ -0,0 +1,263 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* This explicit define prevents compat-5.3.h from loading compat-5.3.c */ +#define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + +#include "lbz2_stream.h" +#include "lbz2_common.h" + +#define LBZ2_STREAM_MT "LBZ2_STREAM_MT" + +typedef struct { + bz_stream bz_stream; + int isDecompressing; +} lbz2_stream; + +static lbz2_stream *lbz2_check_stream(lua_State *L, int index) { + return (lbz2_stream *)luaL_checkudata(L, index, LBZ2_STREAM_MT); +} + +static int lbz2_stream_initCompress(lua_State *L) { + lbz2_stream *stream; + int errorCode; + int blockSize100k = luaL_optinteger(L, 1, 9); + int verbosity = luaL_optinteger(L, 2, 0); + int workFactor = luaL_optinteger(L, 3, 0); + + stream = lua_newuserdata(L, sizeof(*stream)); + memset(stream, 0, sizeof(*stream)); + + stream->isDecompressing = 0; + + luaL_getmetatable(L, LBZ2_STREAM_MT); + lua_setmetatable(L, -2); + + errorCode = BZ2_bzCompressInit(&stream->bz_stream, blockSize100k, verbosity, workFactor); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + return 1; +} + +static int lbz2_stream_initDecompress(lua_State *L) { + lbz2_stream *stream; + int errorCode; + int verbosity = luaL_optinteger(L, 1, 0); + int isSmall = lua_toboolean(L, 2); + + stream = lua_newuserdata(L, sizeof(*stream)); + memset(stream, 0, sizeof(*stream)); + + stream->isDecompressing = 1; + + luaL_getmetatable(L, LBZ2_STREAM_MT); + lua_setmetatable(L, -2); + + errorCode = BZ2_bzDecompressInit(&stream->bz_stream, verbosity, isSmall); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + return 1; +} +static int lbz2_stream_close(lua_State *L) { + lbz2_stream *stream = lbz2_check_stream(L, 1); + int errorCode = BZ_OK; + + if (stream->bz_stream.state) { + if (stream->isDecompressing) { + errorCode = BZ2_bzDecompressEnd(&stream->bz_stream); + } else { + errorCode = BZ2_bzCompressEnd(&stream->bz_stream); + } + } + + lua_pushnil(L); + lua_setmetatable(L, 1); + + if (BZ_OK != errorCode) { + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +static int lbz2_stream_perform_compress(lua_State *L, lbz2_stream *stream, int action) { + int errorCode = BZ_OK; + luaL_Buffer B; + + luaL_buffinit(L, &B); + + while (1) { + stream->bz_stream.avail_out = LUAL_BUFFERSIZE; + stream->bz_stream.next_out = luaL_prepbuffer(&B); + errorCode = BZ2_bzCompress(&stream->bz_stream, action); + + switch (action) { + case BZ_RUN: + if (BZ_RUN_OK != errorCode) { + goto fail; + } + luaL_addsize(&B, LUAL_BUFFERSIZE - stream->bz_stream.avail_out); + if (stream->bz_stream.avail_in == 0 || stream->bz_stream.avail_out == LUAL_BUFFERSIZE) { + goto complete; + } + break; + case BZ_FLUSH: + if (BZ_FLUSH_OK != errorCode && BZ_RUN_OK != errorCode) { + goto fail; + } + luaL_addsize(&B, LUAL_BUFFERSIZE - stream->bz_stream.avail_out); + if (BZ_RUN_OK == errorCode) { + goto complete; + } + break; + case BZ_FINISH: + if (BZ_FINISH_OK != errorCode && BZ_STREAM_END != errorCode) { + goto fail; + } + luaL_addsize(&B, LUAL_BUFFERSIZE - stream->bz_stream.avail_out); + if (BZ_STREAM_END == errorCode) { + goto complete; + } + } + } +complete: + luaL_pushresult(&B); + return 1; + +fail: + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; +} + +static int lbz2_stream_perform_decompress(lua_State *L, lbz2_stream *stream) { + int errorCode = BZ_OK; + luaL_Buffer B; + + luaL_buffinit(L, &B); + + while (1) { + stream->bz_stream.avail_out = LUAL_BUFFERSIZE; + stream->bz_stream.next_out = luaL_prepbuffer(&B); + errorCode = BZ2_bzDecompress(&stream->bz_stream); + + if (BZ_OK != errorCode && BZ_STREAM_END != errorCode) { + goto fail; + } + luaL_addsize(&B, LUAL_BUFFERSIZE - stream->bz_stream.avail_out); + /* Stream over with */ + if (errorCode == BZ_STREAM_END) { + goto completeStream; + } + /* No more bytes left this round */ + if (stream->bz_stream.avail_in == 0 && stream->bz_stream.avail_out == LUAL_BUFFERSIZE) { + goto complete; + } + } +complete: + luaL_pushresult(&B); + return 1; + +completeStream: + luaL_pushresult(&B); + /* Report in addition to the data collected, the number of trailing bytes + * still available in the input buffer for other use. */ + lua_pushinteger(L, stream->bz_stream.avail_in); + return 2; + +fail: + lua_pushnil(L); + lua_pushstring(L, lbz2_error(errorCode)); + return 2; +} +static int lbz2_stream_update(lua_State *L) { + lbz2_stream *stream = lbz2_check_stream(L, 1); + size_t dataLength; + const char *data = luaL_optlstring(L, 2, NULL, &dataLength); + + /* Update the pointers and feed the output buffer while data is available */ + stream->bz_stream.avail_in = dataLength; + /* Cast away const-ness since input data is never altered */ + stream->bz_stream.next_in = (char *)data; + + /* For compression, need to specially flag finishing state */ + if (!stream->isDecompressing) { + return lbz2_stream_perform_compress(L, stream, !data ? BZ_FINISH : BZ_RUN); + } else { + return lbz2_stream_perform_decompress(L, stream); + } +} + +static int lbz2_stream_flush(lua_State *L) { + lbz2_stream *stream = lbz2_check_stream(L, 1); + if (!stream->isDecompressing) { + return lbz2_stream_perform_compress(L, stream, BZ_FLUSH); + } else { + /* Invalid for decompression */ + lua_pushnil(L); + lua_pushstring(L, lbz2_error(BZ_SEQUENCE_ERROR)); + return 2; + } +} + +static luaL_Reg lbz2_stream_ops[] = { + { "update", lbz2_stream_update }, + { "flush", lbz2_stream_flush }, + { "close", lbz2_stream_close }, + { NULL, NULL } +}; + +static luaL_Reg lbz2_stream_global[] = { + { "initCompress", lbz2_stream_initCompress }, + { "initDecompress", lbz2_stream_initDecompress }, + { NULL, NULL } +}; + +void register_lbz2_stream(lua_State *L) { + luaL_newmetatable(L, LBZ2_STREAM_MT); + lua_newtable(L); + luaL_setfuncs(L, lbz2_stream_ops, 0); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lbz2_stream_close); + lua_setfield(L, -2, "__gc"); + lua_pop(L, 1); + + luaL_setfuncs(L, lbz2_stream_global, 0); +} + diff --git a/vendor/lua-bz2/lbz2_stream.h b/vendor/lua-bz2/lbz2_stream.h new file mode 100644 index 00000000..a321ea59 --- /dev/null +++ b/vendor/lua-bz2/lbz2_stream.h @@ -0,0 +1,25 @@ +/* This file implements the Lua binding to libbzip2. + * + * Copyright (c) 2008, Evan Klitzke + * Copyright (c) 2012, Thomas Harning Jr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef LBZ2_STREAM_H +#define LBZ2_STREAM_H + +void register_lbz2_stream(lua_State *L); + +#endif diff --git a/vendor/lua-bz2/lua-bz2-0.2.1-1.rockspec b/vendor/lua-bz2/lua-bz2-0.2.1-1.rockspec new file mode 100644 index 00000000..c66770f6 --- /dev/null +++ b/vendor/lua-bz2/lua-bz2-0.2.1-1.rockspec @@ -0,0 +1,48 @@ +package = "lua-bz2" +version = "0.2.1-1" +source = { + url = "git+ssh://git@github.com/hishamhm/lua-bz2.git", + tag = "0.2.1" +} +description = { + summary = "A Lua binding to Julian Seward's libbzip2", + detailed = [[ + Support for reading and writing .bz2 files + and handling streams compressed in bzip2 format. + ]], + homepage = "https://github.com/hishamhm/lua-bz2", + license = "ISC" +} +external_dependencies = { + BZ2 = { + library = "bz2" + } +} +build = { + type = "builtin", + modules = { + bz2 = { + defines = { + "COMPAT53_PREFIX=compat53" + }, + incdirs = { + "$(BZ2_INCDIR)" + }, + libdirs = { + "$(BZ2_LIBDIR)" + }, + libraries = { + "bz2" + }, + sources = { + "lbz.c", + "lbz2_common.c", + "lbz2_file_reader.c", + "lbz2_file_writer.c", + "lbz2_stream.c", + "compat-5.3.c" + } + }, + ["bz2.ltn12"] = "bz2/ltn12.lua" + } +} diff --git a/vendor/lua-zlib/.gitattributes b/vendor/lua-zlib/.gitattributes new file mode 100644 index 00000000..54be6288 --- /dev/null +++ b/vendor/lua-zlib/.gitattributes @@ -0,0 +1 @@ +lua_zlib.c export-subst ident diff --git a/vendor/lua-zlib/README b/vendor/lua-zlib/README new file mode 100644 index 00000000..c438ee05 --- /dev/null +++ b/vendor/lua-zlib/README @@ -0,0 +1,163 @@ +********************************************************************** +* Author : Brian Maher +* Library : lua_zlib - Lua 5.1 interface to zlib +* +* The MIT License +* +* Copyright (c) 2009 Brian Maher +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +********************************************************************** + +To use this library, you need zlib, get it here: + http://www.gzip.org/zlib/ + +To build this library, you can use CMake and get it here: + http://www.cmake.org/cmake/resources/software.html + +...or you can use GNU Make. + make + +Loading the library: + + If you built the library as a loadable package + [local] zlib = require 'zlib' + + If you compiled the package statically into your application, call + the function "luaopen_zlib(L)". It will create a table with the zlib + functions and leave it on the stack. + +-- zlib functions -- + +int major, int minor, int patch = zlib.version() + + returns numeric zlib version for the major, minor, and patch + levels of the version dynamically linked in. + +function stream = zlib.deflate([ int compression_level ], [ int window_size ]) + + If no compression_level is provided uses Z_DEFAULT_COMPRESSION (6), + compression level is a number from 1-9 where zlib.BEST_SPEED is 1 + and zlib.BEST_COMPRESSION is 9. + + Returns a "stream" function that compresses (or deflates) all + strings passed in. Specifically, use it as such: + + string deflated, bool eof, int bytes_in, int bytes_out = + stream(string input [, 'sync' | 'full' | 'finish']) + + Takes input and deflates and returns a portion of it, + optionally forcing a flush. + + A 'sync' flush will force all pending output to be flushed to + the return value and the output is aligned on a byte boundary, + so that the decompressor can get all input data available so + far. Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + A 'full' flush will flush all output as with 'sync', and the + compression state is reset so that decompression can restart + from this point if previous compressed data has been damaged + or if random access is desired. Using Z_FULL_FLUSH too often + can seriously degrade the compression. + + A 'finish' flush will force all pending output to be processed + and results in the stream become unusable. Any future + attempts to print anything other than the empty string will + result in an error that begins with IllegalState. + + The eof result is true if 'finish' was specified, otherwise + it is false. + + The bytes_in is how many bytes of input have been passed to + stream, and bytes_out is the number of bytes returned in + deflated string chunks. + +function stream = zlib.inflate([int windowBits]) + + Returns a "stream" function that decompresses (or inflates) all + strings passed in. Optionally specify a windowBits argument + that is passed to inflateInit2(), see zlib.h for details about + this argument. By default, gzip header detection is done, and + the max window size is used. + + The "stream" function should be used as such: + + string inflated, bool eof, int bytes_in, int bytes_out = + stream(string input) + + Takes input and inflates and returns a portion of it. If it + detects the end of a deflation stream, then total will be the + total number of bytes read from input and all future calls to + stream() with a non empty string will result in an error that + begins with IllegalState. + + No flush options are provided since the maximal amount of + input is always processed. + + eof will be true when the input string is determined to be at + the "end of the file". + + The bytes_in is how many bytes of input have been passed to + stream, and bytes_out is the number of bytes returned in + inflated string chunks. + + +function compute_checksum = zlib.adler32() +function compute_checksum = zlib.crc32() + + Create a new checksum computation function using either the + adler32 or crc32 algorithms. This resulting function should be + used as such: + + int checksum = compute_checksum(string input | + function compute_checksum) + + The compute_checksum function takes as input either a string + that is logically getting appended to or another + compute_checksum function that is logically getting appended. + The result is the updated checksum. + + For example, these uses will all result in the same checksum: + + -- All in one call: + local csum = zlib.crc32()("one two") + + -- Multiple calls: + local compute = zlib.crc32() + compute("one") + assert(csum == compute(" two")) + + -- Multiple compute_checksums joined: + local compute1, compute2 = zlib.crc32(), zlib.crc32() + compute1("one") + compute2(" two") + assert(csum == compute1(compute2)) + +NOTE: This library ships with an "lzlib" compatibility shim. However, the +following things are not compatible: + + * zlib.version() in lzlib returns a string, but this library returns a + numeric tuple (see above). + + * zlib.{adler,crc}32() in lzlib returns the {adler,crc}32 initial value, + however if this value is used with calls to adler32 it works in + compatibility mode. + +To use this shim add the -DLZLIB_COMPAT compiler flag. \ No newline at end of file diff --git a/vendor/lua-zlib/lua-zlib-1.1-0.rockspec b/vendor/lua-zlib/lua-zlib-1.1-0.rockspec new file mode 100644 index 00000000..7927d6ce --- /dev/null +++ b/vendor/lua-zlib/lua-zlib-1.1-0.rockspec @@ -0,0 +1,42 @@ +package = "lua-zlib" +version = "1.2-0" +source = { + url = "git://github.com/brimworks/lua-zlib.git", + tag = "v1.2", +} +description = { + summary = "Simple streaming interface to zlib for Lua.", + detailed = [[ + Simple streaming interface to zlib for Lua. + Consists of two functions: inflate and deflate. + Both functions return "stream functions" (takes a buffer of input and returns a buffer of output). + This project is hosted on github. + ]], + homepage = "https://github.com/brimworks/lua-zlib", + license = "MIT" +} +dependencies = { + "lua >= 5.1, <= 5.3" +} +external_dependencies = { + ZLIB = { + header = "zlib.h" + } +} + +build = { + type = "builtin", + modules = { + zlib = { + sources = { "lua_zlib.c" }, + libraries = { "z" }, + defines = { "LZLIB_COMPAT" }, + incdirs = { "$(ZLIB_INCDIR)" }, + } + }, + platforms = { + windows = { modules = { zlib = { libraries = { + "$(ZLIB_LIBDIR)/zlib" -- Must full path to `"zlib"`, or else will cause the `LINK : fatal error LNK1149` + } } } } + } +} diff --git a/vendor/lua-zlib/lua_zlib.c b/vendor/lua-zlib/lua_zlib.c new file mode 100644 index 00000000..da57e91a --- /dev/null +++ b/vendor/lua-zlib/lua_zlib.c @@ -0,0 +1,1295 @@ +#include +#include +#include +#include +#include +#include + +/* + * ** compatibility with Lua 5.2 + * */ +#if (LUA_VERSION_NUM >= 502) +#undef luaL_register +#define luaL_register(L,n,f) \ + { if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); } + +#endif + +#if (LUA_VERSION_NUM >= 503) +#undef luaL_optint +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L,(n),(d))) +#endif + +#ifdef LZLIB_COMPAT +/**************** lzlib compatibilty **********************************/ +/* Taken from https://raw.githubusercontent.com/LuaDist/lzlib/93b88e931ffa7cd0a52a972b6b26d37628f479f3/lzlib.c */ + +/************************************************************************ +* Author : Tiago Dionizio * +* Library : lzlib - Lua 5 interface to access zlib library functions * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the * +* "Software"), to deal in the Software without restriction, including * +* without limitation the rights to use, copy, modify, merge, publish, * +* distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to * +* the following conditions: * +* * +* The above copyright notice and this permission notice shall be * +* included in all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +************************************************************************/ + + +/* +** ========================================================================= +** compile time options wich determine available functionality +** ========================================================================= +*/ + +/* TODO + +- also call flush on table/userdata when flush function is detected +- remove io_cb check inflate_block if condition +- only set eos when ZSTREAM_END is reached +- check for stream errors to close stream when really needed + +*/ + + +/* +** ========================================================================= +** zlib stream metamethods +** ========================================================================= +*/ +#define ZSTREAMMETA "zlib:zstream" + +#define LZ_ANY -1 +#define LZ_NONE 0 +#define LZ_DEFLATE 1 +#define LZ_INFLATE 2 + +#if 0 + #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE +#else + #define LZ_BUFFER_SIZE 8192 +#endif + +typedef struct { + /* zlib structures */ + z_stream zstream; + /* stream state. LZ_DEFLATE | LZ_INFLATE */ + int state; + int error; + int peek; + int eos; + /* user callback source for reading/writing */ + int io_cb; + /* input buffer */ + int i_buffer_ref; + size_t i_buffer_pos; + size_t i_buffer_len; + const char *i_buffer; + /* output buffer */ + size_t o_buffer_len; + size_t o_buffer_max; + char o_buffer[LZ_BUFFER_SIZE]; + /* dictionary */ + const Bytef *dictionary; + size_t dictionary_len; +} lz_stream; + + +/* forward declarations */ +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); + + +static lz_stream *lzstream_new(lua_State *L, int src) { + lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); + + luaL_getmetatable(L, ZSTREAMMETA); + lua_setmetatable(L, -2); /* set metatable */ + + s->state = LZ_NONE; + s->error = Z_OK; + s->eos = 0; + s->io_cb = LUA_REFNIL; + + s->i_buffer = NULL; + s->i_buffer_ref = LUA_REFNIL; + s->i_buffer_pos = 0; + s->i_buffer_len = 0; + + s->peek = 0; + s->o_buffer_len = 0; + s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + + /* prepare source */ + if (lua_isstring(L, src)) { + lua_pushvalue(L, src); + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); + } else { + /* table | function | userdata */ + lua_pushvalue(L, src); + s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + return s; +} + +static void lzstream_cleanup(lua_State *L, lz_stream *s) { + if (s && s->state != LZ_NONE) { + if (s->state == LZ_INFLATE) { + inflateEnd(&s->zstream); + } + if (s->state == LZ_DEFLATE) { + deflateEnd(&s->zstream); + } + + luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->state = LZ_NONE; + } +} + +/* ====================================================================== */ + +static lz_stream *lzstream_get(lua_State *L, int index) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); + return s; +} + +static lz_stream *lzstream_check(lua_State *L, int index, int state) { + lz_stream *s = lzstream_get(L, index); + if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { + luaL_argerror(L, index, "attempt to use invalid zlib stream"); + } + return s; +} + +/* ====================================================================== */ + +static int lzstream_tostring(lua_State *L) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); + + if (s->state == LZ_NONE) { + lua_pushstring(L, "zlib stream (closed)"); + } else if (s->state == LZ_DEFLATE) { + lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); + } else if (s->state == LZ_INFLATE) { + lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); + } else { + lua_pushfstring(L, "%p", (void*)s); + } + + return 1; +} + +/* ====================================================================== */ + +static int lzstream_gc(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + lzstream_cleanup(L, s); + return 0; +} + +/* ====================================================================== */ + +static int lzstream_close(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + + if (s->state == LZ_DEFLATE) { + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, Z_FINISH); + } + + lzstream_cleanup(L, s); + lua_pushboolean(L, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_adler(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_ANY); + lua_pushnumber(L, s->zstream.adler); + return 1; +} + +/* ====================================================================== */ + +/* + zlib.deflate( + sink: function | { write: function [, close: function, flush: function] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary: [""] + ) +*/ +static int lzlib_deflate(lua_State *L) { + int level, method, windowBits, memLevel, strategy; + lz_stream *s; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :write function? */ + lua_getfield(L, 1, "write"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "output parameter does not provide :write function"); + } + lua_pop(L, 1); + } + else if (!lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); + } + + level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + windowBits = (int) luaL_optinteger(L, 4, 15); + memLevel = (int) luaL_optinteger(L, 5, 8); + strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { + lua_pushliteral(L, "call to deflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to deflateSetDictionnary failed"); + lua_error(L); + } + } + + s->state = LZ_DEFLATE; + return 1; +} + +/* + zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary: [""] + ) +*/ +static int lzlib_inflate(lua_State *L) +{ + int windowBits; + lz_stream *s; + int have_peek = 0; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :read function? */ + lua_getfield(L, 1, "read"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "input parameter does not provide :read function"); + } + lua_pop(L, 1); + /* check for peek function */ + lua_getfield(L, 1, "peek"); + have_peek = lua_isfunction(L, -1); + lua_pop(L, 1); + } + else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); + } + + windowBits = (int) luaL_optinteger(L, 2, 15); + dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (windowBits > 0 && windowBits < 16) { + windowBits |= 32; + } + + if (inflateInit2(&s->zstream, windowBits) != Z_OK) { + lua_pushliteral(L, "call to inflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + s->dictionary = (const Bytef *) dictionary; + s->dictionary_len = dictionary_len; + } + + s->peek = have_peek; + s->state = LZ_INFLATE; + return 1; +} + +/* ====================================================================== */ + +static int lz_pushresult (lua_State *L, lz_stream *s) { + if (s->error == Z_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, zError(s->error)); + lua_pushinteger(L, s->error); + return 3; + } +} + +/* + Get block to process: + - top of stack gets +*/ +static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { + if (s->i_buffer_pos >= s->i_buffer_len) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isnil(L, -1)) { + if (lua_isfunction(L, -1)) { + lua_pushinteger(L, hint); + lua_call(L, 1, 1); + } else { + lua_getfield(L, -1, (s->peek ? "peek" : "read")); + lua_insert(L, -2); + lua_pushinteger(L, hint); + lua_call(L, 2, 1); + } + + if (lua_isstring(L, -1)) { + s->i_buffer_pos = 0; + s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); + if (s->i_buffer_len > 0) { + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } else { + lua_pushliteral(L, "deflate callback must return string or nil"); + lua_error(L); + } + } else { + lua_pop(L, 1); + } + } + + return s->i_buffer; +} + +static int lzstream_inflate_block(lua_State *L, lz_stream *s) { + if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { + int r; + + if (s->i_buffer_len == s->i_buffer_pos) { + s->zstream.next_in = NULL; + s->zstream.avail_in = 0; + } else { + s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); + s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; + } + + s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; + s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; + + /* munch some more */ + r = inflate(&s->zstream, Z_SYNC_FLUSH); + + if (r == Z_NEED_DICT) { + if (s->dictionary == NULL) { + lua_pushliteral(L, "no inflate dictionary provided"); + lua_error(L); + } + + if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to inflateSetDictionnary failed"); + lua_error(L); + } + + r = inflate(&s->zstream, Z_SYNC_FLUSH); + } + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + s->error = r; + #if 1 + lua_pushfstring(L, "failed to decompress [%d]", r); + lua_error(L); + #endif + } + + if (r == Z_STREAM_END) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + s->eos = 1; + } + + /* number of processed bytes */ + if (s->peek) { + size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + lua_getfield(L, -1, "read"); + lua_insert(L, -2); + lua_pushinteger(L, processed); + lua_call(L, 2, 0); + } + + s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; + s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; + } + + return s->o_buffer_len; +} + +/* +** Remove n bytes from the output buffer. +*/ +static void lzstream_remove(lz_stream *s, size_t n) { + memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); + s->o_buffer_len -= n; +} + +/* +** Copy at most n bytes to buffer b and remove them from the +** output stream buffer. +*/ +static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { + /* check output */ + if (n > s->o_buffer_len) { + n = s->o_buffer_len; + } + + if (n > 0) { + lua_pushlstring(L, s->o_buffer, n); + luaL_addvalue(b); + + lzstream_remove(s, n); + } + + return n; +} + +/* + z:read( + {number | '*l' | '*a'}* + ) +*/ +static int lz_test_eof(lua_State *L, lz_stream *s) { + lua_pushlstring(L, NULL, 0); + if (s->o_buffer_len > 0) { + return 1; + } else if (s->eos) { + return 0; + } else { + return lzstream_inflate_block(L, s); + } +} + +static int lz_read_line(lua_State *L, lz_stream *s) { + luaL_Buffer b; + size_t l = 0, n; + + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + char *p = s->o_buffer; + size_t len = s->o_buffer_len; + + /* find newline in output buffer */ + for (n = 0; n < len; ++n, ++p) { + if (*p == '\n' || *p == '\r') { + int eat_nl = *p == '\r'; + luaL_addlstring(&b, s->o_buffer, n); + lzstream_remove(s, n+1); + l += n; + + if (eat_nl && lzstream_inflate_block(L, s)) { + if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { + lzstream_remove(s, 1); + } + } + + luaL_pushresult(&b); + return 1; + } + } + + if (len > 0) { + luaL_addlstring(&b, s->o_buffer, len); + lzstream_remove(s, len); + l += len; + } + } while (lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + return l > 0 || !s->eos || s->o_buffer_len > 0; +} + + +static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { + size_t len; + luaL_Buffer b; + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + size_t rlen = lzstream_flush_buffer(L, s, n, &b); + n -= rlen; + } while (n > 0 && lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + lua_tolstring(L, -1, &len); + return n == 0 || len > 0; +} + +static int lzstream_decompress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = lz_read_line(L, s); + n = 3; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = 2; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'l': /* line */ + success = lz_read_line(L, s); + break; + case 'a': /* file */ + lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - 2; +} + + +static int lzstream_readline(lua_State *L) { + lz_stream *s; + int sucess; + + s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); + sucess = lz_read_line(L, s); + + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + + if (sucess) { + return 1; + } else { + /* EOF */ + return 0; + } +} + +static int lzstream_lines(lua_State *L) { + lzstream_check(L, 1, LZ_INFLATE); + lua_settop(L, 1); + lua_pushcclosure(L, lzstream_readline, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { + int r, arg; + int self = 0; + size_t b_size = s->o_buffer_max; + unsigned char *b = (unsigned char *)s->o_buffer; + + /* number of processed bytes */ + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isfunction(L, -1)) { + self = 1; + lua_getfield(L, -1, "write"); + } + + for (arg = from; arg <= to; arg++) { + s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); + + do { + s->zstream.next_out = b; + s->zstream.avail_out = b_size; + + /* bake some more */ + r = deflate(&s->zstream, flush); + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + lua_pushboolean(L, 0); + lua_pushfstring(L, "failed to compress [%d]", r); + return 2; + } + + if (s->zstream.avail_out != b_size) { + /* write output */ + lua_pushvalue(L, -1); /* function */ + if (self) lua_pushvalue(L, -3); /* self */ + lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ + lua_call(L, (self ? 2 : 1), 0); + } + + if (r == Z_STREAM_END) { + lzstream_cleanup(L, s); + break; + } + + /* process all input */ + } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); + } + + lua_pushboolean(L, 1); + return 1; +} + +static int lzstream_compress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); +} + + +/* ====================================================================== */ + +static int lzstream_flush(lua_State *L) { + static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; + static const char *const flush_opts[] = { "sync", "full", "finish" }; + + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); + + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, flush_values[flush]); +} + +/* ====================================================================== */ + +static int lzlib_compress(lua_State *L) { + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + int windowBits = (int) luaL_optinteger(L, 4, 15); + int memLevel = (int) luaL_optinteger(L, 5, 8); + int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy); + + if (ret != Z_OK) + { + lua_pushnil(L); + lua_pushnumber(L, ret); + return 2; + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for(;;) + { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* munch some more */ + ret = deflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + /* error condition? */ + if (ret != Z_OK) + break; + } + + /* cleanup */ + deflateEnd(&zs); + + luaL_pushresult(&b); + lua_pushnumber(L, ret); + return 2; +} + +/* ====================================================================== */ + +static int lzlib_decompress(lua_State *L) +{ + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int windowBits = (int) luaL_optinteger(L, 2, 15); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = inflateInit2(&zs, windowBits); + + if (ret != Z_OK) { + lua_pushliteral(L, "failed to initialize zstream structures"); + lua_error(L); + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for (;;) { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* bake some more */ + ret = inflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + /* cleanup */ + inflateEnd(&zs); + + lua_pushliteral(L, "failed to process zlib stream"); + lua_error(L); + } + } + + /* cleanup */ + inflateEnd(&zs); + + luaL_pushresult(&b); + return 1; +} + +#endif +/**********************************************************************/ + +#define DEF_MEM_LEVEL 8 + +typedef uLong (*checksum_t) (uLong crc, const Bytef *buf, uInt len); +typedef uLong (*checksum_combine_t)(uLong crc1, uLong crc2, z_off_t len2); + + +static int lz_deflate(lua_State *L); +static int lz_deflate_delete(lua_State *L); +static int lz_inflate_delete(lua_State *L); +static int lz_inflate(lua_State *L); +static int lz_checksum(lua_State *L); +static int lz_checksum_new(lua_State *L, checksum_t checksum, checksum_combine_t combine); +static int lz_adler32(lua_State *L); +static int lz_crc32(lua_State *L); + +static int lz_version(lua_State *L) { + const char* version = zlibVersion(); + int count = strlen(version) + 1; + char* cur = (char*)memcpy(lua_newuserdata(L, count), + version, count); + + count = 0; + while ( *cur ) { + char* begin = cur; + /* Find all digits: */ + while ( isdigit(*cur) ) cur++; + if ( begin != cur ) { + int is_end = *cur == '\0'; + *cur = '\0'; + lua_pushnumber(L, atoi(begin)); + count++; + if ( is_end ) break; + cur++; + } + while ( *cur && ! isdigit(*cur) ) cur++; + } + + return count; +} + +static int lz_assert(lua_State *L, int result, const z_stream* stream, const char* file, int line) { + /* Both of these are "normal" return codes: */ + if ( result == Z_OK || result == Z_STREAM_END ) return result; + switch ( result ) { + case Z_NEED_DICT: + lua_pushfstring(L, "RequiresDictionary: input stream requires a dictionary to be deflated (%s) at %s line %d", + stream->msg, file, line); + break; + case Z_STREAM_ERROR: + lua_pushfstring(L, "InternalError: inconsistent internal zlib stream (%s) at %s line %d", + stream->msg, file, line); + break; + case Z_DATA_ERROR: + lua_pushfstring(L, "InvalidInput: input string does not conform to zlib format or checksum failed at %s line %d", + file, line); + break; + case Z_MEM_ERROR: + lua_pushfstring(L, "OutOfMemory: not enough memory (%s) at %s line %d", + stream->msg, file, line); + break; + case Z_BUF_ERROR: + lua_pushfstring(L, "InternalError: no progress possible (%s) at %s line %d", + stream->msg, file, line); + break; + case Z_VERSION_ERROR: + lua_pushfstring(L, "IncompatibleLibrary: built with version %s, but dynamically linked with version %s (%s) at %s line %d", + ZLIB_VERSION, zlibVersion(), stream->msg, file, line); + break; + default: + lua_pushfstring(L, "ZLibError: unknown code %d (%s) at %s line %d", + result, stream->msg, file, line); + } + lua_error(L); + return result; +} + +/** + * @upvalue z_stream - Memory for the z_stream. + * @upvalue remainder - Any remainder from the last deflate call. + * + * @param string - "print" to deflate stream. + * @param int - flush output buffer? Z_SYNC_FLUSH, Z_FULL_FLUSH, or Z_FINISH. + * + * if no params, terminates the stream (as if we got empty string and Z_FINISH). + */ +static int lz_filter_impl(lua_State *L, int (*filter)(z_streamp, int), int (*end)(z_streamp), char* name) { + int flush = Z_NO_FLUSH, result; + z_stream* stream; + luaL_Buffer buff; + size_t avail_in; + + if ( filter == deflate ) { + const char *const opts[] = { "none", "sync", "full", "finish", NULL }; + flush = luaL_checkoption(L, 2, opts[0], opts); + if ( flush ) flush++; + /* Z_NO_FLUSH(0) Z_SYNC_FLUSH(2), Z_FULL_FLUSH(3), Z_FINISH (4) */ + + /* No arguments or nil, we are terminating the stream: */ + if ( lua_gettop(L) == 0 || lua_isnil(L, 1) ) { + flush = Z_FINISH; + } + } + + stream = (z_stream*)lua_touserdata(L, lua_upvalueindex(1)); + if ( stream == NULL ) { + if ( lua_gettop(L) >= 1 && lua_isstring(L, 1) ) { + lua_pushfstring(L, "IllegalState: calling %s function when stream was previously closed", name); + lua_error(L); + } + lua_pushstring(L, ""); + lua_pushboolean(L, 1); + return 2; /* Ignore duplicate calls to "close". */ + } + + luaL_buffinit(L, &buff); + + if ( lua_gettop(L) > 1 ) lua_pushvalue(L, 1); + + if ( lua_isstring(L, lua_upvalueindex(2)) ) { + lua_pushvalue(L, lua_upvalueindex(2)); + if ( lua_gettop(L) > 1 && lua_isstring(L, -2) ) { + lua_concat(L, 2); + } + } + + /* Do the actual deflate'ing: */ + if (lua_gettop(L) > 0) { + stream->next_in = (unsigned char*)lua_tolstring(L, -1, &avail_in); + } else { + stream->next_in = NULL; + avail_in = 0; + } + stream->avail_in = avail_in; + + if ( ! stream->avail_in && ! flush ) { + /* Passed empty string, make it a noop instead of erroring out. */ + lua_pushstring(L, ""); + lua_pushboolean(L, 0); + lua_pushinteger(L, stream->total_in); + lua_pushinteger(L, stream->total_out); + return 4; + } + + do { + stream->next_out = (unsigned char*)luaL_prepbuffer(&buff); + stream->avail_out = LUAL_BUFFERSIZE; + result = filter(stream, flush); + if ( Z_BUF_ERROR != result ) { + /* Ignore Z_BUF_ERROR since that just indicates that we + * need a larger buffer in order to proceed. Thanks to + * Tobias Markmann for finding this bug! + */ + lz_assert(L, result, stream, __FILE__, __LINE__); + } + luaL_addsize(&buff, LUAL_BUFFERSIZE - stream->avail_out); + } while ( stream->avail_out == 0 ); + + /* Need to do this before we alter the stack: */ + luaL_pushresult(&buff); + + /* Save remainder in lua_upvalueindex(2): */ + if ( NULL != stream->next_in ) { + lua_pushlstring(L, (char*)stream->next_in, stream->avail_in); + lua_replace(L, lua_upvalueindex(2)); + } + + /* "close" the stream/remove finalizer: */ + if ( result == Z_STREAM_END ) { + /* Clear-out the metatable so end is not called twice: */ + lua_pushnil(L); + lua_setmetatable(L, lua_upvalueindex(1)); + + /* nil the upvalue: */ + lua_pushnil(L); + lua_replace(L, lua_upvalueindex(1)); + + /* Close the stream: */ + lz_assert(L, end(stream), stream, __FILE__, __LINE__); + + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + lua_pushinteger(L, stream->total_in); + lua_pushinteger(L, stream->total_out); + return 4; +} + +static void lz_create_deflate_mt(lua_State *L) { + luaL_newmetatable(L, "lz.deflate.meta"); /* {} */ + + lua_pushcfunction(L, lz_deflate_delete); + lua_setfield(L, -2, "__gc"); + + lua_pop(L, 1); /* */ +} + +static int lz_deflate_new(lua_State *L) { + int level; + int window_size; + int result; + +#ifdef LZLIB_COMPAT + if ( lua_istable(L, 1) || lua_isuserdata(L, 1) || lua_isfunction(L, 1) ) { + return lzlib_deflate(L); + } +#endif + + level = luaL_optint(L, 1, Z_DEFAULT_COMPRESSION); + window_size = luaL_optint(L, 2, MAX_WBITS); + + /* Allocate the stream: */ + z_stream* stream = (z_stream*)lua_newuserdata(L, sizeof(z_stream)); + + stream->zalloc = Z_NULL; + stream->zfree = Z_NULL; + + result = deflateInit2(stream, level, Z_DEFLATED, window_size, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + lz_assert(L, result, stream, __FILE__, __LINE__); + + /* Don't allow destructor to execute unless deflateInit2 was successful: */ + luaL_getmetatable(L, "lz.deflate.meta"); + lua_setmetatable(L, -2); + + lua_pushnil(L); + lua_pushcclosure(L, lz_deflate, 2); + return 1; +} + +static int lz_deflate(lua_State *L) { + return lz_filter_impl(L, deflate, deflateEnd, "deflate"); +} + +static int lz_deflate_delete(lua_State *L) { + z_stream* stream = (z_stream*)lua_touserdata(L, 1); + + /* Ignore errors. */ + deflateEnd(stream); + + return 0; +} + + +static void lz_create_inflate_mt(lua_State *L) { + luaL_newmetatable(L, "lz.inflate.meta"); /* {} */ + + lua_pushcfunction(L, lz_inflate_delete); + lua_setfield(L, -2, "__gc"); + + lua_pop(L, 1); /* */ +} + +static int lz_inflate_new(lua_State *L) { + /* Allocate the stream */ + z_stream* stream; + +#ifdef LZLIB_COMPAT + int type = lua_type(L, 1); + if ( type == LUA_TTABLE || type == LUA_TUSERDATA || type == LUA_TFUNCTION || type == LUA_TSTRING ) { + return lzlib_inflate(L); + } +#endif + + stream = (z_stream*)lua_newuserdata(L, sizeof(z_stream)); + + /* By default, we will do gzip header detection w/ max window size */ + int window_size = lua_isnumber(L, 1) ? lua_tointeger(L, 1) : MAX_WBITS + 32; + + stream->zalloc = Z_NULL; + stream->zfree = Z_NULL; + stream->next_in = Z_NULL; + stream->avail_in = 0; + + lz_assert(L, inflateInit2(stream, window_size), stream, __FILE__, __LINE__); + + /* Don't allow destructor to execute unless deflateInit was successful: */ + luaL_getmetatable(L, "lz.inflate.meta"); + lua_setmetatable(L, -2); + + lua_pushnil(L); + lua_pushcclosure(L, lz_inflate, 2); + return 1; +} + +static int lz_inflate(lua_State *L) { + return lz_filter_impl(L, inflate, inflateEnd, "inflate"); +} + +static int lz_inflate_delete(lua_State *L) { + z_stream* stream = (z_stream*)lua_touserdata(L, 1); + + /* Ignore errors: */ + inflateEnd(stream); + + return 0; +} + +static int lz_checksum(lua_State *L) { + if ( lua_gettop(L) <= 0 ) { + lua_pushvalue(L, lua_upvalueindex(3)); + lua_pushvalue(L, lua_upvalueindex(4)); + } else if ( lua_isfunction(L, 1) ) { + checksum_combine_t combine = (checksum_combine_t) + lua_touserdata(L, lua_upvalueindex(2)); + + lua_pushvalue(L, 1); + lua_call(L, 0, 2); + if ( ! lua_isnumber(L, -2) || ! lua_isnumber(L, -1) ) { + luaL_argerror(L, 1, "expected function to return two numbers"); + } + + /* Calculate and replace the checksum */ + lua_pushnumber(L, + combine((uLong)lua_tonumber(L, lua_upvalueindex(3)), + (uLong)lua_tonumber(L, -2), + (z_off_t)lua_tonumber(L, -1))); + lua_pushvalue(L, -1); + lua_replace(L, lua_upvalueindex(3)); + + /* Calculate and replace the length */ + lua_pushnumber(L, + lua_tonumber(L, lua_upvalueindex(4)) + lua_tonumber(L, -2)); + lua_pushvalue(L, -1); + lua_replace(L, lua_upvalueindex(4)); + } else { + const Bytef* str; + size_t len; + + checksum_t checksum = (checksum_t) + lua_touserdata(L, lua_upvalueindex(1)); + str = (const Bytef*)luaL_checklstring(L, 1, &len); + + /* Calculate and replace the checksum */ + lua_pushnumber(L, + checksum((uLong)lua_tonumber(L, lua_upvalueindex(3)), + str, + len)); + lua_pushvalue(L, -1); + lua_replace(L, lua_upvalueindex(3)); + + /* Calculate and replace the length */ + lua_pushnumber(L, + lua_tonumber(L, lua_upvalueindex(4)) + len); + lua_pushvalue(L, -1); + lua_replace(L, lua_upvalueindex(4)); + } + return 2; +} + +static int lz_checksum_new(lua_State *L, checksum_t checksum, checksum_combine_t combine) { + lua_pushlightuserdata(L, checksum); + lua_pushlightuserdata(L, combine); + lua_pushnumber(L, checksum(0L, Z_NULL, 0)); + lua_pushnumber(L, 0); + lua_pushcclosure(L, lz_checksum, 4); + return 1; +} + +static int lz_adler32(lua_State *L) { +#ifdef LZLIB_COMPAT + /* lzlib compat*/ + if ( lua_gettop(L) != 0 ) { + size_t len; + int adler; + const unsigned char* buf; + if ( lua_isfunction(L, 1) ) { + adler = adler32(0L, Z_NULL, 0); + } else { + adler = (int)luaL_checkinteger(L, 1); + } + buf = (unsigned char*)luaL_checklstring(L, 2, &len); + lua_pushnumber(L, adler32(adler, buf, len)); + return 1; + } +#endif + return lz_checksum_new(L, adler32, adler32_combine); +} + +static int lz_crc32(lua_State *L) { +#ifdef LZLIB_COMPAT + /* lzlib compat*/ + if ( lua_gettop(L) != 0 ) { + size_t len; + int crc; + const unsigned char* buf; + if ( lua_isfunction(L, 1) ) { + crc = crc32(0L, Z_NULL, 0); + } else { + crc = (int)luaL_checkinteger(L, 1); + } + buf = (unsigned char*)luaL_checklstring(L, 2, &len); + lua_pushnumber(L, crc32(crc, buf, len)); + return 1; + } +#endif + return lz_checksum_new(L, crc32, crc32_combine); +} + + +static const luaL_Reg zlib_functions[] = { + { "deflate", lz_deflate_new }, + { "inflate", lz_inflate_new }, + { "adler32", lz_adler32 }, + { "crc32", lz_crc32 }, +#ifdef LZLIB_COMPAT + { "compress", lzlib_compress }, + { "decompress", lzlib_decompress }, +#endif + { "version", lz_version }, + { NULL, NULL } +}; + +#define SETLITERAL(n,v) (lua_pushliteral(L, n), lua_pushliteral(L, v), lua_settable(L, -3)) +#define SETINT(n,v) (lua_pushliteral(L, n), lua_pushinteger(L, v), lua_settable(L, -3)) + +LUALIB_API int luaopen_zlib(lua_State * const L) { + lz_create_deflate_mt(L); + lz_create_inflate_mt(L); + + luaL_register(L, "zlib", zlib_functions); + + SETINT("BEST_SPEED", Z_BEST_SPEED); + SETINT("BEST_COMPRESSION", Z_BEST_COMPRESSION); + + SETLITERAL("_COPYRIGHT", "Copyright (c) 2009-2016 Brian Maher"); + SETLITERAL("_DESCRIPTION", "Simple streaming interface to the zlib library"); + SETLITERAL("_VERSION", "lua-zlib $Id$ $Format:%d$"); + + /* Expose this to lua so we can do a test: */ + SETINT("_TEST_BUFSIZ", LUAL_BUFFERSIZE); + + /* lzlib compatibility */ +#ifdef LZLIB_COMPAT + SETINT("NO_COMPRESSION", Z_NO_COMPRESSION); + SETINT("DEFAULT_COMPRESSION", Z_DEFAULT_COMPRESSION); + SETINT("FILTERED", Z_FILTERED); + SETINT("HUFFMAN_ONLY", Z_HUFFMAN_ONLY); + SETINT("RLE", Z_RLE); + SETINT("FIXED", Z_FIXED); + SETINT("DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); + SETINT("MINIMUM_MEMLEVEL", 1); + SETINT("MAXIMUM_MEMLEVEL", 9); + SETINT("DEFAULT_MEMLEVEL", 8); + SETINT("DEFAULT_WINDOWBITS", 15); + SETINT("MINIMUM_WINDOWBITS", 8); + SETINT("MAXIMUM_WINDOWBITS", 15); + SETINT("GZIP_WINDOWBITS", 16); + SETINT("RAW_WINDOWBITS", -1); +#endif + + return 1; +} diff --git a/vendor/luafilesystem/.gitignore b/vendor/luafilesystem/.gitignore new file mode 100644 index 00000000..ddaacd81 --- /dev/null +++ b/vendor/luafilesystem/.gitignore @@ -0,0 +1,2 @@ +*.so + diff --git a/vendor/luafilesystem/.travis.yml b/vendor/luafilesystem/.travis.yml new file mode 100644 index 00000000..618e48e1 --- /dev/null +++ b/vendor/luafilesystem/.travis.yml @@ -0,0 +1,34 @@ +language: c + +sudo: false + +env: + - LUA="lua 5.1" + - LUA="lua 5.2" + - LUA="lua 5.3" + - LUA="luajit 2.1" + +before_install: + - pip install --user cpp-coveralls hererocks + - hererocks env --$LUA --luarocks latest + - export PATH="$PWD/env/bin:$PATH" + - luarocks install lua-path + - luarocks install dkjson + - luarocks install luacov + # install luacov-coveralls, but avoid installing luafilesystem + - luarocks install luacov-coveralls --server=https://luarocks.org/dev --deps-mode=none + +install: + - luarocks make CFLAGS="-O2 -fPIC -ftest-coverage -fprofile-arcs" LIBFLAG="-shared --coverage" + +script: + - lua -lluacov tests/test.lua + +after_success: + - coveralls -b . -i src --dump c.report.json + - luacov-coveralls -j c.report.json -v + +notifications: + email: + on_success: change + on_failure: always diff --git a/vendor/luafilesystem/LICENSE b/vendor/luafilesystem/LICENSE new file mode 100644 index 00000000..8475345a --- /dev/null +++ b/vendor/luafilesystem/LICENSE @@ -0,0 +1,21 @@ +Copyright Ā© 2003-2014 Kepler Project. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/luafilesystem/README.md b/vendor/luafilesystem/README.md new file mode 100644 index 00000000..d0fd3f29 --- /dev/null +++ b/vendor/luafilesystem/README.md @@ -0,0 +1,28 @@ +[![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE) +[![Build Status](https://travis-ci.org/keplerproject/luafilesystem.svg?branch=master)](https://travis-ci.org/keplerproject/luafilesystem) +[![Build status](https://ci.appveyor.com/api/projects/status/y04s4ms7u16trw8e?svg=true)](https://ci.appveyor.com/project/ignacio/luafilesystem) +[![Coverage Status](https://coveralls.io/repos/keplerproject/luafilesystem/badge.png)](https://coveralls.io/r/keplerproject/luafilesystem) + +# LuaFileSystem - File System Library for Lua + +Copyright 2003-2020 Kepler Project + +https://keplerproject.github.io/luafilesystem + +# Description + +LuaFileSystem is a Lua library developed to complement the set of functions +related to file systems offered by the standard Lua distribution. + +LuaFileSystem offers a portable way to access the underlying directory structure and file attributes. +LuaFileSystem is free software and uses the same license as Lua 5.x (MIT). + +# LuaRocks Installation + +``` +luarocks install luafilesystem +``` + +# Documentation + +Please check the documentation at doc/us/ for more information. diff --git a/vendor/luafilesystem/luafilesystem-scm-1.rockspec b/vendor/luafilesystem/luafilesystem-scm-1.rockspec new file mode 100644 index 00000000..71cf19b4 --- /dev/null +++ b/vendor/luafilesystem/luafilesystem-scm-1.rockspec @@ -0,0 +1,28 @@ +package = "luafilesystem" +version = "scm-1" +source = { + url = "git://github.com/keplerproject/luafilesystem" +} +description = { + summary = "File System Library for the Lua Programming Language", + detailed = [[ + LuaFileSystem is a Lua library developed to complement the set of + functions related to file systems offered by the standard Lua + distribution. LuaFileSystem offers a portable way to access the + underlying directory structure and file attributes. + ]], + license = "MIT/X11" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + lfs = "src/lfs.c" + }, + copy_directories = { + "doc", + "tests" + } +} diff --git a/vendor/luafilesystem/src/.gitignore b/vendor/luafilesystem/src/.gitignore new file mode 100644 index 00000000..9d22eb46 --- /dev/null +++ b/vendor/luafilesystem/src/.gitignore @@ -0,0 +1,2 @@ +*.o +*.so diff --git a/vendor/luafilesystem/src/lfs.c b/vendor/luafilesystem/src/lfs.c new file mode 100644 index 00000000..95ab63b4 --- /dev/null +++ b/vendor/luafilesystem/src/lfs.c @@ -0,0 +1,1182 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2020 +** (http://keplerproject.github.io/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename | attributetable]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.link (old, new[, symlink]) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +*/ + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#ifndef _WIN32 +#ifndef _AIX +#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ +#else +#define _LARGE_FILES 1 /* AIX */ +#endif +#endif +#endif + +#ifdef _WIN32 +#define _WIN32_WINNT 0x600 +#endif + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#include +#include +#include +#include + +#ifdef __BORLANDC__ +#include +#else +#include +#endif + +#include + +/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ +#define LFS_MAXPATHLEN MAX_PATH + +#else + +#include +#include +#include +#include +#include +#include /* for MAXPATHLEN */ + +#ifdef MAXPATHLEN +#define LFS_MAXPATHLEN MAXPATHLEN +#else +#include /* for _POSIX_PATH_MAX */ +#define LFS_MAXPATHLEN _POSIX_PATH_MAX +#endif + +#endif + +#include +#include +#include + +#include "lfs.h" + +#define LFS_VERSION "1.8.0" +#define LFS_LIBNAME "lfs" + +#if LUA_VERSION_NUM >= 503 /* Lua 5.3+ */ + +#ifndef luaL_optlong +#define luaL_optlong luaL_optinteger +#endif + +#endif + +#if LUA_VERSION_NUM >= 502 +#define new_lib(L, l) (luaL_newlib(L, l)) +#else +#define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) +#endif + +/* Define 'strerror' for systems that do not implement it */ +#ifdef NO_STRERROR +#define strerror(_) "System unable to describe the error" +#endif + +#define DIR_METATABLE "directory metatable" +typedef struct dir_data { + int closed; +#ifdef _WIN32 + intptr_t hFile; + char pattern[MAX_PATH + 1]; +#else + DIR *dir; +#endif +} dir_data; + +#define LOCK_METATABLE "lock metatable" + +#ifdef _WIN32 + +#ifdef __BORLANDC__ +#define lfs_setmode(file, m) (setmode(_fileno(file), m)) +#define STAT_STRUCT struct stati64 +#else +#define lfs_setmode(file, m) (_setmode(_fileno(file), m)) +#define STAT_STRUCT struct _stati64 +#endif + +#ifndef _S_IFLNK +#define _S_IFLNK 0x400 +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (mode&_S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (mode&_S_IFREG) +#endif +#ifndef S_ISLNK +#define S_ISLNK(mode) (mode&_S_IFLNK) +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(mode) (0) +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (0) +#endif +#ifndef S_ISCHR +#define S_ISCHR(mode) (mode&_S_IFCHR) +#endif +#ifndef S_ISBLK +#define S_ISBLK(mode) (0) +#endif + +#define STAT_FUNC _stati64 +#define LSTAT_FUNC lfs_win32_lstat + +#else + +#define _O_TEXT 0 +#define _O_BINARY 0 +#define lfs_setmode(file, m) ((void)file, (void)m, 0) +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat + +#endif + +#ifdef _WIN32 +#define lfs_mkdir _mkdir +#else +#define lfs_mkdir(path) (mkdir((path), \ + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) +#endif + +#ifdef _WIN32 + +int lfs_win32_pusherror(lua_State * L) +{ + int en = GetLastError(); + lua_pushnil(L); + if (en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; +} + +#define TICKS_PER_SECOND 10000000 +#define EPOCH_DIFFERENCE 11644473600LL +time_t windowsToUnixTime(FILETIME ft) +{ + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (time_t) (uli.QuadPart / TICKS_PER_SECOND - EPOCH_DIFFERENCE); +} + +int lfs_win32_lstat(const char *path, STAT_STRUCT * buffer) +{ + WIN32_FILE_ATTRIBUTE_DATA win32buffer; + if (GetFileAttributesEx(path, GetFileExInfoStandard, &win32buffer)) { + if (!(win32buffer.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + return STAT_FUNC(path, buffer); + } + buffer->st_mode = _S_IFLNK; + buffer->st_dev = 0; + buffer->st_ino = 0; + buffer->st_nlink = 0; + buffer->st_uid = 0; + buffer->st_gid = 0; + buffer->st_rdev = 0; + buffer->st_atime = windowsToUnixTime(win32buffer.ftLastAccessTime); + buffer->st_mtime = windowsToUnixTime(win32buffer.ftLastWriteTime); + buffer->st_ctime = windowsToUnixTime(win32buffer.ftCreationTime); + buffer->st_size = 0; + return 0; + } else { + return 1; + } +} + +#endif + +/* +** Utility functions +*/ +static int pusherror(lua_State * L, const char *info) +{ + lua_pushnil(L); + if (info == NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; +} + +static int pushresult(lua_State * L, int res, const char *info) +{ + if (res == -1) { + return pusherror(L, info); + } else { + lua_pushboolean(L, 1); + return 1; + } +} + + +/* +** This function changes the working (current) directory +*/ +static int change_dir(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushnil(L); + lua_pushfstring(L, "Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean(L, 1); + return 1; + } +} + +/* +** This function returns the current directory +** If unable to get the current directory, it returns nil +** and a string describing the error +*/ +static int get_dir(lua_State * L) +{ +#ifdef NO_GETCWD + lua_pushnil(L); + lua_pushstring(L, "Function 'getcwd' not provided by system"); + return 2; +#else + char *path = NULL; + /* Passing (NULL, 0) is not guaranteed to work. + Use a temp buffer and size instead. */ + size_t size = LFS_MAXPATHLEN; /* initial buffer size */ + int result; + while (1) { + char *path2 = realloc(path, size); + if (!path2) { /* failed to allocate */ + result = pusherror(L, "get_dir realloc() failed"); + break; + } + path = path2; + if (getcwd(path, size) != NULL) { + /* success, push the path to the Lua stack */ + lua_pushstring(L, path); + result = 1; + break; + } + if (errno != ERANGE) { /* unexpected error */ + result = pusherror(L, "get_dir getcwd() failed"); + break; + } + /* ERANGE = insufficient buffer capacity, double size and retry */ + size *= 2; + } + free(path); + return result; +#endif +} + +/* +** Check if the given element on the stack is a file and returns it. +*/ +static FILE *check_file(lua_State * L, int idx, const char *funcname) +{ +#if LUA_VERSION_NUM == 501 + FILE **fh = (FILE **) luaL_checkudata(L, idx, "FILE*"); + if (*fh == NULL) { + luaL_error(L, "%s: closed file", funcname); + return 0; + } else + return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504 + luaL_Stream *fh = (luaL_Stream *) luaL_checkudata(L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error(L, "%s: closed file", funcname); + return 0; + } else + return fh->f; +#else +#error unsupported Lua version +#endif +} + + +/* +** +*/ +static int _file_lock(lua_State * L, FILE * fh, const char *mode, + const long start, long len, const char *funcname) +{ + int code; +#ifdef _WIN32 + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, + the program immediately tries again after 1 second. + If, after 10 attempts, the bytes cannot be locked, + the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, + the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been + previously locked. + + Regions should be locked only briefly and should be unlocked + before closing a file or exiting the program. + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': + lkmode = LK_NBLCK; + break; + case 'w': + lkmode = LK_NBLCK; + break; + case 'u': + lkmode = LK_UNLCK; + break; + default: + return luaL_error(L, "%s: invalid mode", funcname); + } + if (!len) { + fseek(fh, 0L, SEEK_END); + len = ftell(fh); + } + fseek(fh, start, SEEK_SET); +#ifdef __BORLANDC__ + code = locking(fileno(fh), lkmode, len); +#else + code = _locking(fileno(fh), lkmode, len); +#endif +#else + struct flock f; + switch (*mode) { + case 'w': + f.l_type = F_WRLCK; + break; + case 'r': + f.l_type = F_RDLCK; + break; + case 'u': + f.l_type = F_UNLCK; + break; + default: + return luaL_error(L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t) start; + f.l_len = (off_t) len; + code = fcntl(fileno(fh), F_SETLK, &f); +#endif + return (code != -1); +} + +#ifdef _WIN32 +typedef struct lfs_Lock { + HANDLE fd; +} lfs_Lock; +static int lfs_lock_dir(lua_State * L) +{ + size_t pathl; + HANDLE fd; + lfs_Lock *lock; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + ln = (char *) malloc(pathl + strlen(lockfile) + 1); + if (!ln) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } + strcpy(ln, path); + strcat(ln, lockfile); + fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); + free(ln); + if (fd == INVALID_HANDLE_VALUE) { + return lfs_win32_pusherror(L); + } + lock = (lfs_Lock *) lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; + luaL_getmetatable(L, LOCK_METATABLE); + lua_setmetatable(L, -2); + return 1; +} + +static int lfs_unlock_dir(lua_State * L) +{ + lfs_Lock *lock = (lfs_Lock *) luaL_checkudata(L, 1, LOCK_METATABLE); + if (lock->fd != INVALID_HANDLE_VALUE) { + CloseHandle(lock->fd); + lock->fd = INVALID_HANDLE_VALUE; + } + return 0; +} +#else +typedef struct lfs_Lock { + char *ln; +} lfs_Lock; +static int lfs_lock_dir(lua_State * L) +{ + lfs_Lock *lock; + size_t pathl; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + lock = (lfs_Lock *) lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char *) malloc(pathl + strlen(lockfile) + 1); + if (!ln) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } + strcpy(ln, path); + strcat(ln, lockfile); + if (symlink("lock", ln) == -1) { + free(ln); + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } + lock->ln = ln; + luaL_getmetatable(L, LOCK_METATABLE); + lua_setmetatable(L, -2); + return 1; +} + +static int lfs_unlock_dir(lua_State * L) +{ + lfs_Lock *lock = (lfs_Lock *) luaL_checkudata(L, 1, LOCK_METATABLE); + if (lock->ln) { + unlink(lock->ln); + free(lock->ln); + lock->ln = NULL; + } + return 0; +} +#endif + +static int lfs_g_setmode(lua_State * L, FILE * f, int arg) +{ + static const int mode[] = { _O_BINARY, _O_TEXT }; + static const char *const modenames[] = { "binary", "text", NULL }; + int op = luaL_checkoption(L, arg, NULL, modenames); + int res = lfs_setmode(f, mode[op]); + if (res != -1) { + int i; + lua_pushboolean(L, 1); + for (i = 0; modenames[i] != NULL; i++) { + if (mode[i] == res) { + lua_pushstring(L, modenames[i]); + return 2; + } + } + lua_pushnil(L); + return 2; + } else { + return pusherror(L, NULL); + } +} + +static int lfs_f_setmode(lua_State * L) +{ + return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); +} + +/* +** Locks a file. +** @param #1 File handle. +** @param #2 String with lock mode ('w'rite, 'r'ead). +** @param #3 Number with start position (optional). +** @param #4 Number with length (optional). +*/ +static int file_lock(lua_State * L) +{ + FILE *fh = check_file(L, 1, "lock"); + const char *mode = luaL_checkstring(L, 2); + const long start = (long) luaL_optinteger(L, 3, 0); + long len = (long) luaL_optinteger(L, 4, 0); + if (_file_lock(L, fh, mode, start, len, "lock")) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushfstring(L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Unlocks a file. +** @param #1 File handle. +** @param #2 Number with start position (optional). +** @param #3 Number with length (optional). +*/ +static int file_unlock(lua_State * L) +{ + FILE *fh = check_file(L, 1, "unlock"); + const long start = (long) luaL_optinteger(L, 2, 0); + long len = (long) luaL_optinteger(L, 3, 0); + if (_file_lock(L, fh, "u", start, len, "unlock")) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushfstring(L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Creates a link. +** @param #1 Object to link to. +** @param #2 Name of link. +** @param #3 True if link is symbolic (optional). +*/ +static int make_link(lua_State * L) +{ + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); +#ifndef _WIN32 + return pushresult(L, + (lua_toboolean(L, 3) ? symlink : link) (oldpath, + newpath), + NULL); +#else + int symbolic = lua_toboolean(L, 3); + STAT_STRUCT oldpathinfo; + int is_dir = 0; + if (STAT_FUNC(oldpath, &oldpathinfo) == 0) { + is_dir = S_ISDIR(oldpathinfo.st_mode) != 0; + } + if (!symbolic && is_dir) { + lua_pushnil(L); + lua_pushstring(L, + "hard links to directories are not supported on Windows"); + return 2; + } + + int result = symbolic ? CreateSymbolicLink(newpath, oldpath, is_dir) + : CreateHardLink(newpath, oldpath, NULL); + + if (result) { + return pushresult(L, result, NULL); + } else { + lua_pushnil(L); + lua_pushstring(L, symbolic ? "make_link CreateSymbolicLink() failed" + : "make_link CreateHardLink() failed"); + return 2; + } +#endif +} + + +/* +** Creates a directory. +** @param #1 Directory path. +*/ +static int make_dir(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + return pushresult(L, lfs_mkdir(path), NULL); +} + + +/* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + return pushresult(L, rmdir(path), NULL); +} + + +/* +** Directory iterator +*/ +static int dir_iter(lua_State * L) +{ +#ifdef _WIN32 + struct _finddata_t c_file; +#else + struct dirent *entry; +#endif + dir_data *d = (dir_data *) luaL_checkudata(L, 1, DIR_METATABLE); + luaL_argcheck(L, d->closed == 0, 1, "closed directory"); +#ifdef _WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst(d->pattern, &c_file)) == -1L) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext(d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose(d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } +#else + if ((entry = readdir(d->dir)) != NULL) { + lua_pushstring(L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir(d->dir); + d->closed = 1; + return 0; + } +#endif +} + + +/* +** Closes directory iterators +*/ +static int dir_close(lua_State * L) +{ + dir_data *d = (dir_data *) lua_touserdata(L, 1); +#ifdef _WIN32 + if (!d->closed && d->hFile) { + _findclose(d->hFile); + } +#else + if (!d->closed && d->dir) { + closedir(d->dir); + } +#endif + d->closed = 1; + return 0; +} + + +/* +** Factory of directory iterators +*/ +static int dir_iter_factory(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + dir_data *d; + lua_pushcfunction(L, dir_iter); + d = (dir_data *) lua_newuserdata(L, sizeof(dir_data)); + luaL_getmetatable(L, DIR_METATABLE); + lua_setmetatable(L, -2); + d->closed = 0; +#ifdef _WIN32 + d->hFile = 0L; + if (strlen(path) > MAX_PATH - 2) + luaL_error(L, "path too long: %s", path); + else + sprintf(d->pattern, "%s/*", path); +#else + d->dir = opendir(path); + if (d->dir == NULL) + luaL_error(L, "cannot open %s: %s", path, strerror(errno)); +#endif +#if LUA_VERSION_NUM >= 504 + lua_pushnil(L); + lua_pushvalue(L, -2); + return 4; +#else + return 2; +#endif +} + + +/* +** Creates directory metatable. +*/ +static int dir_create_meta(lua_State * L) +{ + luaL_newmetatable(L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "__gc"); + +#if LUA_VERSION_NUM >= 504 + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "__close"); +#endif + return 1; +} + + +/* +** Creates lock metatable. +*/ +static int lock_create_meta(lua_State * L) +{ + luaL_newmetatable(L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + + +/* +** Convert the inode protection mode to a string. +*/ +#ifdef _WIN32 +static const char *mode2string(unsigned short mode) +{ +#else +static const char *mode2string(mode_t mode) +{ +#endif + if (S_ISREG(mode)) + return "file"; + else if (S_ISDIR(mode)) + return "directory"; + else if (S_ISLNK(mode)) + return "link"; + else if (S_ISSOCK(mode)) + return "socket"; + else if (S_ISFIFO(mode)) + return "named pipe"; + else if (S_ISCHR(mode)) + return "char device"; + else if (S_ISBLK(mode)) + return "block device"; + else + return "other"; +} + + +/* +** Set access time and modification values for a file. +** @param #1 File path. +** @param #2 Access time in seconds, current time is used if missing. +** @param #3 Modification time in seconds, access time is used if missing. +*/ +static int file_utime(lua_State * L) +{ + const char *file = luaL_checkstring(L, 1); + struct utimbuf utb, *buf; + + if (lua_gettop(L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t) luaL_optnumber(L, 2, 0); + utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime); + buf = &utb; + } + + return pushresult(L, utime(file, buf), NULL); +} + + +/* inode protection mode */ +static void push_st_mode(lua_State * L, STAT_STRUCT * info) +{ + lua_pushstring(L, mode2string(info->st_mode)); +} + +/* device inode resides on */ +static void push_st_dev(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_dev); +} + +/* inode's number */ +static void push_st_ino(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_ino); +} + +/* number of hard links to the file */ +static void push_st_nlink(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_nlink); +} + +/* user-id of owner */ +static void push_st_uid(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_uid); +} + +/* group-id of owner */ +static void push_st_gid(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_gid); +} + +/* device type, for special file inode */ +static void push_st_rdev(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_rdev); +} + +/* time of last access */ +static void push_st_atime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_atime); +} + +/* time of last data modification */ +static void push_st_mtime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_mtime); +} + +/* time of last file status change */ +static void push_st_ctime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_ctime); +} + +/* file size, in bytes */ +static void push_st_size(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_size); +} + +#ifndef _WIN32 +/* blocks allocated for file */ +static void push_st_blocks(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_blocks); +} + +/* optimal file system I/O blocksize */ +static void push_st_blksize(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_blksize); +} +#endif + + /* + ** Convert the inode protection mode to a permission list. + */ + +#ifdef _WIN32 +static const char *perm2string(unsigned short mode) +{ + static char perms[10] = "---------"; + int i; + for (i = 0; i < 9; i++) + perms[i] = '-'; + if (mode & _S_IREAD) { + perms[0] = 'r'; + perms[3] = 'r'; + perms[6] = 'r'; + } + if (mode & _S_IWRITE) { + perms[1] = 'w'; + perms[4] = 'w'; + perms[7] = 'w'; + } + if (mode & _S_IEXEC) { + perms[2] = 'x'; + perms[5] = 'x'; + perms[8] = 'x'; + } + return perms; +} +#else +static const char *perm2string(mode_t mode) +{ + static char perms[10] = "---------"; + int i; + for (i = 0; i < 9; i++) + perms[i] = '-'; + if (mode & S_IRUSR) + perms[0] = 'r'; + if (mode & S_IWUSR) + perms[1] = 'w'; + if (mode & S_IXUSR) + perms[2] = 'x'; + if (mode & S_IRGRP) + perms[3] = 'r'; + if (mode & S_IWGRP) + perms[4] = 'w'; + if (mode & S_IXGRP) + perms[5] = 'x'; + if (mode & S_IROTH) + perms[6] = 'r'; + if (mode & S_IWOTH) + perms[7] = 'w'; + if (mode & S_IXOTH) + perms[8] = 'x'; + return perms; +} +#endif + +/* permssions string */ +static void push_st_perm(lua_State * L, STAT_STRUCT * info) +{ + lua_pushstring(L, perm2string(info->st_mode)); +} + +typedef void (*_push_function)(lua_State * L, STAT_STRUCT * info); + +struct _stat_members { + const char *name; + _push_function push; +}; + +struct _stat_members members[] = { + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, + { "permissions", push_st_perm }, +#ifndef _WIN32 + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, +#endif + { NULL, NULL } +}; + +/* +** Get file or symbolic link information +*/ +static int _file_info_(lua_State * L, + int (*st)(const char *, STAT_STRUCT *)) +{ + STAT_STRUCT info; + const char *file = luaL_checkstring(L, 1); + int i; + + if (st(file, &info)) { + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", + file, strerror(errno)); + lua_pushinteger(L, errno); + return 3; + } + if (lua_isstring(L, 2)) { + const char *member = lua_tostring(L, 2); + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push(L, &info); + return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable(L, 2)) { + lua_newtable(L); + } + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring(L, members[i].name); + members[i].push(L, &info); + lua_rawset(L, -3); + } + return 1; +} + + +/* +** Get file information using stat. +*/ +static int file_info(lua_State * L) +{ + return _file_info_(L, STAT_FUNC); +} + + +/* +** Push the symlink target to the top of the stack. +** Assumes the file name is at position 1 of the stack. +** Returns 1 if successful (with the target on top of the stack), +** 0 on failure (with stack unchanged, and errno set). +*/ +static int push_link_target(lua_State * L) +{ + const char *file = luaL_checkstring(L, 1); +#ifdef _WIN32 + HANDLE h = CreateFile(file, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return lfs_win32_pusherror(L); + } +#endif + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + int ok = 0; + while (!ok) { + char *target2 = realloc(target, size); + if (!target2) { /* failed to allocate */ + break; + } + target = target2; +#ifdef _WIN32 + tsize = GetFinalPathNameByHandle(h, target, size, FILE_NAME_OPENED); +#else + tsize = readlink(file, target, size); +#endif + if (tsize < 0) { /* a readlink() error occurred */ + break; + } + if (tsize < size) { +#ifdef _WIN32 + if (tsize > 4 && strncmp(target, "\\\\?\\", 4) == 0) { + memmove_s(target, tsize - 3, target + 4, tsize - 3); + tsize -= 4; + } +#endif + ok = 1; + break; + } + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + if (ok) { + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + } +#ifdef _WIN32 + CloseHandle(h); +#endif + free(target); + return ok; +} + +/* +** Get symbolic link information using lstat. +*/ +static int link_info(lua_State * L) +{ + int ret; + if (lua_isstring(L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_(L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info(lua_State * L) +{ + lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); + lua_setfield(L, -2, "_COPYRIGHT"); + lua_pushliteral(L, + "LuaFileSystem is a Lua library developed to complement " + "the set of functions related to file systems offered by " + "the standard Lua distribution"); + lua_setfield(L, -2, "_DESCRIPTION"); + lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); + lua_setfield(L, -2, "_VERSION"); +} + + +static const struct luaL_Reg fslib[] = { + { "attributes", file_info }, + { "chdir", change_dir }, + { "currentdir", get_dir }, + { "dir", dir_iter_factory }, + { "link", make_link }, + { "lock", file_lock }, + { "mkdir", make_dir }, + { "rmdir", remove_dir }, + { "symlinkattributes", link_info }, + { "setmode", lfs_f_setmode }, + { "touch", file_utime }, + { "unlock", file_unlock }, + { "lock_dir", lfs_lock_dir }, + { NULL, NULL }, +}; + +LFS_EXPORT int luaopen_lfs(lua_State * L) +{ + dir_create_meta(L); + lock_create_meta(L); + new_lib(L, fslib); + lua_pushvalue(L, -1); + lua_setglobal(L, LFS_LIBNAME); + set_info(L); + return 1; +} diff --git a/vendor/luafilesystem/src/lfs.def b/vendor/luafilesystem/src/lfs.def new file mode 100644 index 00000000..bd8c847a --- /dev/null +++ b/vendor/luafilesystem/src/lfs.def @@ -0,0 +1,4 @@ +LIBRARY lfs.dll +VERSION 1.8 +EXPORTS +luaopen_lfs diff --git a/vendor/luafilesystem/src/lfs.h b/vendor/luafilesystem/src/lfs.h new file mode 100644 index 00000000..13b60a91 --- /dev/null +++ b/vendor/luafilesystem/src/lfs.h @@ -0,0 +1,35 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2020 +** (http://keplerproject.github.io/luafilesystem) +*/ + +/* Define 'chdir' for systems that do not implement it */ +#ifdef NO_CHDIR +#define chdir(p) (-1) +#define chdir_error "Function 'chdir' not provided by system" +#else +#define chdir_error strerror(errno) +#endif + +#ifdef _WIN32 +#define chdir(p) (_chdir(p)) +#define getcwd(d, s) (_getcwd(d, s)) +#define rmdir(p) (_rmdir(p)) +#define LFS_EXPORT __declspec (dllexport) +#ifndef fileno +#define fileno(f) (_fileno(f)) +#endif +#else +#define LFS_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + LFS_EXPORT int luaopen_lfs(lua_State * L); + +#ifdef __cplusplus +} +#endif diff --git a/vendor/luasec/.gitignore b/vendor/luasec/.gitignore new file mode 100644 index 00000000..355518b2 --- /dev/null +++ b/vendor/luasec/.gitignore @@ -0,0 +1,3 @@ +/src/*.o +/src/luasocket/*.o +/*.dll diff --git a/vendor/luasec/CHANGELOG b/vendor/luasec/CHANGELOG new file mode 100644 index 00000000..f7c0a780 --- /dev/null +++ b/vendor/luasec/CHANGELOG @@ -0,0 +1,257 @@ +-------------------------------------------------------------------------------- +LuaSec 1.3.2 +--------------- +This version includes: + +* Fix: place EAI_OVERFLOW inside macro, unbreak build on <10.7 (Sergey Fedorov) +* Fix: Expand workaround for zero errno to OpenSSL 3.0.x (Kim Alvefur) +* Fix: reset block timeout at send or receive (MartinDahlberg) + +-------------------------------------------------------------------------------- +LuaSec 1.3.1 +--------------- +This version includes: + +* Fix: check if PSK is available + +-------------------------------------------------------------------------------- +LuaSec 1.3.0 +--------------- +This version includes: + +* Add :getlocalchain() + :getlocalcertificate() to mirror the peer methods (@mwild1) +* Add Pre-Shared Key (PSK) support (@jclab-joseph) + +-------------------------------------------------------------------------------- +LuaSec 1.2.0 +--------------- +This version includes: + +* Add key material export method +* Backguard compat for openssl on providers, like LTS linuxes + +-------------------------------------------------------------------------------- +LuaSec 1.1.0 +--------------- +This version includes: + +* Fix missing DANE flag +* Remove unused parameter in https.lua + +-------------------------------------------------------------------------------- +LuaSec 1.0.2 +--------------- +This version includes: + +* Fix handle SSL_send SYSCALL error without errno +* Fix off by one in cert:validat(notafter) +* Fix meth_get_{sinagure => signature}_name function name +* Fix update the Lua state reference on the selected SSL context after SNI +* Fix ignore SSL_OP_BIT(n) macro and update option.c + +-------------------------------------------------------------------------------- +LuaSec 1.0.1 +--------------- +This version includes: + + +* Fix luaL_buffinit() can use the stack and broke buffer_meth_receive() + +-------------------------------------------------------------------------------- +LuaSec 1.0 +--------------- +This version includes: + + +* Add cert:getsignaturename() + +-------------------------------------------------------------------------------- +LuaSec 0.9 +--------------- +This version includes: + + +* Add DNS-based Authentication of Named Entities (DANE) support +* Add __close() metamethod +* Fix deprecation warnings with OpenSSL 1.1 +* Fix special case listing of TLS 1.3 EC curves +* Fix general_name leak in cert:extensions() +* Fix unexported 'ssl.config' table +* Replace $(LD) with $(CCLD) variable +* Remove multiple definitions of 'ssl_options' variable +* Use tag in git format: v0.9 + +-------------------------------------------------------------------------------- +LuaSec 0.8.2 +--------------- +This version includes: + +* Fix unexported 'ssl.config' table (backported) + +-------------------------------------------------------------------------------- +LuaSec 0.8.1 +--------------- +This version includes: + +* Fix general_name leak in cert:extensions() (backported) + +-------------------------------------------------------------------------------- +LuaSec 0.8 +--------------- +This version includes: + +* Add support to ALPN +* Add support to TLS 1.3 +* Add support to multiple certificates +* Add timeout to https module (https.TIMEOUT) +* Drop support to SSL 3.0 +* Drop support to TLS 1.0 from https module +* Fix invalid reference to Lua state +* Fix memory leak when get certficate extensions + +-------------------------------------------------------------------------------- +LuaSec 0.7.2 +--------------- +This version includes: + +* Fix unexported 'ssl.config' table (backported) + +-------------------------------------------------------------------------------- +LuaSec 0.7.1 +--------------- +This version includes: + +* Fix general_name leak in cert:extensions() (backported) + +-------------------------------------------------------------------------------- +LuaSec 0.7 +--------------- +LuaSec depends on OpenSSL, and integrates with LuaSocket to make it +easy to add secure connections to any Lua applications or scripts. + +Documentation: https://github.com/brunoos/luasec/wiki + +This version includes: + +* Add support to OpenSSL 1.1.0 +* Add support to elliptic curves list +* Add ssl.config that exports some OpenSSL information +* Add integration with luaossl + +-------------------------------------------------------------------------------- +LuaSec 0.6 +------------ +LuaSec depends on OpenSSL, and integrates with LuaSocket to make it +easy to add secure connections to any Lua applications or scripts. + +Documentation: https://github.com/brunoos/luasec/wiki + +This version includes: + +* Lua 5.2 and 5.3 compatibility + +* Context module: + - Add ctx:checkkey() + +* SSL module: + - Add conn:sni() and conn:getsniname() + +* Context options: + - Add "any" protocol ("sslv23" is deprecated) + +* HTTPS module: + - Using "any" protocol without SSLv2/SSLv3, by default + +* X509 module: + - Human readable IP address + - Add cert:issued() + - Add cert:pubkey() + +* Some bug fixes + + +=> Thanks to everyone who collaborate with LuaSec <= + +-------------------------------------------------------------------------------- +LuaSec 0.5 +------------ +LuaSec depends on OpenSSL, and integrates with LuaSocket to make it +easy to add secure connections to any Lua applications or scripts. + +This version includes: + + * A new certificate (X509) API, which supports: + - Reading the subject (identity) and issuer of the certificate. + - Reading various X509 extensions, including email and dnsName. + - Converting certificates to and from the standard ASCII PEM + format. + - Generating the fingerprint/digest of a certificate (using SHA1, + SHA256 or SHA512). + - Reading the certificate's expiration, serial number, and other + info. + + * The ability to get more detailed information from OpenSSL about + why a certificate failed verification, for each certificate in the + chain. + + * Flags to force acceptance of invalid certificates, e.g. to allow + the use of self-signed certificates in a Trust On First Use model. + + * Flags to control checking CRLs for certificate revocation status. + + * Support for ECDH cipher suites. + + * An API to get the TLS 'finished' messages used for SASL channel + binding (e.g. the SCRAM PLUS mechanisms). + +The work in this release was undertaken by Kim Alvefur, Paul Aurich, +Tobias Markmann, Bruno Silvestre and Matthew Wild. + +-------------------------------------------------------------------------------- +LuaSec 0.4.1 +------------ +- SSL options updated --- based on OpenSSL 1.0.0d. +- Activate SSL_MODE_RELEASE_BUFFERS by default if it is available. + (thanks Prosody project) + +--------------------------------------------------------------------------------- +LuaSec 0.4 +------------ +- Add option 'no_ticket' (included in OpenSSL 0.9.8f). +- Add HTTPS module. (thanks Tomas Guisasola and Pablo Musa) + +-------------------------------------------------------------------------------- +LuaSec 0.3.3 +------------ +- BUG: Clear the error queue before call I/O functions (see SSL_get_error + manual). + (thanks Matthew Wild) + +-------------------------------------------------------------------------------- +LuaSec 0.3.2 +------------ +- BUG: Windows uses a different way to report socket error. + (thanks Sebastien Perin) + +-------------------------------------------------------------------------------- +LuaSec 0.3.1 +------------ +- BUG: receive("a") returns 'closed' error instead of the content when the + SSL/TLS connection is shut down cleanly. (thanks Matthias Diener) + +-------------------------------------------------------------------------------- +LuaSec 0.3 +---------- +- Add functions ssl.rawcontext() and ssl.rawconnection() +- Add support to encrypted key password. (thanks Norbert Kiesel) + +-------------------------------------------------------------------------------- +LuaSec 0.2.1 +------------ +- 'key' and 'certificate' configurations become optional. (thanks RenĆ© Rebe) +- Add '_VERSION' variable to module. + +-------------------------------------------------------------------------------- +LuaSec 0.2 +---------- +Initial version diff --git a/vendor/luasec/INSTALL b/vendor/luasec/INSTALL new file mode 100644 index 00000000..07b94df8 --- /dev/null +++ b/vendor/luasec/INSTALL @@ -0,0 +1,39 @@ +LuaSec 1.3.2 +------------ + +* OpenSSL options: + + By default, this version includes options for OpenSSL 3.0.8 + + If you need to generate the options for a different version of OpenSSL: + + $ cd src + $ lua options.lua -g /usr/include/openssl/ssl.h > options.c + +-------------------------------------------------------------------------------- + +* On Linux, BSD, and Mac OS X: + + - Edit 'Makefile' + * Inform the path to where install the Lua modules (LUAPATH) and binaries + modules (LUACPATH) + * If Lua or OpenSSL are not in the default path, set the + variables INCDIR and LIBDIR. + * For Mac OS X, set the variable MACOSX_VERSION. + + - Use 'make ' to compile + * Platforms: linux, bsd, or macosx + + - Use 'make install' to install the modules. + +-------------------------------------------------------------------------------- + +* On Windows: + + - Use the Visual C++ project to compile the library. + + - Copy the 'ssl.lua' file to some place in your LUA_PATH. + + - Copy the 'ssl.dll' file to some place in your LUA_CPATH. + + - Create a directory 'ssl' in your LUA_PATH and copy 'https.lua' to it. diff --git a/vendor/luasec/LICENSE b/vendor/luasec/LICENSE new file mode 100644 index 00000000..09df9964 --- /dev/null +++ b/vendor/luasec/LICENSE @@ -0,0 +1,21 @@ +LuaSec 1.3.2 license +Copyright (C) 2006-2023 Bruno Silvestre, UFG + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/luasec/README.md b/vendor/luasec/README.md new file mode 100644 index 00000000..afdce4ee --- /dev/null +++ b/vendor/luasec/README.md @@ -0,0 +1,6 @@ +LuaSec 1.3.2 +=============== +LuaSec depends on OpenSSL, and integrates with LuaSocket to make it +easy to add secure connections to any Lua applications or scripts. + +Documentation: https://github.com/brunoos/luasec/wiki diff --git a/vendor/luasec/luasec-1.3.2-1.rockspec b/vendor/luasec/luasec-1.3.2-1.rockspec new file mode 100644 index 00000000..a6a36d78 --- /dev/null +++ b/vendor/luasec/luasec-1.3.2-1.rockspec @@ -0,0 +1,105 @@ +package = "LuaSec" +version = "1.3.2-1" +source = { + url = "git+https://github.com/brunoos/luasec", + tag = "v1.3.2", +} +description = { + summary = "A binding for OpenSSL library to provide TLS/SSL communication over LuaSocket.", + detailed = "This version delegates to LuaSocket the TCP connection establishment between the client and server. Then LuaSec uses this connection to start a secure TLS/SSL session.", + homepage = "https://github.com/brunoos/luasec/wiki", + license = "MIT" +} +dependencies = { + "lua >= 5.1", "luasocket" +} +external_dependencies = { + platforms = { + unix = { + OPENSSL = { + header = "openssl/ssl.h", + library = "ssl" + } + }, + windows = { + OPENSSL = { + header = "openssl/ssl.h", + } + }, + } +} +build = { + type = "builtin", + copy_directories = { + "samples" + }, + platforms = { + unix = { + install = { + lib = { + "ssl.so" + }, + lua = { + "src/ssl.lua", ['ssl.https'] = "src/https.lua" + } + }, + modules = { + ssl = { + defines = { + "WITH_LUASOCKET", "LUASOCKET_DEBUG", + }, + incdirs = { + "$(OPENSSL_INCDIR)", "src/", "src/luasocket", + }, + libdirs = { + "$(OPENSSL_LIBDIR)" + }, + libraries = { + "ssl", "crypto" + }, + sources = { + "src/options.c", "src/config.c", "src/ec.c", + "src/x509.c", "src/context.c", "src/ssl.c", + "src/luasocket/buffer.c", "src/luasocket/io.c", + "src/luasocket/timeout.c", "src/luasocket/usocket.c" + } + } + } + }, + windows = { + install = { + lib = { + "ssl.dll" + }, + lua = { + "src/ssl.lua", ['ssl.https'] = "src/https.lua" + } + }, + modules = { + ssl = { + 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" + }, + libdirs = { + "$(OPENSSL_LIBDIR)", + "$(OPENSSL_BINDIR)", + }, + libraries = { + "libssl", "libcrypto", "ws2_32" + }, + incdirs = { + "$(OPENSSL_INCDIR)", "src/", "src/luasocket" + }, + sources = { + "src/options.c", "src/config.c", "src/ec.c", + "src/x509.c", "src/context.c", "src/ssl.c", + "src/luasocket/buffer.c", "src/luasocket/io.c", + "src/luasocket/timeout.c", "src/luasocket/wsocket.c" + } + } + } + } + } +} diff --git a/vendor/luasec/src/Makefile b/vendor/luasec/src/Makefile new file mode 100644 index 00000000..9b06a038 --- /dev/null +++ b/vendor/luasec/src/Makefile @@ -0,0 +1,66 @@ +CMOD=ssl.so +LMOD=ssl.lua + +OBJS= \ + options.o \ + x509.o \ + context.o \ + ssl.o \ + config.o \ + ec.o + +LIBS=-lssl -lcrypto -lluasocket + +WARN=-Wall -pedantic + +BSD_CFLAGS=-O2 -fPIC $(WARN) $(INCDIR) $(DEFS) +BSD_LDFLAGS=-O -fPIC -shared $(LIBDIR) + +LNX_CFLAGS=-O2 -fPIC $(WARN) $(INCDIR) $(DEFS) +LNX_LDFLAGS=-O -fPIC -shared $(LIBDIR) + +MAC_ENV=env MACOSX_DEPLOYMENT_TARGET='$(MACVER)' +MAC_CFLAGS=-O2 -fno-common $(WARN) $(INCDIR) $(DEFS) +MAC_LDFLAGS=-bundle -undefined dynamic_lookup $(LIBDIR) + +INSTALL = install +CC ?= cc +CCLD ?= $(MYENV) $(CC) +CFLAGS += $(MYCFLAGS) +LDFLAGS += $(MYLDFLAGS) + +.PHONY: all clean install none linux bsd macosx luasocket + +all: + +install: $(CMOD) $(LMOD) + $(INSTALL) -d $(DESTDIR)$(LUAPATH)/ssl $(DESTDIR)$(LUACPATH) + $(INSTALL) $(CMOD) $(DESTDIR)$(LUACPATH) + $(INSTALL) -m644 $(LMOD) $(DESTDIR)$(LUAPATH) + $(INSTALL) -m644 https.lua $(DESTDIR)$(LUAPATH)/ssl + +linux: + @$(MAKE) $(CMOD) MYCFLAGS="$(LNX_CFLAGS)" MYLDFLAGS="$(LNX_LDFLAGS)" EXTRA="$(EXTRA)" + +bsd: + @$(MAKE) $(CMOD) MYCFLAGS="$(BSD_CFLAGS)" MYLDFLAGS="$(BSD_LDFLAGS)" EXTRA="$(EXTRA)" + +macosx: + @$(MAKE) $(CMOD) MYCFLAGS="$(MAC_CFLAGS)" MYLDFLAGS="$(MAC_LDFLAGS)" MYENV="$(MAC_ENV)" EXTRA="$(EXTRA)" + +luasocket: + @cd luasocket && $(MAKE) + +$(CMOD): $(EXTRA) $(OBJS) + $(CCLD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + cd luasocket && $(MAKE) clean + rm -f $(OBJS) $(CMOD) + +options.o: options.h options.c +ec.o: ec.c ec.h +x509.o: x509.c x509.h compat.h +context.o: context.c context.h ec.h compat.h options.h +ssl.o: ssl.c ssl.h context.h x509.h compat.h +config.o: config.c ec.h options.h compat.h diff --git a/vendor/luasec/src/compat.h b/vendor/luasec/src/compat.h new file mode 100644 index 00000000..9f97e2a6 --- /dev/null +++ b/vendor/luasec/src/compat.h @@ -0,0 +1,63 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#ifndef LSEC_COMPAT_H +#define LSEC_COMPAT_H + +#include + +//------------------------------------------------------------------------------ + +#if defined(_WIN32) +#define LSEC_API __declspec(dllexport) +#else +#define LSEC_API extern +#endif + +//------------------------------------------------------------------------------ + +#if (LUA_VERSION_NUM == 501) + +#define luaL_testudata(L, ud, tname) lsec_testudata(L, ud, tname) +#define setfuncs(L, R) luaL_register(L, NULL, R) +#define lua_rawlen(L, i) lua_objlen(L, i) + +#ifndef luaL_newlib +#define luaL_newlib(L, R) do { lua_newtable(L); luaL_register(L, NULL, R); } while(0) +#endif + +#else +#define setfuncs(L, R) luaL_setfuncs(L, R, 0) +#endif + +//------------------------------------------------------------------------------ + +#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x1010000fL)) +#define LSEC_ENABLE_DANE +#endif + +//------------------------------------------------------------------------------ + +#if !((defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL)) || (OPENSSL_VERSION_NUMBER < 0x1010000fL)) +#define LSEC_API_OPENSSL_1_1_0 +#endif + +//------------------------------------------------------------------------------ + +#if !defined(LIBRESSL_VERSION_NUMBER) && ((OPENSSL_VERSION_NUMBER & 0xFFFFF000L) == 0x10101000L || (OPENSSL_VERSION_NUMBER & 0xFFFFF000L) == 0x30000000L) +#define LSEC_OPENSSL_ERRNO_BUG +#endif + +//------------------------------------------------------------------------------ + +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_NO_PSK) +#define LSEC_ENABLE_PSK +#endif + +//------------------------------------------------------------------------------ + +#endif diff --git a/vendor/luasec/src/config.c b/vendor/luasec/src/config.c new file mode 100644 index 00000000..5f0e2c60 --- /dev/null +++ b/vendor/luasec/src/config.c @@ -0,0 +1,108 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include "compat.h" +#include "options.h" +#include "ec.h" + +/** + * Registre the module. + */ +LSEC_API int luaopen_ssl_config(lua_State *L) +{ + lsec_ssl_option_t *opt; + + lua_newtable(L); + + // Options + lua_pushstring(L, "options"); + lua_newtable(L); + for (opt = lsec_get_ssl_options(); opt->name; opt++) { + lua_pushstring(L, opt->name); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + } + lua_rawset(L, -3); + + // Protocols + lua_pushstring(L, "protocols"); + lua_newtable(L); + + lua_pushstring(L, "tlsv1"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushstring(L, "tlsv1_1"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushstring(L, "tlsv1_2"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#ifdef TLS1_3_VERSION + lua_pushstring(L, "tlsv1_3"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + + lua_rawset(L, -3); + + // Algorithms + lua_pushstring(L, "algorithms"); + lua_newtable(L); + +#ifndef OPENSSL_NO_EC + lua_pushstring(L, "ec"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + lua_rawset(L, -3); + + // Curves + lua_pushstring(L, "curves"); + lsec_get_curves(L); + lua_rawset(L, -3); + + // Capabilities + lua_pushstring(L, "capabilities"); + lua_newtable(L); + + // ALPN + lua_pushstring(L, "alpn"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + +#ifdef LSEC_ENABLE_PSK + lua_pushstring(L, "psk"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + +#ifdef LSEC_ENABLE_DANE + // DANE + lua_pushstring(L, "dane"); +#ifdef DANE_FLAG_NO_DANE_EE_NAMECHECKS + lua_createtable(L, 0, 1); + lua_pushstring(L, "no_ee_namechecks"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#else + lua_pushboolean(L, 1); +#endif + lua_rawset(L, -3); +#endif + +#ifndef OPENSSL_NO_EC + lua_pushstring(L, "curves_list"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushstring(L, "ecdh_auto"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + lua_rawset(L, -3); + + return 1; +} diff --git a/vendor/luasec/src/context.c b/vendor/luasec/src/context.c new file mode 100644 index 00000000..881ebb90 --- /dev/null +++ b/vendor/luasec/src/context.c @@ -0,0 +1,1099 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2014-2023 Kim Alvefur, Paul Aurich, Tobias Markmann, Matthew Wild + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +#if defined(WIN32) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "compat.h" +#include "context.h" +#include "options.h" + +#ifndef OPENSSL_NO_EC +#include +#include "ec.h" +#endif + +/*--------------------------- Auxiliary Functions ----------------------------*/ + +/** + * Return the context. + */ +static p_context checkctx(lua_State *L, int idx) +{ + return (p_context)luaL_checkudata(L, idx, "SSL:Context"); +} + +static p_context testctx(lua_State *L, int idx) +{ + return (p_context)luaL_testudata(L, idx, "SSL:Context"); +} + +/** + * Prepare the SSL options flag. + */ +static int set_option_flag(const char *opt, unsigned long *flag) +{ + lsec_ssl_option_t *p; + for (p = lsec_get_ssl_options(); p->name; p++) { + if (!strcmp(opt, p->name)) { + *flag |= p->code; + return 1; + } + } + return 0; +} + +#ifndef LSEC_API_OPENSSL_1_1_0 +/** + * Find the protocol. + */ +static const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax) +{ + (void)vmin; + (void)vmax; + if (!strcmp(method, "any")) return SSLv23_method(); + if (!strcmp(method, "sslv23")) return SSLv23_method(); // deprecated + if (!strcmp(method, "tlsv1")) return TLSv1_method(); + if (!strcmp(method, "tlsv1_1")) return TLSv1_1_method(); + if (!strcmp(method, "tlsv1_2")) return TLSv1_2_method(); + return NULL; +} + +#else + +/** + * Find the protocol. + */ +static const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax) +{ + if (!strcmp(method, "any") || !strcmp(method, "sslv23")) { // 'sslv23' is deprecated + *vmin = 0; + *vmax = 0; + return TLS_method(); + } + else if (!strcmp(method, "tlsv1")) { + *vmin = TLS1_VERSION; + *vmax = TLS1_VERSION; + return TLS_method(); + } + else if (!strcmp(method, "tlsv1_1")) { + *vmin = TLS1_1_VERSION; + *vmax = TLS1_1_VERSION; + return TLS_method(); + } + else if (!strcmp(method, "tlsv1_2")) { + *vmin = TLS1_2_VERSION; + *vmax = TLS1_2_VERSION; + return TLS_method(); + } +#if defined(TLS1_3_VERSION) + else if (!strcmp(method, "tlsv1_3")) { + *vmin = TLS1_3_VERSION; + *vmax = TLS1_3_VERSION; + return TLS_method(); + } +#endif + return NULL; +} +#endif + +/** + * Prepare the SSL handshake verify flag. + */ +static int set_verify_flag(const char *str, int *flag) +{ + if (!strcmp(str, "none")) { + *flag |= SSL_VERIFY_NONE; + return 1; + } + if (!strcmp(str, "peer")) { + *flag |= SSL_VERIFY_PEER; + return 1; + } + if (!strcmp(str, "client_once")) { + *flag |= SSL_VERIFY_CLIENT_ONCE; + return 1; + } + if (!strcmp(str, "fail_if_no_peer_cert")) { + *flag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + return 1; + } + return 0; +} + +/** + * Password callback for reading the private key. + */ +static int passwd_cb(char *buf, int size, int flag, void *udata) +{ + lua_State *L = (lua_State*)udata; + switch (lua_type(L, 3)) { + case LUA_TFUNCTION: + lua_pushvalue(L, 3); + lua_call(L, 0, 1); + if (lua_type(L, -1) != LUA_TSTRING) { + lua_pop(L, 1); /* Remove the result from the stack */ + return 0; + } + /* fallback */ + case LUA_TSTRING: + strncpy(buf, lua_tostring(L, -1), size); + lua_pop(L, 1); /* Remove the result from the stack */ + buf[size-1] = '\0'; + return (int)strlen(buf); + } + return 0; +} + +/** + * Add an error related to a depth certificate of the chain. + */ +static void add_cert_error(lua_State *L, SSL *ssl, int err, int depth) +{ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ssl); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + /* Create an error table for this connection */ + lua_newtable(L); + lua_pushlightuserdata(L, (void*)ssl); + lua_pushvalue(L, -2); /* keep the table on stack */ + lua_settable(L, -4); + } + lua_rawgeti(L, -1, depth+1); + /* If the table doesn't exist, create it */ + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove 'nil' from stack */ + lua_newtable(L); + lua_pushvalue(L, -1); /* keep the table on stack */ + lua_rawseti(L, -3, depth+1); + } + lua_pushstring(L, X509_verify_cert_error_string(err)); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + /* Clear the stack */ + lua_pop(L, 3); +} + +/** + * Call Lua user function to get the DH key. + */ +static DH *dhparam_cb(SSL *ssl, int is_export, int keylength) +{ + BIO *bio; + lua_State *L; + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); + + L = pctx->L; + + /* Get the callback */ + luaL_getmetatable(L, "SSL:DH:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_gettable(L, -2); + + /* Invoke the callback */ + lua_pushboolean(L, is_export); + lua_pushnumber(L, keylength); + lua_call(L, 2, 1); + + /* Load parameters from returned value */ + if (lua_type(L, -1) != LUA_TSTRING) { + lua_pop(L, 2); /* Remove values from stack */ + return NULL; + } + + bio = BIO_new_mem_buf((void*)lua_tostring(L, -1), lua_rawlen(L, -1)); + if (bio) { + pctx->dh_param = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + lua_pop(L, 2); /* Remove values from stack */ + return pctx->dh_param; +} + +/** + * Set the "ignore purpose" before to start verifing the certificate chain. + */ +static int cert_verify_cb(X509_STORE_CTX *x509_ctx, void *ptr) +{ + int verify; + lua_State *L; + SSL_CTX *ctx = (SSL_CTX*)ptr; + p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); + + L = pctx->L; + + /* Get verify flags */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_gettable(L, -2); + verify = (int)lua_tonumber(L, -1); + + lua_pop(L, 2); /* Remove values from stack */ + + if (verify & LSEC_VERIFY_IGNORE_PURPOSE) { + /* Set parameters to ignore the server purpose */ + X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(x509_ctx); + if (param) { + X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_SSL_SERVER); + X509_VERIFY_PARAM_set_trust(param, X509_TRUST_SSL_SERVER); + } + } + /* Call OpenSSL standard verification function */ + return X509_verify_cert(x509_ctx); +} + +/** + * This callback implements the "continue on error" flag and log the errors. + */ +static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + int err; + int verify; + SSL *ssl; + SSL_CTX *ctx; + p_context pctx; + lua_State *L; + + /* Short-circuit optimization */ + if (preverify_ok) + return 1; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ctx = SSL_get_SSL_CTX(ssl); + pctx = (p_context)SSL_CTX_get_app_data(ctx); + L = pctx->L; + + /* Get verify flags */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_gettable(L, -2); + verify = (int)lua_tonumber(L, -1); + + lua_pop(L, 2); /* Remove values from stack */ + + err = X509_STORE_CTX_get_error(x509_ctx); + if (err != X509_V_OK) + add_cert_error(L, ssl, err, X509_STORE_CTX_get_error_depth(x509_ctx)); + + return (verify & LSEC_VERIFY_CONTINUE ? 1 : preverify_ok); +} + +/*------------------------------ Lua Functions -------------------------------*/ + +/** + * Create a SSL context. + */ +static int create(lua_State *L) +{ + p_context ctx; + const char *str_method; + const SSL_METHOD *method; + int vmin, vmax; + + str_method = luaL_checkstring(L, 1); + method = str2method(str_method, &vmin, &vmax); + if (!method) { + lua_pushnil(L); + lua_pushfstring(L, "invalid protocol (%s)", str_method); + return 2; + } + ctx = (p_context) lua_newuserdata(L, sizeof(t_context)); + if (!ctx) { + lua_pushnil(L); + lua_pushstring(L, "error creating context"); + return 2; + } + memset(ctx, 0, sizeof(t_context)); + ctx->context = SSL_CTX_new(method); + if (!ctx->context) { + lua_pushnil(L); + lua_pushfstring(L, "error creating context (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } +#ifdef LSEC_API_OPENSSL_1_1_0 + SSL_CTX_set_min_proto_version(ctx->context, vmin); + SSL_CTX_set_max_proto_version(ctx->context, vmax); +#endif + ctx->mode = LSEC_MODE_INVALID; + ctx->L = L; + luaL_getmetatable(L, "SSL:Context"); + lua_setmetatable(L, -2); + + /* No session support */ + SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF); + /* Link LuaSec context with the OpenSSL context */ + SSL_CTX_set_app_data(ctx->context, ctx); + + return 1; +} + +/** + * Load the trusting certificates. + */ +static int load_locations(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *cafile = luaL_optstring(L, 2, NULL); + const char *capath = luaL_optstring(L, 3, NULL); + if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error loading CA locations (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +/** + * Load the certificate file. + */ +static int load_cert(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *filename = luaL_checkstring(L, 2); + if (SSL_CTX_use_certificate_chain_file(ctx, filename) != 1) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error loading certificate (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +/** + * Load the key file -- only in PEM format. + */ +static int load_key(lua_State *L) +{ + int ret = 1; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *filename = luaL_checkstring(L, 2); + switch (lua_type(L, 3)) { + case LUA_TSTRING: + case LUA_TFUNCTION: + SSL_CTX_set_default_passwd_cb(ctx, passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, L); + /* fallback */ + case LUA_TNIL: + if (SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM) == 1) + lua_pushboolean(L, 1); + else { + ret = 2; + lua_pushboolean(L, 0); + lua_pushfstring(L, "error loading private key (%s)", + ERR_reason_error_string(ERR_get_error())); + } + SSL_CTX_set_default_passwd_cb(ctx, NULL); + SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL); + break; + default: + lua_pushstring(L, "invalid callback value"); + lua_error(L); + } + return ret; +} + +/** + * Check that the certificate public key matches the private key + */ + +static int check_key(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + lua_pushboolean(L, SSL_CTX_check_private_key(ctx)); + return 1; +} + +/** + * Set the cipher list. + */ +static int set_cipher(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *list = luaL_checkstring(L, 2); + if (SSL_CTX_set_cipher_list(ctx, list) != 1) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error setting cipher list (%s)", ERR_reason_error_string(ERR_get_error())); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set the cipher suites. + */ +static int set_ciphersuites(lua_State *L) +{ +#if defined(TLS1_3_VERSION) + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *list = luaL_checkstring(L, 2); + if (SSL_CTX_set_ciphersuites(ctx, list) != 1) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error setting cipher list (%s)", ERR_reason_error_string(ERR_get_error())); + return 2; + } +#endif + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set the depth for certificate checking. + */ +static int set_depth(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + SSL_CTX_set_verify_depth(ctx, (int)luaL_checkinteger(L, 2)); + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set the handshake verify options. + */ +static int set_verify(lua_State *L) +{ + int i; + const char *str; + int flag = 0; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + int max = lua_gettop(L); + for (i = 2; i <= max; i++) { + str = luaL_checkstring(L, i); + if (!set_verify_flag(str, &flag)) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "invalid verify option (%s)", str); + return 2; + } + } + if (flag) SSL_CTX_set_verify(ctx, flag, NULL); + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set the protocol options. + */ +static int set_options(lua_State *L) +{ + int i; + const char *str; + unsigned long flag = 0L; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + int max = lua_gettop(L); + /* any option? */ + if (max > 1) { + for (i = 2; i <= max; i++) { + str = luaL_checkstring(L, i); + if (!set_option_flag(str, &flag)) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "invalid option (%s)", str); + return 2; + } + } + SSL_CTX_set_options(ctx, flag); + } + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set the context mode. + */ +static int set_mode(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + const char *str = luaL_checkstring(L, 2); + if (!strcmp("server", str)) { + ctx->mode = LSEC_MODE_SERVER; + lua_pushboolean(L, 1); + return 1; + } + if (!strcmp("client", str)) { + ctx->mode = LSEC_MODE_CLIENT; + lua_pushboolean(L, 1); + return 1; + } + lua_pushboolean(L, 0); + lua_pushfstring(L, "invalid mode (%s)", str); + return 1; +} + +/** + * Configure DH parameters. + */ +static int set_dhparam(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + SSL_CTX_set_tmp_dh_callback(ctx, dhparam_cb); + + /* Save callback */ + luaL_getmetatable(L, "SSL:DH:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + return 0; +} + +#if !defined(OPENSSL_NO_EC) +/** + * Set elliptic curve. + */ +static int set_curve(lua_State *L) +{ + long ret; + EC_KEY *key = NULL; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *str = luaL_checkstring(L, 2); + + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); + + key = lsec_find_ec_key(L, str); + + if (!key) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "elliptic curve '%s' not supported", str); + return 2; + } + + ret = SSL_CTX_set_tmp_ecdh(ctx, key); + /* SSL_CTX_set_tmp_ecdh takes its own reference */ + EC_KEY_free(key); + + if (!ret) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error setting elliptic curve (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + +/** + * Set elliptic curves list. + */ +static int set_curves_list(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *str = luaL_checkstring(L, 2); + + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); + + if (SSL_CTX_set1_curves_list(ctx, str) != 1) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "unknown elliptic curve in \"%s\"", str); + return 2; + } + +#if defined(LIBRESSL_VERSION_NUMBER) || !defined(LSEC_API_OPENSSL_1_1_0) + (void)SSL_CTX_set_ecdh_auto(ctx, 1); +#endif + + lua_pushboolean(L, 1); + return 1; +} +#endif + +/** + * Set the protocols a client should send for ALPN. + */ +static int set_alpn(lua_State *L) +{ + long ret; + size_t len; + p_context ctx = checkctx(L, 1); + const char *str = luaL_checklstring(L, 2, &len); + + ret = SSL_CTX_set_alpn_protos(ctx->context, (const unsigned char*)str, len); + if (ret) { + lua_pushboolean(L, 0); + lua_pushfstring(L, "error setting ALPN (%s)", ERR_reason_error_string(ERR_get_error())); + return 2; + } + lua_pushboolean(L, 1); + return 1; +} + +/** + * This standard callback calls the server's callback in Lua sapce. + * The server has to return a list in wire-format strings. + * This function uses a helper function to match server and client lists. + */ +static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) +{ + int ret; + size_t server_len; + const char *server; + p_context ctx = (p_context)arg; + lua_State *L = ctx->L; + + luaL_getmetatable(L, "SSL:ALPN:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_gettable(L, -2); + + lua_pushlstring(L, (const char*)in, inlen); + + lua_call(L, 1, 1); + + if (!lua_isstring(L, -1)) { + lua_pop(L, 2); + return SSL_TLSEXT_ERR_NOACK; + } + + // Protocol list from server in wire-format string + server = luaL_checklstring(L, -1, &server_len); + ret = SSL_select_next_proto((unsigned char**)out, outlen, (const unsigned char*)server, + server_len, in, inlen); + if (ret != OPENSSL_NPN_NEGOTIATED) { + lua_pop(L, 2); + return SSL_TLSEXT_ERR_NOACK; + } + + // Copy the result because lua_pop() can collect the pointer + ctx->alpn = malloc(*outlen); + memcpy(ctx->alpn, (void*)*out, *outlen); + *out = (const unsigned char*)ctx->alpn; + + lua_pop(L, 2); + + return SSL_TLSEXT_ERR_OK; +} + +/** + * Set a callback a server can use to select the next protocol with ALPN. + */ +static int set_alpn_cb(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + + luaL_getmetatable(L, "SSL:ALPN:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + SSL_CTX_set_alpn_select_cb(ctx->context, alpn_cb, ctx); + + lua_pushboolean(L, 1); + return 1; +} + +#if defined(LSEC_ENABLE_PSK) +/** + * Callback to select the PSK. + */ +static unsigned int server_psk_cb(SSL *ssl, const char *identity, unsigned char *psk, + unsigned int max_psk_len) +{ + size_t psk_len; + const char *ret_psk; + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); + lua_State *L = pctx->L; + + luaL_getmetatable(L, "SSL:PSK:Registry"); + lua_pushlightuserdata(L, (void*)pctx->context); + lua_gettable(L, -2); + + lua_pushstring(L, identity); + lua_pushinteger(L, max_psk_len); + + lua_call(L, 2, 1); + + if (!lua_isstring(L, -1)) { + lua_pop(L, 2); + return 0; + } + + ret_psk = lua_tolstring(L, -1, &psk_len); + + if (psk_len == 0 || psk_len > max_psk_len) + psk_len = 0; + else + memcpy(psk, ret_psk, psk_len); + + lua_pop(L, 2); + + return psk_len; +} + +/** + * Set a PSK callback for server. + */ +static int set_server_psk_cb(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + + luaL_getmetatable(L, "SSL:PSK:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + SSL_CTX_set_psk_server_callback(ctx->context, server_psk_cb); + + lua_pushboolean(L, 1); + return 1; +} + +/* + * Set the PSK indentity hint. + */ +static int set_psk_identity_hint(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + const char *hint = luaL_checkstring(L, 2); + int ret = SSL_CTX_use_psk_identity_hint(ctx->context, hint); + lua_pushboolean(L, ret); + return 1; +} + +/* + * Client callback to PSK. + */ +static unsigned int client_psk_cb(SSL *ssl, const char *hint, char *identity, + unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) +{ + size_t psk_len; + size_t identity_len; + const char *ret_psk; + const char *ret_identity; + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); + lua_State *L = pctx->L; + + luaL_getmetatable(L, "SSL:PSK:Registry"); + lua_pushlightuserdata(L, (void*)pctx->context); + lua_gettable(L, -2); + + if (hint) + lua_pushstring(L, hint); + else + lua_pushnil(L); + + // Leave space to '\0' + lua_pushinteger(L, max_identity_len-1); + lua_pushinteger(L, max_psk_len); + + lua_call(L, 3, 2); + + if (!lua_isstring(L, -1) || !lua_isstring(L, -2)) { + lua_pop(L, 3); + return 0; + } + + ret_identity = lua_tolstring(L, -2, &identity_len); + ret_psk = lua_tolstring(L, -1, &psk_len); + + if (identity_len >= max_identity_len || psk_len > max_psk_len) + psk_len = 0; + else { + memcpy(identity, ret_identity, identity_len); + identity[identity_len] = 0; + memcpy(psk, ret_psk, psk_len); + } + + lua_pop(L, 3); + + return psk_len; +} + +/** + * Set a PSK callback for client. + */ +static int set_client_psk_cb(lua_State *L) { + p_context ctx = checkctx(L, 1); + + luaL_getmetatable(L, "SSL:PSK:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + SSL_CTX_set_psk_client_callback(ctx->context, client_psk_cb); + + lua_pushboolean(L, 1); + return 1; +} +#endif + +#if defined(LSEC_ENABLE_DANE) +/* + * DANE + */ +static int dane_options[] = { + /* TODO move into options.c + * however this symbol is not from openssl/ssl.h but rather from + * openssl/x509_vfy.h + * */ +#ifdef DANE_FLAG_NO_DANE_EE_NAMECHECKS + DANE_FLAG_NO_DANE_EE_NAMECHECKS, +#endif + 0 +}; +static const char *dane_option_names[] = { +#ifdef DANE_FLAG_NO_DANE_EE_NAMECHECKS + "no_ee_namechecks", +#endif + NULL +}; + +static int set_dane(lua_State *L) +{ + int ret, i; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + ret = SSL_CTX_dane_enable(ctx); + for (i = 2; ret > 0 && i <= lua_gettop(L); i++) { + ret = SSL_CTX_dane_set_flags(ctx, dane_options[luaL_checkoption(L, i, NULL, dane_option_names)]); + } + lua_pushboolean(L, (ret > 0)); + return 1; +} +#endif + +/** + * Package functions + */ +static luaL_Reg funcs[] = { + {"create", create}, + {"locations", load_locations}, + {"loadcert", load_cert}, + {"loadkey", load_key}, + {"checkkey", check_key}, + {"setalpn", set_alpn}, + {"setalpncb", set_alpn_cb}, + {"setcipher", set_cipher}, + {"setciphersuites", set_ciphersuites}, + {"setdepth", set_depth}, + {"setdhparam", set_dhparam}, + {"setverify", set_verify}, + {"setoptions", set_options}, +#if defined(LSEC_ENABLE_PSK) + {"setpskhint", set_psk_identity_hint}, + {"setserverpskcb", set_server_psk_cb}, + {"setclientpskcb", set_client_psk_cb}, +#endif + {"setmode", set_mode}, +#if !defined(OPENSSL_NO_EC) + {"setcurve", set_curve}, + {"setcurveslist", set_curves_list}, +#endif +#if defined(LSEC_ENABLE_DANE) + {"setdane", set_dane}, +#endif + {NULL, NULL} +}; + +/*-------------------------------- Metamethods -------------------------------*/ + +/** + * Collect SSL context -- GC metamethod. + */ +static int meth_destroy(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + if (ctx->context) { + /* Clear registries */ + luaL_getmetatable(L, "SSL:DH:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushnil(L); + lua_settable(L, -3); + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushnil(L); + lua_settable(L, -3); + luaL_getmetatable(L, "SSL:ALPN:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushnil(L); + lua_settable(L, -3); + luaL_getmetatable(L, "SSL:PSK:Registry"); + lua_pushlightuserdata(L, (void*)ctx->context); + lua_pushnil(L); + lua_settable(L, -3); + + SSL_CTX_free(ctx->context); + ctx->context = NULL; + } + return 0; +} + +/** + * Object information -- tostring metamethod. + */ +static int meth_tostring(lua_State *L) +{ + p_context ctx = checkctx(L, 1); + lua_pushfstring(L, "SSL context: %p", ctx); + return 1; +} + +/** + * Set extra flags for handshake verification. + */ +static int meth_set_verify_ext(lua_State *L) +{ + int i; + const char *str; + int crl_flag = 0; + int lsec_flag = 0; + SSL_CTX *ctx = lsec_checkcontext(L, 1); + int max = lua_gettop(L); + for (i = 2; i <= max; i++) { + str = luaL_checkstring(L, i); + if (!strcmp(str, "lsec_continue")) { + lsec_flag |= LSEC_VERIFY_CONTINUE; + } else if (!strcmp(str, "lsec_ignore_purpose")) { + lsec_flag |= LSEC_VERIFY_IGNORE_PURPOSE; + } else if (!strcmp(str, "crl_check")) { + crl_flag |= X509_V_FLAG_CRL_CHECK; + } else if (!strcmp(str, "crl_check_chain")) { + crl_flag |= X509_V_FLAG_CRL_CHECK_ALL; + } else { + lua_pushboolean(L, 0); + lua_pushfstring(L, "invalid verify option (%s)", str); + return 2; + } + } + /* Set callback? */ + if (lsec_flag) { + SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), verify_cb); + SSL_CTX_set_cert_verify_callback(ctx, cert_verify_cb, (void*)ctx); + /* Save flag */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_pushnumber(L, lsec_flag); + lua_settable(L, -3); + } else { + SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), NULL); + SSL_CTX_set_cert_verify_callback(ctx, NULL, NULL); + /* Remove flag */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_pushnil(L); + lua_settable(L, -3); + } + + /* X509 flag */ + X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), crl_flag); + + /* Ok */ + lua_pushboolean(L, 1); + return 1; +} + +/** + * Context metamethods. + */ +static luaL_Reg meta[] = { + {"__close", meth_destroy}, + {"__gc", meth_destroy}, + {"__tostring", meth_tostring}, + {NULL, NULL} +}; + +/** + * Index metamethods. + */ +static luaL_Reg meta_index[] = { + {"setverifyext", meth_set_verify_ext}, + {NULL, NULL} +}; + + +/*----------------------------- Public Functions ---------------------------*/ + +/** + * Retrieve the SSL context from the Lua stack. + */ +SSL_CTX* lsec_checkcontext(lua_State *L, int idx) +{ + p_context ctx = checkctx(L, idx); + return ctx->context; +} + +SSL_CTX* lsec_testcontext(lua_State *L, int idx) +{ + p_context ctx = testctx(L, idx); + return (ctx) ? ctx->context : NULL; +} + +/** + * Retrieve the mode from the context in the Lua stack. + */ +int lsec_getmode(lua_State *L, int idx) +{ + p_context ctx = checkctx(L, idx); + return ctx->mode; +} + +/*-- Compat - Lua 5.1 --*/ +#if (LUA_VERSION_NUM == 501) + +void *lsec_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + +#endif + +/*------------------------------ Initialization ------------------------------*/ + +/** + * Registre the module. + */ +LSEC_API int luaopen_ssl_context(lua_State *L) +{ + luaL_newmetatable(L, "SSL:DH:Registry"); /* Keep all DH callbacks */ + luaL_newmetatable(L, "SSL:ALPN:Registry"); /* Keep all ALPN callbacks */ + luaL_newmetatable(L, "SSL:PSK:Registry"); /* Keep all PSK callbacks */ + luaL_newmetatable(L, "SSL:Verify:Registry"); /* Keep all verify flags */ + luaL_newmetatable(L, "SSL:Context"); + setfuncs(L, meta); + + /* Create __index metamethods for context */ + luaL_newlib(L, meta_index); + lua_setfield(L, -2, "__index"); + + lsec_load_curves(L); + + /* Return the module */ + luaL_newlib(L, funcs); + + return 1; +} diff --git a/vendor/luasec/src/context.h b/vendor/luasec/src/context.h new file mode 100644 index 00000000..dd6bd098 --- /dev/null +++ b/vendor/luasec/src/context.h @@ -0,0 +1,47 @@ +#ifndef LSEC_CONTEXT_H +#define LSEC_CONTEXT_H + +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include +#include + +#include "compat.h" + +#define LSEC_MODE_INVALID 0 +#define LSEC_MODE_SERVER 1 +#define LSEC_MODE_CLIENT 2 + +#define LSEC_VERIFY_CONTINUE 1 +#define LSEC_VERIFY_IGNORE_PURPOSE 2 + +typedef struct t_context_ { + SSL_CTX *context; + lua_State *L; + DH *dh_param; + void *alpn; + int mode; +} t_context; +typedef t_context* p_context; + +/* Retrieve the SSL context from the Lua stack */ +SSL_CTX *lsec_checkcontext(lua_State *L, int idx); +SSL_CTX *lsec_testcontext(lua_State *L, int idx); + +/* Retrieve the mode from the context in the Lua stack */ +int lsec_getmode(lua_State *L, int idx); + +/* Registre the module. */ +LSEC_API int luaopen_ssl_context(lua_State *L); + +/* Compat - Lua 5.1 */ +#if (LUA_VERSION_NUM == 501) +void *lsec_testudata (lua_State *L, int ud, const char *tname); +#endif + +#endif diff --git a/vendor/luasec/src/ec.c b/vendor/luasec/src/ec.c new file mode 100644 index 00000000..c7025a55 --- /dev/null +++ b/vendor/luasec/src/ec.c @@ -0,0 +1,116 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +#include "ec.h" + +#ifndef OPENSSL_NO_EC + +EC_KEY *lsec_find_ec_key(lua_State *L, const char *str) +{ + int nid; + lua_pushstring(L, "SSL:EC:CURVES"); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushstring(L, str); + lua_rawget(L, -2); + + if (!lua_isnumber(L, -1)) + return NULL; + + nid = (int)lua_tonumber(L, -1); + return EC_KEY_new_by_curve_name(nid); +} + +void lsec_load_curves(lua_State *L) +{ + size_t i; + size_t size; + const char *name; + EC_builtin_curve *curves = NULL; + + lua_pushstring(L, "SSL:EC:CURVES"); + lua_newtable(L); + + size = EC_get_builtin_curves(NULL, 0); + if (size > 0) { + curves = (EC_builtin_curve*)malloc(sizeof(EC_builtin_curve) * size); + EC_get_builtin_curves(curves, size); + for (i = 0; i < size; i++) { + name = OBJ_nid2sn(curves[i].nid); + if (name != NULL) { + lua_pushstring(L, name); + lua_pushnumber(L, curves[i].nid); + lua_rawset(L, -3); + } + switch (curves[i].nid) { + case NID_X9_62_prime256v1: + lua_pushstring(L, "P-256"); + lua_pushnumber(L, curves[i].nid); + lua_rawset(L, -3); + break; + case NID_secp384r1: + lua_pushstring(L, "P-384"); + lua_pushnumber(L, curves[i].nid); + lua_rawset(L, -3); + break; + case NID_secp521r1: + lua_pushstring(L, "P-521"); + lua_pushnumber(L, curves[i].nid); + lua_rawset(L, -3); + break; + } + } + free(curves); + } + + /* These are special so are manually added here */ +#ifdef NID_X25519 + lua_pushstring(L, "X25519"); + lua_pushnumber(L, NID_X25519); + lua_rawset(L, -3); +#endif + +#ifdef NID_X448 + lua_pushstring(L, "X448"); + lua_pushnumber(L, NID_X448); + lua_rawset(L, -3); +#endif + + lua_rawset(L, LUA_REGISTRYINDEX); +} + +void lsec_get_curves(lua_State *L) +{ + lua_newtable(L); + + lua_pushstring(L, "SSL:EC:CURVES"); + lua_rawget(L, LUA_REGISTRYINDEX); + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + lua_pop(L, 1); + lua_pushvalue(L, -1); + lua_pushboolean(L, 1); + lua_rawset(L, -5); + } + lua_pop(L, 1); +} + +#else + +void lsec_load_curves(lua_State *L) +{ + // do nothing +} + +void lsec_get_curves(lua_State *L) +{ + lua_newtable(L); +} + +#endif diff --git a/vendor/luasec/src/ec.h b/vendor/luasec/src/ec.h new file mode 100644 index 00000000..16d29360 --- /dev/null +++ b/vendor/luasec/src/ec.h @@ -0,0 +1,22 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#ifndef LSEC_EC_H +#define LSEC_EC_H + +#include + +#ifndef OPENSSL_NO_EC +#include + +EC_KEY *lsec_find_ec_key(lua_State *L, const char *str); +#endif + +void lsec_get_curves(lua_State *L); +void lsec_load_curves(lua_State *L); + +#endif diff --git a/vendor/luasec/src/https.lua b/vendor/luasec/src/https.lua new file mode 100644 index 00000000..85cef361 --- /dev/null +++ b/vendor/luasec/src/https.lua @@ -0,0 +1,147 @@ +---------------------------------------------------------------------------- +-- LuaSec 1.3.2 +-- +-- Copyright (C) 2009-2023 PUC-Rio +-- +-- Author: Pablo Musa +-- Author: Tomas Guisasola +--------------------------------------------------------------------------- + +local socket = require("socket") +local ssl = require("ssl") +local ltn12 = require("ltn12") +local http = require("socket.http") +local url = require("socket.url") + +local try = socket.try + +-- +-- Module +-- +local _M = { + _VERSION = "1.3.2", + _COPYRIGHT = "LuaSec 1.3.2 - Copyright (C) 2009-2023 PUC-Rio", + PORT = 443, + TIMEOUT = 60 +} + +-- TLS configuration +local cfg = { + protocol = "any", + options = {"all", "no_sslv2", "no_sslv3", "no_tlsv1"}, + verify = "none", +} + +-------------------------------------------------------------------- +-- Auxiliar Functions +-------------------------------------------------------------------- + +-- Insert default HTTPS port. +local function default_https_port(u) + return url.build(url.parse(u, {port = _M.PORT})) +end + +-- Convert an URL to a table according to Luasocket needs. +local function urlstring_totable(url, body, result_table) + url = { + url = default_https_port(url), + method = body and "POST" or "GET", + sink = ltn12.sink.table(result_table) + } + if body then + url.source = ltn12.source.string(body) + url.headers = { + ["content-length"] = #body, + ["content-type"] = "application/x-www-form-urlencoded", + } + end + return url +end + +-- Forward calls to the real connection object. +local function reg(conn) + local mt = getmetatable(conn.sock).__index + for name, method in pairs(mt) do + if type(method) == "function" then + conn[name] = function (self, ...) + return method(self.sock, ...) + end + end + end +end + +-- Return a function which performs the SSL/TLS connection. +local function tcp(params) + params = params or {} + -- Default settings + for k, v in pairs(cfg) do + params[k] = params[k] or v + end + -- Force client mode + params.mode = "client" + -- 'create' function for LuaSocket + return function () + local conn = {} + conn.sock = try(socket.tcp()) + local st = getmetatable(conn.sock).__index.settimeout + function conn:settimeout(...) + return st(self.sock, _M.TIMEOUT) + end + -- Replace TCP's connection function + function conn:connect(host, port) + try(self.sock:connect(host, port)) + self.sock = try(ssl.wrap(self.sock, params)) + self.sock:sni(host) + self.sock:settimeout(_M.TIMEOUT) + try(self.sock:dohandshake()) + reg(self) + return 1 + end + return conn + end +end + +-------------------------------------------------------------------- +-- Main Function +-------------------------------------------------------------------- + +-- Make a HTTP request over secure connection. This function receives +-- the same parameters of LuaSocket's HTTP module (except 'proxy' and +-- 'redirect') plus LuaSec parameters. +-- +-- @param url mandatory (string or table) +-- @param body optional (string) +-- @return (string if url == string or 1), code, headers, status +-- +local function request(url, body) + local result_table = {} + local stringrequest = type(url) == "string" + if stringrequest then + url = urlstring_totable(url, body, result_table) + else + url.url = default_https_port(url.url) + end + if http.PROXY or url.proxy then + return nil, "proxy not supported" + elseif url.redirect then + return nil, "redirect not supported" + elseif url.create then + return nil, "create function not permitted" + end + -- New 'create' function to establish a secure connection + url.create = tcp(url) + local res, code, headers, status = http.request(url) + if res and stringrequest then + return table.concat(result_table), code, headers, status + end + return res, code, headers, status +end + +-------------------------------------------------------------------------------- +-- Export module +-- + +_M.request = request +_M.tcp = tcp + +return _M diff --git a/vendor/luasec/src/luasocket/LICENSE b/vendor/luasec/src/luasocket/LICENSE new file mode 100644 index 00000000..eadb747b --- /dev/null +++ b/vendor/luasec/src/luasocket/LICENSE @@ -0,0 +1,21 @@ +LuaSocket 3.0-RC1 license +Copyright (C) 2004-2013 Diego Nehab + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/luasec/src/luasocket/Makefile b/vendor/luasec/src/luasocket/Makefile new file mode 100644 index 00000000..b700fb63 --- /dev/null +++ b/vendor/luasec/src/luasocket/Makefile @@ -0,0 +1,26 @@ +OBJS= \ + io.o \ + buffer.o \ + timeout.o \ + usocket.o + +CC ?= cc +CFLAGS += $(MYCFLAGS) -DLUASOCKET_DEBUG +AR ?= ar +RANLIB ?= ranlib + +.PHONY: all clean + +all: libluasocket.a + +libluasocket.a: $(OBJS) + $(AR) rcu $@ $(OBJS) + $(RANLIB) $@ + +clean: + rm -f $(OBJS) libluasocket.a + +buffer.o: buffer.c buffer.h io.h timeout.h +io.o: io.c io.h timeout.h +timeout.o: timeout.c timeout.h +usocket.o: usocket.c socket.h io.h timeout.h usocket.h diff --git a/vendor/luasec/src/luasocket/buffer.c b/vendor/luasec/src/luasocket/buffer.c new file mode 100644 index 00000000..0eaac418 --- /dev/null +++ b/vendor/luasec/src/luasocket/buffer.c @@ -0,0 +1,278 @@ +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" + +#include "buffer.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); +static int recvline(p_buffer buf, luaL_Buffer *b); +static int recvall(p_buffer buf, luaL_Buffer *b); +static int buffer_get(p_buffer buf, const char **data, size_t *count); +static void buffer_skip(p_buffer buf, size_t count); +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int buffer_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; + buf->io = io; + buf->tm = tm; + buf->received = buf->sent = 0; + buf->birthday = timeout_gettime(); +} + +/*-------------------------------------------------------------------------*\ +* object:getstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_getstats(lua_State *L, p_buffer buf) { + lua_pushnumber(L, (lua_Number) buf->received); + lua_pushnumber(L, (lua_Number) buf->sent); + lua_pushnumber(L, timeout_gettime() - buf->birthday); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* object:setstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_setstats(lua_State *L, p_buffer buf) { + buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); + buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* object:send() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_send(lua_State *L, p_buffer buf) { + int top = lua_gettop(L); + int err = IO_DONE; + size_t size = 0, sent = 0; + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); + timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; + if (end > (long) size) end = (long) size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, (lua_Number) (sent+start-1)); + } else { + lua_pushnumber(L, (lua_Number) (sent+start-1)); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* object:receive() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_receive(lua_State *L, p_buffer buf) { + luaL_Buffer b; + size_t size; + const char *part; + int err = IO_DONE; + int top = lua_gettop(L); + if (top < 3) { + lua_settop(L, 3); + top = 3; + } + part = luaL_optlstring(L, 3, "", &size); + timeout_markstart(buf->tm); + /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); + /* receive new patterns */ + if (!lua_isnumber(L, 2)) { + const char *p= luaL_optstring(L, 2, "*l"); + if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else luaL_argcheck(L, 0, 2, "invalid receive pattern"); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else { + double n = lua_tonumber(L, 2); + size_t wanted = (size_t) n; + luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); + if (size == 0 || wanted > size) + err = recvraw(buf, wanted-size, &b); + } + /* check if there was an error */ + if (err != IO_DONE) { + /* we can't push anything in the stack before pushing the + * contents of the buffer. this is the reason for the complication */ + luaL_pushresult(&b); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { + luaL_pushresult(&b); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* Determines if there is any data in the read buffer +\*-------------------------------------------------------------------------*/ +int buffer_isempty(p_buffer buf) { + return buf->first >= buf->last; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sends a block of data (unbuffered) +\*-------------------------------------------------------------------------*/ +#define STEPSIZE 8192 +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { + p_io io = buf->io; + p_timeout tm = buf->tm; + size_t total = 0; + int err = IO_DONE; + while (total < count && err == IO_DONE) { + size_t done = 0; + size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; + err = io->send(io->ctx, data+total, step, &done, tm); + total += done; + } + *sent = total; + buf->sent += total; + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a fixed number of bytes (buffered) +\*-------------------------------------------------------------------------*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + size_t count; const char *data; + err = buffer_get(buf, &data, &count); + count = MIN(count, wanted - total); + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + total += count; + if (total >= wanted) break; + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads everything until the connection is closed (buffered) +\*-------------------------------------------------------------------------*/ +static int recvall(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + const char *data; size_t count; + err = buffer_get(buf, &data, &count); + total += count; + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + } + if (err == IO_CLOSED) { + if (total > 0) return IO_DONE; + else return IO_CLOSED; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* are not returned by the function and are discarded from the buffer +\*-------------------------------------------------------------------------*/ +static int recvline(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + while (err == IO_DONE) { + size_t count, pos; const char *data; + err = buffer_get(buf, &data, &count); + pos = 0; + while (pos < count && data[pos] != '\n') { + /* we ignore all \r's */ + if (data[pos] != '\r') luaL_addchar(b, data[pos]); + pos++; + } + if (pos < count) { /* found '\n' */ + buffer_skip(buf, pos+1); /* skip '\n' too */ + break; /* we are done */ + } else /* reached the end of the buffer */ + buffer_skip(buf, pos); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Skips a given number of bytes from read buffer. No data is read from the +* transport layer +\*-------------------------------------------------------------------------*/ +static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; + buf->first += count; + if (buffer_isempty(buf)) + buf->first = buf->last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data available in buffer, or get more data from transport layer +* if buffer is empty +\*-------------------------------------------------------------------------*/ +static int buffer_get(p_buffer buf, const char **data, size_t *count) { + int err = IO_DONE; + p_io io = buf->io; + p_timeout tm = buf->tm; + if (buffer_isempty(buf)) { + size_t got; + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); + buf->first = 0; + buf->last = got; + } + *count = buf->last - buf->first; + *data = buf->data + buf->first; + return err; +} diff --git a/vendor/luasec/src/luasocket/buffer.h b/vendor/luasec/src/luasocket/buffer.h new file mode 100644 index 00000000..1281bb39 --- /dev/null +++ b/vendor/luasec/src/luasocket/buffer.h @@ -0,0 +1,45 @@ +#ifndef BUF_H +#define BUF_H +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. +\*=========================================================================*/ +#include "lua.h" + +#include "io.h" +#include "timeout.h" + +/* buffer size in bytes */ +#define BUF_SIZE 8192 + +/* buffer control structure */ +typedef struct t_buffer_ { + double birthday; /* throttle support info: creation time, */ + size_t sent, received; /* bytes sent, and bytes received */ + p_io io; /* IO driver used for this buffer */ + p_timeout tm; /* timeout management for this buffer */ + size_t first, last; /* index of first and last bytes of stored data */ + char data[BUF_SIZE]; /* storage space for buffer data */ +} t_buffer; +typedef t_buffer *p_buffer; + +int buffer_open(lua_State *L); +void buffer_init(p_buffer buf, p_io io, p_timeout tm); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_meth_getstats(lua_State *L, p_buffer buf); +int buffer_meth_setstats(lua_State *L, p_buffer buf); +int buffer_isempty(p_buffer buf); + +#endif /* BUF_H */ diff --git a/vendor/luasec/src/luasocket/io.c b/vendor/luasec/src/luasocket/io.c new file mode 100644 index 00000000..35f46f78 --- /dev/null +++ b/vendor/luasec/src/luasocket/io.c @@ -0,0 +1,30 @@ +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { + io->send = send; + io->recv = recv; + io->error = error; + io->ctx = ctx; +} + +/*-------------------------------------------------------------------------*\ +* I/O error strings +\*-------------------------------------------------------------------------*/ +const char *io_strerror(int err) { + switch (err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; + default: return "unknown error"; + } +} diff --git a/vendor/luasec/src/luasocket/io.h b/vendor/luasec/src/luasocket/io.h new file mode 100644 index 00000000..8945e2c0 --- /dev/null +++ b/vendor/luasec/src/luasocket/io.h @@ -0,0 +1,65 @@ +#ifndef IO_H +#define IO_H +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +\*=========================================================================*/ +#include +#include "lua.h" + +#include "timeout.h" + +/* IO error codes */ +enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 +}; + +/* interface to error message function */ +typedef const char *(*p_error) ( + void *ctx, /* context needed by send */ + int err /* error code */ +); + +/* interface to send function */ +typedef int (*p_send) ( + void *ctx, /* context needed by send */ + const char *data, /* pointer to buffer with data to send */ + size_t count, /* number of bytes to send from buffer */ + size_t *sent, /* number of bytes sent uppon return */ + p_timeout tm /* timeout control */ +); + +/* interface to recv function */ +typedef int (*p_recv) ( + void *ctx, /* context needed by recv */ + char *data, /* pointer to buffer where data will be written */ + size_t count, /* number of bytes to receive into buffer */ + size_t *got, /* number of bytes received uppon return */ + p_timeout tm /* timeout control */ +); + +/* IO driver definition */ +typedef struct t_io_ { + void *ctx; /* context needed by send/recv */ + p_send send; /* send function pointer */ + p_recv recv; /* receive function pointer */ + p_error error; /* strerror function */ +} t_io; +typedef t_io *p_io; + +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); +const char *io_strerror(int err); + +#endif /* IO_H */ + diff --git a/vendor/luasec/src/luasocket/socket.h b/vendor/luasec/src/luasocket/socket.h new file mode 100644 index 00000000..07c20fe3 --- /dev/null +++ b/vendor/luasec/src/luasocket/socket.h @@ -0,0 +1,78 @@ +#ifndef SOCKET_H +#define SOCKET_H +/*=========================================================================*\ +* Socket compatibilization module +* LuaSocket toolkit +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Platform specific compatibilization +\*=========================================================================*/ +#ifdef _WIN32 +#include "wsocket.h" +#else +#include "usocket.h" +#endif + +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + +/* we are lazy... */ +typedef struct sockaddr SA; + +/*=========================================================================*\ +* Functions below implement a comfortable platform independent +* interface to sockets +\*=========================================================================*/ +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +void socket_shutdown(p_socket ps, int how); +int socket_sendto(p_socket ps, const char *data, size_t count, + size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, + size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); + +void socket_setnonblocking(p_socket ps); +void socket_setblocking(p_socket ps); + +int socket_waitfd(p_socket ps, int sw, p_timeout tm); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm); + +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_create(p_socket ps, int domain, int type, int protocol); +int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); +int socket_listen(p_socket ps, int backlog); +int socket_accept(p_socket ps, p_socket pa, SA *addr, + socklen_t *addr_len, p_timeout tm); + +const char *socket_hoststrerror(int err); +const char *socket_gaistrerror(int err); +const char *socket_strerror(int err); + +/* these are perfect to use with the io abstraction module + and the buffered input module */ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +const char *socket_ioerror(p_socket ps, int err); + +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); +int socket_gethostbyname(const char *addr, struct hostent **hp); + +#endif /* SOCKET_H */ diff --git a/vendor/luasec/src/luasocket/timeout.c b/vendor/luasec/src/luasocket/timeout.c new file mode 100644 index 00000000..94a524be --- /dev/null +++ b/vendor/luasec/src/luasocket/timeout.c @@ -0,0 +1,220 @@ +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "timeout.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int timeout_lua_gettime(lua_State *L); +static int timeout_lua_sleep(lua_State *L); + +static luaL_Reg func[] = { + { "gettime", timeout_lua_gettime }, + { "sleep", timeout_lua_sleep }, + { NULL, NULL } +}; + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initialize structure +\*-------------------------------------------------------------------------*/ +void timeout_init(p_timeout tm, double block, double total) { + tm->block = block; + tm->total = total; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was successful +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_get(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + return tm->block; + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Returns time since start of operation +* Input +* tm: timeout control structure +* Returns +* start field of structure +\*-------------------------------------------------------------------------*/ +double timeout_getstart(p_timeout tm) { + return tm->start; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was a failure +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_getretry(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +p_timeout timeout_markstart(p_timeout tm) { + tm->start = timeout_gettime(); + return tm; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in s, relative to January 1, 1970 (UTC) +* Returns +* time in s. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +double timeout_gettime(void) { + FILETIME ft; + double t; + GetSystemTimeAsFileTime(&ft); + /* Windows file time (time since January 1, 1601 (UTC)) */ + t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); + /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ + return (t - 11644473600.0); +} +#else +double timeout_gettime(void) { + struct timeval v; + gettimeofday(&v, (struct timezone *) NULL); + /* Unix Epoch time (time since January 1, 1970 (UTC)) */ + return v.tv_sec + v.tv_usec/1.0e6; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int timeout_open(lua_State *L) { +#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +#else + luaL_openlib(L, NULL, func, 0); +#endif + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Sets timeout values for IO operations +* Lua Input: base, time [, mode] +* time: time out value in seconds +* mode: "b" for block timeout, "t" for total timeout. (default: b) +\*-------------------------------------------------------------------------*/ +int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + double t = luaL_optnumber(L, 2, -1); + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': + tm->block = t; + break; + case 'r': case 't': + tm->total = t; + break; + default: + luaL_argcheck(L, 0, 3, "invalid timeout mode"); + break; + } + lua_pushnumber(L, 1); + return 1; +} + +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +static int timeout_lua_gettime(lua_State *L) +{ + lua_pushnumber(L, timeout_gettime()); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + if (n < 0.0) n = 0.0; + if (n < DBL_MAX/1000.0) n *= 1000.0; + if (n > INT_MAX) n = INT_MAX; + Sleep((int)n); + return 0; +} +#else +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + struct timespec t, r; + if (n < 0.0) n = 0.0; + if (n > INT_MAX) n = INT_MAX; + t.tv_sec = (int) n; + n -= t.tv_sec; + t.tv_nsec = (int) (n * 1000000000); + if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; + while (nanosleep(&t, &r) != 0) { + t.tv_sec = r.tv_sec; + t.tv_nsec = r.tv_nsec; + } + return 0; +} +#endif diff --git a/vendor/luasec/src/luasocket/timeout.h b/vendor/luasec/src/luasocket/timeout.h new file mode 100644 index 00000000..4957593b --- /dev/null +++ b/vendor/luasec/src/luasocket/timeout.h @@ -0,0 +1,28 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "lua.h" + +/* timeout control structure */ +typedef struct t_timeout_ { + double block; /* maximum time for blocking calls */ + double total; /* total number of milliseconds for operation */ + double start; /* time of start of operation */ +} t_timeout; +typedef t_timeout *p_timeout; + +int timeout_open(lua_State *L); +void timeout_init(p_timeout tm, double block, double total); +double timeout_get(p_timeout tm); +double timeout_getretry(p_timeout tm); +p_timeout timeout_markstart(p_timeout tm); +double timeout_getstart(p_timeout tm); +double timeout_gettime(void); +int timeout_meth_settimeout(lua_State *L, p_timeout tm); + +#define timeout_iszero(tm) ((tm)->block == 0.0) + +#endif /* TIMEOUT_H */ diff --git a/vendor/luasec/src/luasocket/usocket.c b/vendor/luasec/src/luasocket/usocket.c new file mode 100644 index 00000000..49e618c4 --- /dev/null +++ b/vendor/luasec/src/luasocket/usocket.c @@ -0,0 +1,441 @@ +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* The code is now interrupt-safe. +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include +#include + +#include "socket.h" + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#ifndef SOCKET_SELECT +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + struct pollfd pfd; + pfd.fd = *ps; + pfd.events = sw; + pfd.revents = 0; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + int t = (int)(timeout_getretry(tm)*1e3); + ret = poll(&pfd, 1, t >= 0? t: -1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; + return IO_DONE; +} +#else +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp; + double t; + if (*ps >= FD_SETSIZE) return EINVAL; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + /* must set bits within loop, because select may have modified them */ + rp = wp = NULL; + if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + t = timeout_getretry(tm); + tp = NULL; + if (t >= 0.0) { + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(*ps+1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED; + return IO_DONE; +} +#endif + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + /* instals a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); + close(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Select with timeout control +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { + struct timeval tv; + double t = timeout_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + /* timeout = 0 means no wait */ + ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog)) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE; + else return errno; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + err = errno; + if (err == EINTR) continue; + if (err != EAGAIN && err != ECONNABORTED) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (err == EPIPE) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recvfrom(*ps, data, count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +* Make sure important error messages are standard +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return "host not found"; + default: return hstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case EADDRINUSE: return "address already in use"; + case EISCONN: return "already connected"; + case EACCES: return "permission denied"; + case ECONNREFUSED: return "connection refused"; + case ECONNABORTED: return "closed"; + case ECONNRESET: return "closed"; + case ETIMEDOUT: return "timeout"; + default: return strerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return "temporary failure in name resolution"; + case EAI_BADFLAGS: return "invalid value for ai_flags"; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return "invalid value for hints"; +#endif + case EAI_FAIL: return "non-recoverable failure in name resolution"; + case EAI_FAMILY: return "ai_family not supported"; + case EAI_MEMORY: return "memory allocation failure"; + case EAI_NONAME: + return "host or service not provided, or not known"; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return "argument buffer overflow"; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return "resolved protocol is unknown"; +#endif + case EAI_SERVICE: return "service not supported for socket type"; + case EAI_SOCKTYPE: return "ai_socktype not supported"; + case EAI_SYSTEM: return strerror(errno); + default: return gai_strerror(err); + } +} + diff --git a/vendor/luasec/src/luasocket/usocket.h b/vendor/luasec/src/luasocket/usocket.h new file mode 100644 index 00000000..ecbcd8e5 --- /dev/null +++ b/vendor/luasec/src/luasocket/usocket.h @@ -0,0 +1,70 @@ +#ifndef USOCKET_H +#define USOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include +/* close function */ +#include +/* fnctnl function and associated constants */ +#include +/* struct sockaddr */ +#include +/* socket function */ +#include +/* struct timeval */ +#include +/* gethostbyname and gethostbyaddr functions */ +#include +/* sigpipe handling */ +#include +/* IP stuff*/ +#include +#include +/* TCP options (nagle algorithm disable) */ +#include +#include + +#ifndef SOCKET_SELECT +#include +#define WAITFD_R POLLIN +#define WAITFD_W POLLOUT +#define WAITFD_C (POLLIN|POLLOUT) +#else +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R|WAITFD_W) +#endif + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +/* Some platforms use IPV6_JOIN_GROUP instead if + * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ + +/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ + +typedef int t_socket; +typedef t_socket *p_socket; +typedef struct sockaddr_storage t_sockaddr_storage; + +#define SOCKET_INVALID (-1) + +#endif /* USOCKET_H */ diff --git a/vendor/luasec/src/luasocket/wsocket.c b/vendor/luasec/src/luasocket/wsocket.c new file mode 100644 index 00000000..8c7640eb --- /dev/null +++ b/vendor/luasec/src/luasocket/wsocket.c @@ -0,0 +1,429 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include + +#include "socket.h" + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int len = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return "host not found"; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return "address already in use"; + case WSAECONNREFUSED: return "connection refused"; + case WSAEISCONN: return "already connected"; + case WSAEACCES: return "permission denied"; + case WSAECONNABORTED: return "closed"; + case WSAECONNRESET: return "closed"; + case WSAETIMEDOUT: return "timeout"; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return "Permission denied"; + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return "Socket type not supported"; + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: return "Address already in use"; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return "Connection reset by peer"; + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return "Socket is already connected"; + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return "Connection timed out"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return "Host not found"; + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return "temporary failure in name resolution"; + case EAI_BADFLAGS: return "invalid value for ai_flags"; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return "invalid value for hints"; +#endif + case EAI_FAIL: return "non-recoverable failure in name resolution"; + case EAI_FAMILY: return "ai_family not supported"; + case EAI_MEMORY: return "memory allocation failure"; + case EAI_NONAME: + return "host or service not provided, or not known"; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return "argument buffer overflow"; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return "resolved protocol is unknown"; +#endif + case EAI_SERVICE: return "service not supported for socket type"; + case EAI_SOCKTYPE: return "ai_socktype not supported"; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: return strerror(errno); +#endif + default: return gai_strerror(err); + } +} + diff --git a/vendor/luasec/src/luasocket/wsocket.h b/vendor/luasec/src/luasocket/wsocket.h new file mode 100644 index 00000000..c5a4b1ce --- /dev/null +++ b/vendor/luasec/src/luasocket/wsocket.h @@ -0,0 +1,38 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include +#include + +typedef int socklen_t; +typedef SOCKADDR_STORAGE t_sockaddr_storage; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +#define SOCKET_INVALID (INVALID_SOCKET) + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV (0) +#endif + +#endif /* WSOCKET_H */ diff --git a/vendor/luasec/src/options.c b/vendor/luasec/src/options.c new file mode 100644 index 00000000..3d17d6d5 --- /dev/null +++ b/vendor/luasec/src/options.c @@ -0,0 +1,185 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +#include "options.h" + +/* If you need to generate these options again, see options.lua */ + + +/* + OpenSSL version: OpenSSL 3.0.8 +*/ + +static lsec_ssl_option_t ssl_options[] = { +#if defined(SSL_OP_ALL) + {"all", SSL_OP_ALL}, +#endif +#if defined(SSL_OP_ALLOW_CLIENT_RENEGOTIATION) + {"allow_client_renegotiation", SSL_OP_ALLOW_CLIENT_RENEGOTIATION}, +#endif +#if defined(SSL_OP_ALLOW_NO_DHE_KEX) + {"allow_no_dhe_kex", SSL_OP_ALLOW_NO_DHE_KEX}, +#endif +#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) + {"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION}, +#endif +#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE) + {"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE}, +#endif +#if defined(SSL_OP_CISCO_ANYCONNECT) + {"cisco_anyconnect", SSL_OP_CISCO_ANYCONNECT}, +#endif +#if defined(SSL_OP_CLEANSE_PLAINTEXT) + {"cleanse_plaintext", SSL_OP_CLEANSE_PLAINTEXT}, +#endif +#if defined(SSL_OP_COOKIE_EXCHANGE) + {"cookie_exchange", SSL_OP_COOKIE_EXCHANGE}, +#endif +#if defined(SSL_OP_CRYPTOPRO_TLSEXT_BUG) + {"cryptopro_tlsext_bug", SSL_OP_CRYPTOPRO_TLSEXT_BUG}, +#endif +#if defined(SSL_OP_DISABLE_TLSEXT_CA_NAMES) + {"disable_tlsext_ca_names", SSL_OP_DISABLE_TLSEXT_CA_NAMES}, +#endif +#if defined(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) + {"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS}, +#endif +#if defined(SSL_OP_ENABLE_KTLS) + {"enable_ktls", SSL_OP_ENABLE_KTLS}, +#endif +#if defined(SSL_OP_ENABLE_MIDDLEBOX_COMPAT) + {"enable_middlebox_compat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT}, +#endif +#if defined(SSL_OP_EPHEMERAL_RSA) + {"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA}, +#endif +#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) + {"ignore_unexpected_eof", SSL_OP_IGNORE_UNEXPECTED_EOF}, +#endif +#if defined(SSL_OP_LEGACY_SERVER_CONNECT) + {"legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT}, +#endif +#if defined(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) + {"microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER}, +#endif +#if defined(SSL_OP_MICROSOFT_SESS_ID_BUG) + {"microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG}, +#endif +#if defined(SSL_OP_MSIE_SSLV2_RSA_PADDING) + {"msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING}, +#endif +#if defined(SSL_OP_NETSCAPE_CA_DN_BUG) + {"netscape_ca_dn_bug", SSL_OP_NETSCAPE_CA_DN_BUG}, +#endif +#if defined(SSL_OP_NETSCAPE_CHALLENGE_BUG) + {"netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG}, +#endif +#if defined(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG) + {"netscape_demo_cipher_change_bug", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG}, +#endif +#if defined(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG) + {"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG}, +#endif +#if defined(SSL_OP_NO_ANTI_REPLAY) + {"no_anti_replay", SSL_OP_NO_ANTI_REPLAY}, +#endif +#if defined(SSL_OP_NO_COMPRESSION) + {"no_compression", SSL_OP_NO_COMPRESSION}, +#endif +#if defined(SSL_OP_NO_DTLS_MASK) + {"no_dtls_mask", SSL_OP_NO_DTLS_MASK}, +#endif +#if defined(SSL_OP_NO_DTLSv1) + {"no_dtlsv1", SSL_OP_NO_DTLSv1}, +#endif +#if defined(SSL_OP_NO_DTLSv1_2) + {"no_dtlsv1_2", SSL_OP_NO_DTLSv1_2}, +#endif +#if defined(SSL_OP_NO_ENCRYPT_THEN_MAC) + {"no_encrypt_then_mac", SSL_OP_NO_ENCRYPT_THEN_MAC}, +#endif +#if defined(SSL_OP_NO_EXTENDED_MASTER_SECRET) + {"no_extended_master_secret", SSL_OP_NO_EXTENDED_MASTER_SECRET}, +#endif +#if defined(SSL_OP_NO_QUERY_MTU) + {"no_query_mtu", SSL_OP_NO_QUERY_MTU}, +#endif +#if defined(SSL_OP_NO_RENEGOTIATION) + {"no_renegotiation", SSL_OP_NO_RENEGOTIATION}, +#endif +#if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) + {"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION}, +#endif +#if defined(SSL_OP_NO_SSL_MASK) + {"no_ssl_mask", SSL_OP_NO_SSL_MASK}, +#endif +#if defined(SSL_OP_NO_SSLv2) + {"no_sslv2", SSL_OP_NO_SSLv2}, +#endif +#if defined(SSL_OP_NO_SSLv3) + {"no_sslv3", SSL_OP_NO_SSLv3}, +#endif +#if defined(SSL_OP_NO_TICKET) + {"no_ticket", SSL_OP_NO_TICKET}, +#endif +#if defined(SSL_OP_NO_TLSv1) + {"no_tlsv1", SSL_OP_NO_TLSv1}, +#endif +#if defined(SSL_OP_NO_TLSv1_1) + {"no_tlsv1_1", SSL_OP_NO_TLSv1_1}, +#endif +#if defined(SSL_OP_NO_TLSv1_2) + {"no_tlsv1_2", SSL_OP_NO_TLSv1_2}, +#endif +#if defined(SSL_OP_NO_TLSv1_3) + {"no_tlsv1_3", SSL_OP_NO_TLSv1_3}, +#endif +#if defined(SSL_OP_PKCS1_CHECK_1) + {"pkcs1_check_1", SSL_OP_PKCS1_CHECK_1}, +#endif +#if defined(SSL_OP_PKCS1_CHECK_2) + {"pkcs1_check_2", SSL_OP_PKCS1_CHECK_2}, +#endif +#if defined(SSL_OP_PRIORITIZE_CHACHA) + {"prioritize_chacha", SSL_OP_PRIORITIZE_CHACHA}, +#endif +#if defined(SSL_OP_SAFARI_ECDHE_ECDSA_BUG) + {"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG}, +#endif +#if defined(SSL_OP_SINGLE_DH_USE) + {"single_dh_use", SSL_OP_SINGLE_DH_USE}, +#endif +#if defined(SSL_OP_SINGLE_ECDH_USE) + {"single_ecdh_use", SSL_OP_SINGLE_ECDH_USE}, +#endif +#if defined(SSL_OP_SSLEAY_080_CLIENT_DH_BUG) + {"ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG}, +#endif +#if defined(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG) + {"sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG}, +#endif +#if defined(SSL_OP_TLSEXT_PADDING) + {"tlsext_padding", SSL_OP_TLSEXT_PADDING}, +#endif +#if defined(SSL_OP_TLS_BLOCK_PADDING_BUG) + {"tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG}, +#endif +#if defined(SSL_OP_TLS_D5_BUG) + {"tls_d5_bug", SSL_OP_TLS_D5_BUG}, +#endif +#if defined(SSL_OP_TLS_ROLLBACK_BUG) + {"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG}, +#endif + {NULL, 0L} +}; + +LSEC_API lsec_ssl_option_t* lsec_get_ssl_options() { + return ssl_options; +} + diff --git a/vendor/luasec/src/options.h b/vendor/luasec/src/options.h new file mode 100644 index 00000000..c72d52d0 --- /dev/null +++ b/vendor/luasec/src/options.h @@ -0,0 +1,22 @@ +#ifndef LSEC_OPTIONS_H +#define LSEC_OPTIONS_H + +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include "compat.h" + +struct lsec_ssl_option_s { + const char *name; + unsigned long code; +}; + +typedef struct lsec_ssl_option_s lsec_ssl_option_t; + +LSEC_API lsec_ssl_option_t* lsec_get_ssl_options(); + +#endif diff --git a/vendor/luasec/src/options.lua b/vendor/luasec/src/options.lua new file mode 100644 index 00000000..72428c01 --- /dev/null +++ b/vendor/luasec/src/options.lua @@ -0,0 +1,93 @@ +local function usage() + print("Usage:") + print("* Generate options of your system:") + print(" lua options.lua -g /path/to/ssl.h [version] > options.c") + print("* Examples:") + print(" lua options.lua -g /usr/include/openssl/ssl.h > options.c\n") + print(" lua options.lua -g /usr/include/openssl/ssl.h \"OpenSSL 1.1.1f\" > options.c\n") + + print("* List options of your system:") + print(" lua options.lua -l /path/to/ssl.h\n") +end + +-- +local function printf(str, ...) + print(string.format(str, ...)) +end + +local function generate(options, version) + print([[ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +#include "options.h" + +/* If you need to generate these options again, see options.lua */ + +]]) + + printf([[ +/* + OpenSSL version: %s +*/ +]], version) + + print([[static lsec_ssl_option_t ssl_options[] = {]]) + + for k, option in ipairs(options) do + local name = string.lower(string.sub(option, 8)) + print(string.format([[#if defined(%s)]], option)) + print(string.format([[ {"%s", %s},]], name, option)) + print([[#endif]]) + end + print([[ {NULL, 0L}]]) + print([[ +}; + +LSEC_API lsec_ssl_option_t* lsec_get_ssl_options() { + return ssl_options; +} +]]) +end + +local function loadoptions(file) + local options = {} + local f = assert(io.open(file, "r")) + for line in f:lines() do + local op = string.match(line, "define%s+(SSL_OP_BIT%()") + if not op then + op = string.match(line, "define%s+(SSL_OP_%S+)") + if op then + table.insert(options, op) + end + end + end + table.sort(options, function(a,b) return a +#include + +#if defined(WIN32) +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "x509.h" +#include "context.h" +#include "ssl.h" + + +#ifndef LSEC_API_OPENSSL_1_1_0 +#define SSL_is_server(s) (s->server) +#define SSL_up_ref(ssl) CRYPTO_add(&(ssl)->references, 1, CRYPTO_LOCK_SSL) +#define X509_up_ref(c) CRYPTO_add(&c->references, 1, CRYPTO_LOCK_X509) +#endif + + +/** + * Underline socket error. + */ +static int lsec_socket_error() +{ +#if defined(WIN32) + return WSAGetLastError(); +#else +#if defined(LSEC_OPENSSL_ERRNO_BUG) + // Bug in OpenSSL + if (errno == 0) + return LSEC_IO_SSL; +#endif + return errno; +#endif +} + +/** + * Map error code into string. + */ +static const char *ssl_ioerror(void *ctx, int err) +{ + if (err == LSEC_IO_SSL) { + p_ssl ssl = (p_ssl) ctx; + switch(ssl->error) { + case SSL_ERROR_NONE: return "No error"; + case SSL_ERROR_ZERO_RETURN: return "closed"; + case SSL_ERROR_WANT_READ: return "wantread"; + case SSL_ERROR_WANT_WRITE: return "wantwrite"; + case SSL_ERROR_WANT_CONNECT: return "'connect' not completed"; + case SSL_ERROR_WANT_ACCEPT: return "'accept' not completed"; + case SSL_ERROR_WANT_X509_LOOKUP: return "Waiting for callback"; + case SSL_ERROR_SYSCALL: return "System error"; + case SSL_ERROR_SSL: return ERR_reason_error_string(ERR_get_error()); + default: return "Unknown SSL error"; + } + } + return socket_strerror(err); +} + +/** + * Close the connection before the GC collect the object. + */ +static int meth_destroy(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state == LSEC_STATE_CONNECTED) { + socket_setblocking(&ssl->sock); + SSL_shutdown(ssl->ssl); + } + if (ssl->sock != SOCKET_INVALID) { + socket_destroy(&ssl->sock); + } + ssl->state = LSEC_STATE_CLOSED; + if (ssl->ssl) { + /* Clear the registries */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_pushnil(L); + lua_settable(L, -3); + luaL_getmetatable(L, "SSL:SNI:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_pushnil(L); + lua_settable(L, -3); + /* Destroy the object */ + SSL_free(ssl->ssl); + ssl->ssl = NULL; + } + return 0; +} + +/** + * Perform the TLS/SSL handshake + */ +static int handshake(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_do_handshake(ssl->ssl); + ssl->error = SSL_get_error(ssl->ssl, err); + switch (ssl->error) { + case SSL_ERROR_NONE: + ssl->state = LSEC_STATE_CONNECTED; + return IO_DONE; + 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 + */ +static int ssl_send(void *ctx, const char *data, size_t count, size_t *sent, + p_timeout tm) +{ + int err; + p_ssl ssl = (p_ssl)ctx; + if (ssl->state != LSEC_STATE_CONNECTED) + return IO_CLOSED; + *sent = 0; + for ( ; ; ) { + ERR_clear_error(); + err = SSL_write(ssl->ssl, data, (int)count); + ssl->error = SSL_get_error(ssl->ssl, err); + switch (ssl->error) { + case SSL_ERROR_NONE: + *sent = err; + return IO_DONE; + 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; +} + +/** + * Receive data + */ +static int ssl_recv(void *ctx, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err; + p_ssl ssl = (p_ssl)ctx; + *got = 0; + if (ssl->state != LSEC_STATE_CONNECTED) + return IO_CLOSED; + for ( ; ; ) { + ERR_clear_error(); + err = SSL_read(ssl->ssl, data, (int)count); + ssl->error = SSL_get_error(ssl->ssl, err); + switch (ssl->error) { + case SSL_ERROR_NONE: + *got = err; + return IO_DONE; + case SSL_ERROR_ZERO_RETURN: + return IO_CLOSED; + 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; +} + +static SSL_CTX* luaossl_testcontext(lua_State *L, int arg) { + SSL_CTX **ctx = luaL_testudata(L, arg, "SSL_CTX*"); + if (ctx) + return *ctx; + return NULL; +} + +static SSL* luaossl_testssl(lua_State *L, int arg) { + SSL **ssl = luaL_testudata(L, arg, "SSL*"); + if (ssl) + return *ssl; + return NULL; +} + +/** + * Create a new TLS/SSL object and mark it as new. + */ +static int meth_create(lua_State *L) +{ + p_ssl ssl; + int mode; + SSL_CTX *ctx; + + lua_settop(L, 1); + + ssl = (p_ssl)lua_newuserdata(L, sizeof(t_ssl)); + if (!ssl) { + lua_pushnil(L); + lua_pushstring(L, "error creating SSL object"); + return 2; + } + + if ((ctx = lsec_testcontext(L, 1))) { + mode = lsec_getmode(L, 1); + if (mode == LSEC_MODE_INVALID) { + lua_pushnil(L); + lua_pushstring(L, "invalid mode"); + return 2; + } + ssl->ssl = SSL_new(ctx); + if (!ssl->ssl) { + lua_pushnil(L); + lua_pushfstring(L, "error creating SSL object (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + } else if ((ctx = luaossl_testcontext(L, 1))) { + ssl->ssl = SSL_new(ctx); + if (!ssl->ssl) { + lua_pushnil(L); + lua_pushfstring(L, "error creating SSL object (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + mode = SSL_is_server(ssl->ssl) ? LSEC_MODE_SERVER : LSEC_MODE_CLIENT; + } else if ((ssl->ssl = luaossl_testssl(L, 1))) { + SSL_up_ref(ssl->ssl); + mode = SSL_is_server(ssl->ssl) ? LSEC_MODE_SERVER : LSEC_MODE_CLIENT; + } else { + return luaL_argerror(L, 1, "invalid context"); + } + ssl->state = LSEC_STATE_NEW; + SSL_set_fd(ssl->ssl, (int)SOCKET_INVALID); + SSL_set_mode(ssl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_set_mode(ssl->ssl, SSL_MODE_RELEASE_BUFFERS); + if (mode == LSEC_MODE_SERVER) + SSL_set_accept_state(ssl->ssl); + else + SSL_set_connect_state(ssl->ssl); + + io_init(&ssl->io, (p_send)ssl_send, (p_recv)ssl_recv, + (p_error) ssl_ioerror, ssl); + timeout_init(&ssl->tm, -1, -1); + buffer_init(&ssl->buf, &ssl->io, &ssl->tm); + + luaL_getmetatable(L, "SSL:Connection"); + lua_setmetatable(L, -2); + return 1; +} + +/** + * Buffer send function + */ +static int meth_send(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_send(L, &ssl->buf); +} + +/** + * Buffer receive function + */ +static int meth_receive(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_receive(L, &ssl->buf); +} + +/** + * Get the buffer's statistics. + */ +static int meth_getstats(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_getstats(L, &ssl->buf); +} + +/** + * Set the buffer's statistics. + */ +static int meth_setstats(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_setstats(L, &ssl->buf); +} + +/** + * Select support methods + */ +static int meth_getfd(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + lua_pushnumber(L, ssl->sock); + return 1; +} + +/** + * Set the TLS/SSL file descriptor. + * Call it *before* the handshake. + */ +static int meth_setfd(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_NEW) + luaL_argerror(L, 1, "invalid SSL object state"); + ssl->sock = (t_socket)luaL_checkinteger(L, 2); + socket_setnonblocking(&ssl->sock); + SSL_set_fd(ssl->ssl, (int)ssl->sock); + return 0; +} + +/** + * Lua handshake function. + */ +static int meth_handshake(lua_State *L) +{ + int err; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + p_context ctx = (p_context)SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl->ssl)); + ctx->L = L; + err = handshake(ssl); + if (ctx->dh_param) { + DH_free(ctx->dh_param); + ctx->dh_param = NULL; + } + if (ctx->alpn) { + free(ctx->alpn); + ctx->alpn = NULL; + } + if (err == IO_DONE) { + lua_pushboolean(L, 1); + return 1; + } + lua_pushboolean(L, 0); + lua_pushstring(L, ssl_ioerror((void*)ssl, err)); + return 2; +} + +/** + * Close the connection. + */ +static int meth_close(lua_State *L) +{ + meth_destroy(L); + return 0; +} + +/** + * Set timeout. + */ +static int meth_settimeout(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return timeout_meth_settimeout(L, &ssl->tm); +} + +/** + * Check if there is data in the buffer. + */ +static int meth_dirty(lua_State *L) +{ + int res = 0; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CLOSED) + res = !buffer_isempty(&ssl->buf) || SSL_pending(ssl->ssl); + lua_pushboolean(L, res); + return 1; +} + +/** + * Return the state information about the SSL object. + */ +static int meth_want(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + int code = (ssl->state == LSEC_STATE_CLOSED) + ? SSL_NOTHING + : SSL_want(ssl->ssl); + switch(code) { + case SSL_NOTHING: lua_pushstring(L, "nothing"); break; + case SSL_READING: lua_pushstring(L, "read"); break; + case SSL_WRITING: lua_pushstring(L, "write"); break; + case SSL_X509_LOOKUP: lua_pushstring(L, "x509lookup"); break; + } + return 1; +} + +/** + * Return the compression method used. + */ +static int meth_compression(lua_State *L) +{ +#ifdef OPENSSL_NO_COMP + const void *comp; +#else + const COMP_METHOD *comp; +#endif + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + comp = SSL_get_current_compression(ssl->ssl); + if (comp) + lua_pushstring(L, SSL_COMP_get_name(comp)); + else + lua_pushnil(L); + return 1; +} + +/** + * Return the nth certificate of the peer's chain. + */ +static int meth_getpeercertificate(lua_State *L) +{ + int n; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + /* Default to the first cert */ + n = (int)luaL_optinteger(L, 2, 1); + /* This function is 1-based, but OpenSSL is 0-based */ + --n; + if (n < 0) { + lua_pushnil(L); + lua_pushliteral(L, "invalid certificate index"); + return 2; + } + if (n == 0) { + cert = SSL_get_peer_certificate(ssl->ssl); + if (cert) + lsec_pushx509(L, cert); + else + lua_pushnil(L); + return 1; + } + /* In a server-context, the stack doesn't contain the peer cert, + * so adjust accordingly. + */ + if (SSL_is_server(ssl->ssl)) + --n; + certs = SSL_get_peer_cert_chain(ssl->ssl); + if (n >= sk_X509_num(certs)) { + lua_pushnil(L); + return 1; + } + cert = sk_X509_value(certs, n); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + X509_up_ref(cert); + lsec_pushx509(L, cert); + return 1; +} + +/** + * Return the nth certificate of the chain sent to our peer. + */ +static int meth_getlocalcertificate(lua_State *L) +{ + int n; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + /* Default to the first cert */ + n = (int)luaL_optinteger(L, 2, 1); + /* This function is 1-based, but OpenSSL is 0-based */ + --n; + if (n < 0) { + lua_pushnil(L); + lua_pushliteral(L, "invalid certificate index"); + return 2; + } + if (n == 0) { + cert = SSL_get_certificate(ssl->ssl); + if (cert) + lsec_pushx509(L, cert); + else + lua_pushnil(L); + return 1; + } + /* In a server-context, the stack doesn't contain the peer cert, + * so adjust accordingly. + */ + if (SSL_is_server(ssl->ssl)) + --n; + if(SSL_get0_chain_certs(ssl->ssl, &certs) != 1) { + lua_pushnil(L); + } else { + if (n >= sk_X509_num(certs)) { + lua_pushnil(L); + return 1; + } + cert = sk_X509_value(certs, n); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + X509_up_ref(cert); + lsec_pushx509(L, cert); + } + return 1; +} + +/** + * Return the chain of certificate of the peer. + */ +static int meth_getpeerchain(lua_State *L) +{ + int i; + int idx = 1; + int n_certs; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + lua_newtable(L); + if (SSL_is_server(ssl->ssl)) { + lsec_pushx509(L, SSL_get_peer_certificate(ssl->ssl)); + lua_rawseti(L, -2, idx++); + } + certs = SSL_get_peer_cert_chain(ssl->ssl); + n_certs = sk_X509_num(certs); + for (i = 0; i < n_certs; i++) { + cert = sk_X509_value(certs, i); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + X509_up_ref(cert); + lsec_pushx509(L, cert); + lua_rawseti(L, -2, idx++); + } + return 1; +} + +/** + * Return the chain of certificates sent to the peer. + */ +static int meth_getlocalchain(lua_State *L) +{ + int i; + int idx = 1; + int n_certs; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + lua_newtable(L); + if (SSL_is_server(ssl->ssl)) { + lsec_pushx509(L, SSL_get_certificate(ssl->ssl)); + lua_rawseti(L, -2, idx++); + } + if(SSL_get0_chain_certs(ssl->ssl, &certs)) { + n_certs = sk_X509_num(certs); + for (i = 0; i < n_certs; i++) { + cert = sk_X509_value(certs, i); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + X509_up_ref(cert); + lsec_pushx509(L, cert); + lua_rawseti(L, -2, idx++); + } + } + return 1; +} + +/** + * Copy the table src to the table dst. + */ +static void copy_error_table(lua_State *L, int src, int dst) +{ + lua_pushnil(L); + while (lua_next(L, src) != 0) { + if (lua_istable(L, -1)) { + /* Replace the table with its copy */ + lua_newtable(L); + copy_error_table(L, dst+2, dst+3); + lua_remove(L, dst+2); + } + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, dst); + /* Remove the value and leave the key */ + lua_pop(L, 1); + } +} + +/** + * Return the verification state of the peer chain. + */ +static int meth_getpeerverification(lua_State *L) +{ + long err; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushboolean(L, 0); + lua_pushstring(L, "closed"); + return 2; + } + err = SSL_get_verify_result(ssl->ssl); + if (err == X509_V_OK) { + lua_pushboolean(L, 1); + return 1; + } + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) + lua_pushstring(L, X509_verify_cert_error_string(err)); + else { + /* Copy the table of errors to avoid modifications */ + lua_newtable(L); + copy_error_table(L, lua_gettop(L)-1, lua_gettop(L)); + } + lua_pushboolean(L, 0); + lua_pushvalue(L, -2); + return 2; +} + +/** + * Get the latest "Finished" message sent out. + */ +static int meth_getfinished(lua_State *L) +{ + size_t len = 0; + char *buffer = NULL; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + if ((len = SSL_get_finished(ssl->ssl, NULL, 0)) == 0) + return 0; + buffer = (char*)malloc(len); + if (!buffer) { + lua_pushnil(L); + lua_pushstring(L, "out of memory"); + return 2; + } + SSL_get_finished(ssl->ssl, buffer, len); + lua_pushlstring(L, buffer, len); + free(buffer); + return 1; +} + +/** + * Gets the latest "Finished" message received. + */ +static int meth_getpeerfinished(lua_State *L) +{ + size_t len = 0; + char *buffer = NULL; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 0; + } + if ((len = SSL_get_peer_finished(ssl->ssl, NULL, 0)) == 0) + return 0; + buffer = (char*)malloc(len); + if (!buffer) { + lua_pushnil(L); + lua_pushstring(L, "out of memory"); + return 2; + } + SSL_get_peer_finished(ssl->ssl, buffer, len); + lua_pushlstring(L, buffer, len); + free(buffer); + return 1; +} + +/** + * Get some shared keying material + */ +static int meth_exportkeyingmaterial(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + + if(ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 0; + } + + size_t llen = 0; + size_t contextlen = 0; + const unsigned char *context = NULL; + const char *label = (const char*)luaL_checklstring(L, 2, &llen); + size_t olen = (size_t)luaL_checkinteger(L, 3); + + if (!lua_isnoneornil(L, 4)) + context = (const unsigned char*)luaL_checklstring(L, 4, &contextlen); + + /* Temporary buffer memory-managed by Lua itself */ + unsigned char *out = (unsigned char*)lua_newuserdata(L, olen); + + if(SSL_export_keying_material(ssl->ssl, out, olen, label, llen, context, contextlen, context != NULL) != 1) { + lua_pushnil(L); + lua_pushstring(L, "error exporting keying material"); + return 2; + } + + lua_pushlstring(L, (char*)out, olen); + return 1; +} + +/** + * Object information -- tostring metamethod + */ +static int meth_tostring(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + lua_pushfstring(L, "SSL connection: %p%s", ssl, + ssl->state == LSEC_STATE_CLOSED ? " (closed)" : ""); + return 1; +} + +/** + * Add a method in the SSL metatable. + */ +static int meth_setmethod(lua_State *L) +{ + luaL_getmetatable(L, "SSL:Connection"); + lua_pushstring(L, "__index"); + lua_gettable(L, -2); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_settable(L, -3); + return 0; +} + +/** + * Return information about the connection. + */ +static int meth_info(lua_State *L) +{ + int bits = 0; + int algbits = 0; + char buf[256] = {0}; + const SSL_CIPHER *cipher; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + cipher = SSL_get_current_cipher(ssl->ssl); + if (!cipher) + return 0; + SSL_CIPHER_description(cipher, buf, sizeof(buf)); + bits = SSL_CIPHER_get_bits(cipher, &algbits); + lua_pushstring(L, buf); + lua_pushnumber(L, bits); + lua_pushnumber(L, algbits); + lua_pushstring(L, SSL_get_version(ssl->ssl)); + return 4; +} + +static int sni_cb(SSL *ssl, int *ad, void *arg) +{ + int strict; + SSL_CTX *newctx = NULL; + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + lua_State *L = ((p_context)SSL_CTX_get_app_data(ctx))->L; + const char *name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + /* No name, use default context */ + if (!name) + return SSL_TLSEXT_ERR_NOACK; + /* Retrieve struct from registry */ + luaL_getmetatable(L, "SSL:SNI:Registry"); + lua_pushlightuserdata(L, (void*)ssl); + lua_gettable(L, -2); + /* Strict search? */ + lua_pushstring(L, "strict"); + lua_gettable(L, -2); + strict = lua_toboolean(L, -1); + lua_pop(L, 1); + /* Search for the name in the map */ + lua_pushstring(L, "map"); + lua_gettable(L, -2); + lua_pushstring(L, name); + lua_gettable(L, -2); + if (lua_isuserdata(L, -1)) + newctx = lsec_checkcontext(L, -1); + lua_pop(L, 4); + /* Found, use this context */ + if (newctx) { + p_context pctx = (p_context)SSL_CTX_get_app_data(newctx); + pctx->L = L; + SSL_set_SSL_CTX(ssl, newctx); + return SSL_TLSEXT_ERR_OK; + } + /* Not found, but use initial context */ + if (!strict) + return SSL_TLSEXT_ERR_OK; + return SSL_TLSEXT_ERR_ALERT_FATAL; +} + +static int meth_sni(lua_State *L) +{ + int strict; + SSL_CTX *aux; + const char *name; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl->ssl); + p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); + if (pctx->mode == LSEC_MODE_CLIENT) { + name = luaL_checkstring(L, 2); + SSL_set_tlsext_host_name(ssl->ssl, name); + return 0; + } else if (pctx->mode == LSEC_MODE_SERVER) { + luaL_checktype(L, 2, LUA_TTABLE); + strict = lua_toboolean(L, 3); + /* Check if the table contains only (string -> context) */ + lua_pushnil(L); + while (lua_next(L, 2)) { + luaL_checkstring(L, -2); + aux = lsec_checkcontext(L, -1); + /* Set callback in every context */ + SSL_CTX_set_tlsext_servername_callback(aux, sni_cb); + /* leave the next key on the stack */ + lua_pop(L, 1); + } + /* Save table in the register */ + luaL_getmetatable(L, "SSL:SNI:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_newtable(L); + lua_pushstring(L, "map"); + lua_pushvalue(L, 2); + lua_settable(L, -3); + lua_pushstring(L, "strict"); + lua_pushboolean(L, strict); + lua_settable(L, -3); + lua_settable(L, -3); + /* Set callback in the default context */ + SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); + } + return 0; +} + +static int meth_getsniname(lua_State *L) +{ + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + const char *name = SSL_get_servername(ssl->ssl, TLSEXT_NAMETYPE_host_name); + if (name) + lua_pushstring(L, name); + else + lua_pushnil(L); + return 1; +} + +static int meth_getalpn(lua_State *L) +{ + unsigned len; + const unsigned char *data; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + SSL_get0_alpn_selected(ssl->ssl, &data, &len); + if (data == NULL && len == 0) + lua_pushnil(L); + else + lua_pushlstring(L, (const char*)data, len); + return 1; +} + +static int meth_copyright(lua_State *L) +{ + lua_pushstring(L, "LuaSec 1.3.2 - Copyright (C) 2006-2023 Bruno Silvestre, UFG" +#if defined(WITH_LUASOCKET) + "\nLuaSocket 3.0-RC1 - Copyright (C) 2004-2013 Diego Nehab" +#endif + ); + return 1; +} + +#if defined(LSEC_ENABLE_DANE) +static int meth_dane(lua_State *L) +{ + int ret; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + ret = SSL_dane_enable(ssl->ssl, luaL_checkstring(L, 2)); + lua_pushboolean(L, (ret > 0)); + return 1; +} + +static int meth_tlsa(lua_State *L) +{ + int ret; + size_t len; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + uint8_t usage = (uint8_t)luaL_checkinteger(L, 2); + uint8_t selector = (uint8_t)luaL_checkinteger(L, 3); + uint8_t mtype = (uint8_t)luaL_checkinteger(L, 4); + unsigned char *data = (unsigned char*)luaL_checklstring(L, 5, &len); + + ERR_clear_error(); + ret = SSL_dane_tlsa_add(ssl->ssl, usage, selector, mtype, data, len); + lua_pushboolean(L, (ret > 0)); + + return 1; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * SSL methods + */ +static luaL_Reg methods[] = { + {"close", meth_close}, + {"getalpn", meth_getalpn}, + {"getfd", meth_getfd}, + {"getfinished", meth_getfinished}, + {"getpeercertificate", meth_getpeercertificate}, + {"getlocalcertificate", meth_getlocalcertificate}, + {"getpeerchain", meth_getpeerchain}, + {"getlocalchain", meth_getlocalchain}, + {"getpeerverification", meth_getpeerverification}, + {"getpeerfinished", meth_getpeerfinished}, + {"exportkeyingmaterial",meth_exportkeyingmaterial}, + {"getsniname", meth_getsniname}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"dirty", meth_dirty}, + {"dohandshake", meth_handshake}, + {"receive", meth_receive}, + {"send", meth_send}, + {"settimeout", meth_settimeout}, + {"sni", meth_sni}, + {"want", meth_want}, +#if defined(LSEC_ENABLE_DANE) + {"setdane", meth_dane}, + {"settlsa", meth_tlsa}, +#endif + {NULL, NULL} +}; + +/** + * SSL metamethods. + */ +static luaL_Reg meta[] = { + {"__close", meth_destroy}, + {"__gc", meth_destroy}, + {"__tostring", meth_tostring}, + {NULL, NULL} +}; + +/** + * SSL functions. + */ +static luaL_Reg funcs[] = { + {"compression", meth_compression}, + {"create", meth_create}, + {"info", meth_info}, + {"setfd", meth_setfd}, + {"setmethod", meth_setmethod}, + {"copyright", meth_copyright}, + {NULL, NULL} +}; + +/** + * Initialize modules. + */ +LSEC_API int luaopen_ssl_core(lua_State *L) +{ +#ifndef LSEC_API_OPENSSL_1_1_0 + /* Initialize SSL */ + if (!SSL_library_init()) { + lua_pushstring(L, "unable to initialize SSL library"); + lua_error(L); + } + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); +#endif + +#if defined(WITH_LUASOCKET) + /* Initialize internal library */ + socket_open(); +#endif + + luaL_newmetatable(L, "SSL:SNI:Registry"); + + /* Register the functions and tables */ + luaL_newmetatable(L, "SSL:Connection"); + setfuncs(L, meta); + + luaL_newlib(L, methods); + lua_setfield(L, -2, "__index"); + + luaL_newlib(L, funcs); + + lua_pushstring(L, "SOCKET_INVALID"); + lua_pushinteger(L, SOCKET_INVALID); + lua_rawset(L, -3); + + return 1; +} + +//------------------------------------------------------------------------------ + +#if defined(_MSC_VER) + +/* Empty implementation to allow building with LuaRocks and MS compilers */ +LSEC_API int luaopen_ssl(lua_State *L) { + lua_pushstring(L, "you should not call this function"); + lua_error(L); + return 0; +} + +#endif diff --git a/vendor/luasec/src/ssl.h b/vendor/luasec/src/ssl.h new file mode 100644 index 00000000..2362ffe5 --- /dev/null +++ b/vendor/luasec/src/ssl.h @@ -0,0 +1,41 @@ +#ifndef LSEC_SSL_H +#define LSEC_SSL_H + +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2006-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include +#include + +#include +#include +#include +#include + +#include "compat.h" +#include "context.h" + +#define LSEC_STATE_NEW 1 +#define LSEC_STATE_CONNECTED 2 +#define LSEC_STATE_CLOSED 3 + +#define LSEC_IO_SSL -100 + +typedef struct t_ssl_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; + SSL *ssl; + int state; + int error; +} t_ssl; +typedef t_ssl* p_ssl; + +LSEC_API int luaopen_ssl_core(lua_State *L); + +#endif diff --git a/vendor/luasec/src/ssl.lua b/vendor/luasec/src/ssl.lua new file mode 100644 index 00000000..e22c047e --- /dev/null +++ b/vendor/luasec/src/ssl.lua @@ -0,0 +1,313 @@ +------------------------------------------------------------------------------ +-- LuaSec 1.3.2 +-- +-- Copyright (C) 2006-2023 Bruno Silvestre +-- +------------------------------------------------------------------------------ + +local core = require("ssl.core") +local context = require("ssl.context") +local x509 = require("ssl.x509") +local config = require("ssl.config") + +local unpack = table.unpack or unpack + +-- We must prevent the contexts to be collected before the connections, +-- otherwise the C registry will be cleared. +local registry = setmetatable({}, {__mode="k"}) + +-- +-- +-- +local function optexec(func, param, ctx) + if param then + if type(param) == "table" then + return func(ctx, unpack(param)) + else + return func(ctx, param) + end + end + return true +end + +-- +-- Convert an array of strings to wire-format +-- +local function array2wireformat(array) + local str = "" + for k, v in ipairs(array) do + if type(v) ~= "string" then return nil end + local len = #v + if len == 0 then + return nil, "invalid ALPN name (empty string)" + elseif len > 255 then + return nil, "invalid ALPN name (length > 255)" + end + str = str .. string.char(len) .. v + end + if str == "" then return nil, "invalid ALPN list (empty)" end + return str +end + +-- +-- Convert wire-string format to array +-- +local function wireformat2array(str) + local i = 1 + local array = {} + while i < #str do + local len = str:byte(i) + array[#array + 1] = str:sub(i + 1, i + len) + i = i + len + 1 + end + return array +end + +-- +-- +-- +local function newcontext(cfg) + local succ, msg, ctx + -- Create the context + ctx, msg = context.create(cfg.protocol) + if not ctx then return nil, msg end + -- Mode + succ, msg = context.setmode(ctx, cfg.mode) + if not succ then return nil, msg end + local certificates = cfg.certificates + if not certificates then + certificates = { + { certificate = cfg.certificate, key = cfg.key, password = cfg.password } + } + end + for _, certificate in ipairs(certificates) do + -- Load the key + if certificate.key then + if certificate.password and + type(certificate.password) ~= "function" and + type(certificate.password) ~= "string" + then + return nil, "invalid password type" + end + succ, msg = context.loadkey(ctx, certificate.key, certificate.password) + if not succ then return nil, msg end + end + -- Load the certificate(s) + if certificate.certificate then + succ, msg = context.loadcert(ctx, certificate.certificate) + if not succ then return nil, msg end + if certificate.key and context.checkkey then + succ = context.checkkey(ctx) + if not succ then return nil, "private key does not match public key" end + end + end + end + -- Load the CA certificates + if cfg.cafile or cfg.capath then + succ, msg = context.locations(ctx, cfg.cafile, cfg.capath) + if not succ then return nil, msg end + end + -- Set SSL ciphers + if cfg.ciphers then + succ, msg = context.setcipher(ctx, cfg.ciphers) + if not succ then return nil, msg end + end + -- Set SSL cipher suites + if cfg.ciphersuites then + succ, msg = context.setciphersuites(ctx, cfg.ciphersuites) + if not succ then return nil, msg end + end + -- Set the verification options + succ, msg = optexec(context.setverify, cfg.verify, ctx) + if not succ then return nil, msg end + -- Set SSL options + succ, msg = optexec(context.setoptions, cfg.options, ctx) + if not succ then return nil, msg end + -- Set the depth for certificate verification + if cfg.depth then + succ, msg = context.setdepth(ctx, cfg.depth) + if not succ then return nil, msg end + end + + -- NOTE: Setting DH parameters and elliptic curves needs to come after + -- setoptions(), in case the user has specified the single_{dh,ecdh}_use + -- options. + + -- Set DH parameters + if cfg.dhparam then + if type(cfg.dhparam) ~= "function" then + return nil, "invalid DH parameter type" + end + context.setdhparam(ctx, cfg.dhparam) + end + + -- Set elliptic curves + if (not config.algorithms.ec) and (cfg.curve or cfg.curveslist) then + return false, "elliptic curves not supported" + end + if config.capabilities.curves_list and cfg.curveslist then + succ, msg = context.setcurveslist(ctx, cfg.curveslist) + if not succ then return nil, msg end + elseif cfg.curve then + succ, msg = context.setcurve(ctx, cfg.curve) + if not succ then return nil, msg end + end + + -- Set extra verification options + if cfg.verifyext and ctx.setverifyext then + succ, msg = optexec(ctx.setverifyext, cfg.verifyext, ctx) + if not succ then return nil, msg end + end + + -- ALPN + if cfg.mode == "server" and cfg.alpn then + if type(cfg.alpn) == "function" then + local alpncb = cfg.alpn + -- This callback function has to return one value only + succ, msg = context.setalpncb(ctx, function(str) + local protocols = alpncb(wireformat2array(str)) + if type(protocols) == "string" then + protocols = { protocols } + elseif type(protocols) ~= "table" then + return nil + end + return (array2wireformat(protocols)) -- use "()" to drop error message + end) + if not succ then return nil, msg end + elseif type(cfg.alpn) == "table" then + local protocols = cfg.alpn + -- check if array is valid before use it + succ, msg = array2wireformat(protocols) + if not succ then return nil, msg end + -- This callback function has to return one value only + succ, msg = context.setalpncb(ctx, function() + return (array2wireformat(protocols)) -- use "()" to drop error message + end) + if not succ then return nil, msg end + else + return nil, "invalid ALPN parameter" + end + elseif cfg.mode == "client" and cfg.alpn then + local alpn + if type(cfg.alpn) == "string" then + alpn, msg = array2wireformat({ cfg.alpn }) + elseif type(cfg.alpn) == "table" then + alpn, msg = array2wireformat(cfg.alpn) + else + return nil, "invalid ALPN parameter" + end + if not alpn then return nil, msg end + succ, msg = context.setalpn(ctx, alpn) + if not succ then return nil, msg end + end + + -- PSK + if config.capabilities.psk and cfg.psk then + if cfg.mode == "client" then + if type(cfg.psk) ~= "function" then + return nil, "invalid PSK configuration" + end + succ = context.setclientpskcb(ctx, cfg.psk) + if not succ then return nil, msg end + elseif cfg.mode == "server" then + if type(cfg.psk) == "function" then + succ, msg = context.setserverpskcb(ctx, cfg.psk) + if not succ then return nil, msg end + elseif type(cfg.psk) == "table" then + if type(cfg.psk.hint) == "string" and type(cfg.psk.callback) == "function" then + succ, msg = context.setpskhint(ctx, cfg.psk.hint) + if not succ then return succ, msg end + succ = context.setserverpskcb(ctx, cfg.psk.callback) + if not succ then return succ, msg end + else + return nil, "invalid PSK configuration" + end + else + return nil, "invalid PSK configuration" + end + end + end + + if config.capabilities.dane and cfg.dane then + if type(cfg.dane) == "table" then + context.setdane(ctx, unpack(cfg.dane)) + else + context.setdane(ctx) + end + end + + return ctx +end + +-- +-- +-- +local function wrap(sock, cfg) + local ctx, msg + if type(cfg) == "table" then + ctx, msg = newcontext(cfg) + if not ctx then return nil, msg end + else + ctx = cfg + end + local s, msg = core.create(ctx) + if s then + core.setfd(s, sock:getfd()) + sock:setfd(core.SOCKET_INVALID) + registry[s] = ctx + return s + end + return nil, msg +end + +-- +-- Extract connection information. +-- +local function info(ssl, field) + local str, comp, err, protocol + comp, err = core.compression(ssl) + if err then + return comp, err + end + -- Avoid parser + if field == "compression" then + return comp + end + local info = {compression = comp} + str, info.bits, info.algbits, protocol = core.info(ssl) + if str then + info.cipher, info.protocol, info.key, + info.authentication, info.encryption, info.mac = + string.match(str, + "^(%S+)%s+(%S+)%s+Kx=(%S+)%s+Au=(%S+)%s+Enc=(%S+)%s+Mac=(%S+)") + info.export = (string.match(str, "%sexport%s*$") ~= nil) + end + if protocol then + info.protocol = protocol + end + if field then + return info[field] + end + -- Empty? + return ( (next(info)) and info ) +end + +-- +-- Set method for SSL connections. +-- +core.setmethod("info", info) + +-------------------------------------------------------------------------------- +-- Export module +-- + +local _M = { + _VERSION = "1.3.2", + _COPYRIGHT = core.copyright(), + config = config, + loadcertificate = x509.load, + newcontext = newcontext, + wrap = wrap, +} + +return _M diff --git a/vendor/luasec/src/x509.c b/vendor/luasec/src/x509.c new file mode 100644 index 00000000..6833d46f --- /dev/null +++ b/vendor/luasec/src/x509.c @@ -0,0 +1,750 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2014-2023 Kim Alvefur, Paul Aurich, Tobias Markmann, Matthew Wild + * Copyright (C) 2014-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include +#include + +#if defined(WIN32) +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "x509.h" + + +#ifndef LSEC_API_OPENSSL_1_1_0 +#define X509_get0_notBefore X509_get_notBefore +#define X509_get0_notAfter X509_get_notAfter +#define ASN1_STRING_get0_data ASN1_STRING_data +#endif + +static const char* hex_tab = "0123456789abcdef"; + +/** + * Push the certificate on the stack. + */ +void lsec_pushx509(lua_State* L, X509 *cert) +{ + p_x509 cert_obj = (p_x509)lua_newuserdata(L, sizeof(t_x509)); + cert_obj->cert = cert; + cert_obj->encode = LSEC_AI5_STRING; + luaL_getmetatable(L, "SSL:Certificate"); + lua_setmetatable(L, -2); +} + +/** + * Return the OpenSSL certificate X509. + */ +X509* lsec_checkx509(lua_State* L, int idx) +{ + return ((p_x509)luaL_checkudata(L, idx, "SSL:Certificate"))->cert; +} + +/** + * Return LuaSec certificate X509 representation. + */ +p_x509 lsec_checkp_x509(lua_State* L, int idx) +{ + return (p_x509)luaL_checkudata(L, idx, "SSL:Certificate"); +} + +/*---------------------------------------------------------------------------*/ + +#if defined(LUASEC_INET_NTOP) +/* + * For WinXP (SP3), set the following preprocessor macros: + * LUASEC_INET_NTOP + * WINVER=0x0501 + * _WIN32_WINNT=0x0501 + * NTDDI_VERSION=0x05010300 + * + * For IPv6 addresses, you need to add IPv6 Protocol to your interface. + * + */ +static const char *inet_ntop(int af, const char *src, char *dst, socklen_t size) +{ + int addrsize; + struct sockaddr *addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + + switch (af) { + case AF_INET: + memset((void*)&addr4, 0, sizeof(addr4)); + addr4.sin_family = AF_INET; + memcpy((void*)&addr4.sin_addr, src, sizeof(struct in_addr)); + addr = (struct sockaddr*)&addr4; + addrsize = sizeof(struct sockaddr_in); + break; + case AF_INET6: + memset((void*)&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + memcpy((void*)&addr6.sin6_addr, src, sizeof(struct in6_addr)); + addr = (struct sockaddr*)&addr6; + addrsize = sizeof(struct sockaddr_in6); + break; + default: + return NULL; + } + + if(getnameinfo(addr, addrsize, dst, size, NULL, 0, NI_NUMERICHOST) != 0) + return NULL; + return dst; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Convert the buffer 'in' to hexadecimal. + */ +static void to_hex(const char* in, int length, char* out) +{ + int i; + for (i = 0; i < length; i++) { + out[i*2] = hex_tab[(in[i] >> 4) & 0xF]; + out[i*2+1] = hex_tab[(in[i]) & 0xF]; + } +} + +/** + * Converts the ASN1_OBJECT into a textual representation and put it + * on the Lua stack. + */ +static void push_asn1_objname(lua_State* L, ASN1_OBJECT *object, int no_name) +{ + char buffer[256]; + int len = OBJ_obj2txt(buffer, sizeof(buffer), object, no_name); + len = (len < sizeof(buffer)) ? len : sizeof(buffer); + lua_pushlstring(L, buffer, len); +} + +/** + * Push the ASN1 string on the stack. + */ +static void push_asn1_string(lua_State* L, ASN1_STRING *string, int encode) +{ + int len; + unsigned char *data; + if (!string) { + lua_pushnil(L); + return; + } + switch (encode) { + case LSEC_AI5_STRING: + lua_pushlstring(L, (char*)ASN1_STRING_get0_data(string), ASN1_STRING_length(string)); + break; + case LSEC_UTF8_STRING: + len = ASN1_STRING_to_UTF8(&data, string); + if (len >= 0) { + lua_pushlstring(L, (char*)data, len); + OPENSSL_free(data); + } + else + lua_pushnil(L); + } +} + +/** + * Return a human readable time. + */ +static int push_asn1_time(lua_State *L, const ASN1_UTCTIME *tm) +{ + char *tmp; + long size; + BIO *out = BIO_new(BIO_s_mem()); + ASN1_TIME_print(out, tm); + size = BIO_get_mem_data(out, &tmp); + lua_pushlstring(L, tmp, size); + BIO_free(out); + return 1; +} + +/** + * Return a human readable IP address. + */ +static void push_asn1_ip(lua_State *L, ASN1_STRING *string) +{ + int af; + char dst[INET6_ADDRSTRLEN]; + unsigned char *ip = (unsigned char*)ASN1_STRING_get0_data(string); + switch(ASN1_STRING_length(string)) { + case 4: + af = AF_INET; + break; + case 16: + af = AF_INET6; + break; + default: + lua_pushnil(L); + return; + } + if(inet_ntop(af, ip, dst, INET6_ADDRSTRLEN)) + lua_pushstring(L, dst); + else + lua_pushnil(L); +} + +/** + * + */ +static int push_subtable(lua_State* L, int idx) +{ + lua_pushvalue(L, -1); + lua_gettable(L, idx-1); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_settable(L, idx-3); + lua_replace(L, -2); /* Replace key with table */ + return 1; + } + lua_replace(L, -2); /* Replace key with table */ + return 0; +} + +/** + * Retrieve the general names from the object. + */ +static int push_x509_name(lua_State* L, X509_NAME *name, int encode) +{ + int i; + int n_entries; + ASN1_OBJECT *object; + X509_NAME_ENTRY *entry; + lua_newtable(L); + n_entries = X509_NAME_entry_count(name); + for (i = 0; i < n_entries; i++) { + entry = X509_NAME_get_entry(name, i); + object = X509_NAME_ENTRY_get_object(entry); + lua_newtable(L); + push_asn1_objname(L, object, 1); + lua_setfield(L, -2, "oid"); + push_asn1_objname(L, object, 0); + lua_setfield(L, -2, "name"); + push_asn1_string(L, X509_NAME_ENTRY_get_data(entry), encode); + lua_setfield(L, -2, "value"); + lua_rawseti(L, -2, i+1); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ + +/** + * Retrieve the Subject from the certificate. + */ +static int meth_subject(lua_State* L) +{ + p_x509 px = lsec_checkp_x509(L, 1); + return push_x509_name(L, X509_get_subject_name(px->cert), px->encode); +} + +/** + * Retrieve the Issuer from the certificate. + */ +static int meth_issuer(lua_State* L) +{ + p_x509 px = lsec_checkp_x509(L, 1); + return push_x509_name(L, X509_get_issuer_name(px->cert), px->encode); +} + +/** + * Retrieve the extensions from the certificate. + */ +int meth_extensions(lua_State* L) +{ + int j; + int i = -1; + int n_general_names; + OTHERNAME *otherName; + X509_EXTENSION *extension; + GENERAL_NAME *general_name; + STACK_OF(GENERAL_NAME) *values; + p_x509 px = lsec_checkp_x509(L, 1); + X509 *peer = px->cert; + + /* Return (ret) */ + lua_newtable(L); + + while ((i = X509_get_ext_by_NID(peer, NID_subject_alt_name, i)) != -1) { + extension = X509_get_ext(peer, i); + if (extension == NULL) + break; + values = X509V3_EXT_d2i(extension); + if (values == NULL) + break; + + /* Push ret[oid] */ + push_asn1_objname(L, X509_EXTENSION_get_object(extension), 1); + push_subtable(L, -2); + + /* Set ret[oid].name = name */ + push_asn1_objname(L, X509_EXTENSION_get_object(extension), 0); + lua_setfield(L, -2, "name"); + + n_general_names = sk_GENERAL_NAME_num(values); + for (j = 0; j < n_general_names; j++) { + general_name = sk_GENERAL_NAME_value(values, j); + switch (general_name->type) { + case GEN_OTHERNAME: + otherName = general_name->d.otherName; + push_asn1_objname(L, otherName->type_id, 1); + if (push_subtable(L, -2)) { + push_asn1_objname(L, otherName->type_id, 0); + lua_setfield(L, -2, "name"); + } + push_asn1_string(L, otherName->value->value.asn1_string, px->encode); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_DNS: + lua_pushstring(L, "dNSName"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.dNSName, px->encode); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_EMAIL: + lua_pushstring(L, "rfc822Name"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.rfc822Name, px->encode); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_URI: + lua_pushstring(L, "uniformResourceIdentifier"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.uniformResourceIdentifier, px->encode); + lua_rawseti(L, -2, lua_rawlen(L, -2)+1); + lua_pop(L, 1); + break; + case GEN_IPADD: + lua_pushstring(L, "iPAddress"); + push_subtable(L, -2); + push_asn1_ip(L, general_name->d.iPAddress); + lua_rawseti(L, -2, lua_rawlen(L, -2)+1); + lua_pop(L, 1); + break; + case GEN_X400: + /* x400Address */ + /* not supported */ + break; + case GEN_DIRNAME: + /* directoryName */ + /* not supported */ + break; + case GEN_EDIPARTY: + /* ediPartyName */ + /* not supported */ + break; + case GEN_RID: + /* registeredID */ + /* not supported */ + break; + } + GENERAL_NAME_free(general_name); + } + sk_GENERAL_NAME_free(values); + lua_pop(L, 1); /* ret[oid] */ + i++; /* Next extension */ + } + return 1; +} + +/** + * Convert the certificate to PEM format. + */ +static int meth_pem(lua_State* L) +{ + char* data; + long bytes; + X509* cert = lsec_checkx509(L, 1); + BIO *bio = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_X509(bio, cert)) { + lua_pushnil(L); + return 1; + } + bytes = BIO_get_mem_data(bio, &data); + if (bytes > 0) + lua_pushlstring(L, data, bytes); + else + lua_pushnil(L); + BIO_free(bio); + return 1; +} + +/** + * Extract public key in PEM format. + */ +static int meth_pubkey(lua_State* L) +{ + char* data; + long bytes; + int ret = 1; + X509* cert = lsec_checkx509(L, 1); + BIO *bio = BIO_new(BIO_s_mem()); + EVP_PKEY *pkey = X509_get_pubkey(cert); + if(PEM_write_bio_PUBKEY(bio, pkey)) { + bytes = BIO_get_mem_data(bio, &data); + if (bytes > 0) { + lua_pushlstring(L, data, bytes); + switch(EVP_PKEY_base_id(pkey)) { + case EVP_PKEY_RSA: + lua_pushstring(L, "RSA"); + break; + case EVP_PKEY_DSA: + lua_pushstring(L, "DSA"); + break; + case EVP_PKEY_DH: + lua_pushstring(L, "DH"); + break; + case EVP_PKEY_EC: + lua_pushstring(L, "EC"); + break; + default: + lua_pushstring(L, "Unknown"); + break; + } + lua_pushinteger(L, EVP_PKEY_bits(pkey)); + ret = 3; + } + else + lua_pushnil(L); + } + else + lua_pushnil(L); + /* Cleanup */ + BIO_free(bio); + EVP_PKEY_free(pkey); + return ret; +} + +/** + * Compute the fingerprint. + */ +static int meth_digest(lua_State* L) +{ + unsigned int bytes; + const EVP_MD *digest = NULL; + unsigned char buffer[EVP_MAX_MD_SIZE]; + char hex_buffer[EVP_MAX_MD_SIZE*2]; + X509 *cert = lsec_checkx509(L, 1); + const char *str = luaL_optstring(L, 2, NULL); + if (!str) + digest = EVP_sha1(); + else { + if (!strcmp(str, "sha1")) + digest = EVP_sha1(); + else if (!strcmp(str, "sha256")) + digest = EVP_sha256(); + else if (!strcmp(str, "sha512")) + digest = EVP_sha512(); + } + if (!digest) { + lua_pushnil(L); + lua_pushfstring(L, "digest algorithm not supported (%s)", str); + return 2; + } + if (!X509_digest(cert, digest, buffer, &bytes)) { + lua_pushnil(L); + lua_pushfstring(L, "error processing the certificate (%s)", + ERR_reason_error_string(ERR_get_error())); + return 2; + } + to_hex((char*)buffer, bytes, hex_buffer); + lua_pushlstring(L, hex_buffer, bytes*2); + return 1; +} + +/** + * Check if the certificate is valid in a given time. + */ +static int meth_valid_at(lua_State* L) +{ + int nb, na; + X509* cert = lsec_checkx509(L, 1); + time_t time = luaL_checkinteger(L, 2); + nb = X509_cmp_time(X509_get0_notBefore(cert), &time); + time -= 1; + na = X509_cmp_time(X509_get0_notAfter(cert), &time); + lua_pushboolean(L, nb == -1 && na == 1); + return 1; +} + +/** + * Return the serial number. + */ +static int meth_serial(lua_State *L) +{ + char *tmp; + BIGNUM *bn; + ASN1_INTEGER *serial; + X509* cert = lsec_checkx509(L, 1); + serial = X509_get_serialNumber(cert); + bn = ASN1_INTEGER_to_BN(serial, NULL); + tmp = BN_bn2hex(bn); + lua_pushstring(L, tmp); + BN_free(bn); + OPENSSL_free(tmp); + return 1; +} + +/** + * Return not before date. + */ +static int meth_notbefore(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + return push_asn1_time(L, X509_get0_notBefore(cert)); +} + +/** + * Return not after date. + */ +static int meth_notafter(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + return push_asn1_time(L, X509_get0_notAfter(cert)); +} + +/** + * Check if this certificate issued some other certificate + */ +static int meth_issued(lua_State* L) +{ + int ret, i, len; + + X509_STORE_CTX* ctx = NULL; + X509_STORE* root = NULL; + STACK_OF(X509)* chain = NULL; + + X509* issuer = lsec_checkx509(L, 1); + X509* subject = lsec_checkx509(L, 2); + X509* cert = NULL; + + len = lua_gettop(L); + + /* Check that all arguments are certificates */ + + for (i = 3; i <= len; i++) { + lsec_checkx509(L, i); + } + + /* Before allocating things that require freeing afterwards */ + + chain = sk_X509_new_null(); + ctx = X509_STORE_CTX_new(); + root = X509_STORE_new(); + + if (ctx == NULL || root == NULL) { + lua_pushnil(L); + lua_pushstring(L, "X509_STORE_new() or X509_STORE_CTX_new() error"); + ret = 2; + goto cleanup; + } + + ret = X509_STORE_add_cert(root, issuer); + + if(!ret) { + lua_pushnil(L); + lua_pushstring(L, "X509_STORE_add_cert() error"); + ret = 2; + goto cleanup; + } + + for (i = 3; i <= len && lua_isuserdata(L, i); i++) { + cert = lsec_checkx509(L, i); + sk_X509_push(chain, cert); + } + + ret = X509_STORE_CTX_init(ctx, root, subject, chain); + + if(!ret) { + lua_pushnil(L); + lua_pushstring(L, "X509_STORE_CTX_init() error"); + ret = 2; + goto cleanup; + } + + /* Actual verification */ + if (X509_verify_cert(ctx) <= 0) { + ret = X509_STORE_CTX_get_error(ctx); + lua_pushnil(L); + lua_pushstring(L, X509_verify_cert_error_string(ret)); + ret = 2; + } else { + lua_pushboolean(L, 1); + ret = 1; + } + +cleanup: + + if (ctx != NULL) { + X509_STORE_CTX_free(ctx); + } + + if (chain != NULL) { + X509_STORE_free(root); + } + + sk_X509_free(chain); + + return ret; +} + +/** + * Collect X509 objects. + */ +static int meth_destroy(lua_State* L) +{ + p_x509 px = lsec_checkp_x509(L, 1); + if (px->cert) { + X509_free(px->cert); + px->cert = NULL; + } + return 0; +} + +static int meth_tostring(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + lua_pushfstring(L, "X509 certificate: %p", cert); + return 1; +} + +/** + * Set the encode for ASN.1 string. + */ +static int meth_set_encode(lua_State* L) +{ + int succ = 0; + p_x509 px = lsec_checkp_x509(L, 1); + const char *enc = luaL_checkstring(L, 2); + if (strncmp(enc, "ai5", 3) == 0) { + succ = 1; + px->encode = LSEC_AI5_STRING; + } else if (strncmp(enc, "utf8", 4) == 0) { + succ = 1; + px->encode = LSEC_UTF8_STRING; + } + lua_pushboolean(L, succ); + return 1; +} + +#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) +/** + * Get signature name. + */ +static int meth_get_signature_name(lua_State* L) +{ + p_x509 px = lsec_checkp_x509(L, 1); + int nid = X509_get_signature_nid(px->cert); + const char *name = OBJ_nid2sn(nid); + if (!name) + lua_pushnil(L); + else + lua_pushstring(L, name); + return 1; +} +#endif + +/*---------------------------------------------------------------------------*/ + +static int load_cert(lua_State* L) +{ + X509 *cert; + size_t bytes; + const char* data; + BIO *bio = BIO_new(BIO_s_mem()); + data = luaL_checklstring(L, 1, &bytes); + BIO_write(bio, data, bytes); + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (cert) + lsec_pushx509(L, cert); + else + lua_pushnil(L); + BIO_free(bio); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +/** + * Certificate methods. + */ +static luaL_Reg methods[] = { + {"digest", meth_digest}, + {"setencode", meth_set_encode}, + {"extensions", meth_extensions}, +#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) + {"getsignaturename", meth_get_signature_name}, +#endif + {"issuer", meth_issuer}, + {"notbefore", meth_notbefore}, + {"notafter", meth_notafter}, + {"issued", meth_issued}, + {"pem", meth_pem}, + {"pubkey", meth_pubkey}, + {"serial", meth_serial}, + {"subject", meth_subject}, + {"validat", meth_valid_at}, + {NULL, NULL} +}; + +/** + * X509 metamethods. + */ +static luaL_Reg meta[] = { + {"__close", meth_destroy}, + {"__gc", meth_destroy}, + {"__tostring", meth_tostring}, + {NULL, NULL} +}; + +/** + * X509 functions. + */ +static luaL_Reg funcs[] = { + {"load", load_cert}, + {NULL, NULL} +}; + +/*--------------------------------------------------------------------------*/ + +LSEC_API int luaopen_ssl_x509(lua_State *L) +{ + /* Register the functions and tables */ + luaL_newmetatable(L, "SSL:Certificate"); + setfuncs(L, meta); + + luaL_newlib(L, methods); + lua_setfield(L, -2, "__index"); + + luaL_newlib(L, funcs); + + return 1; +} diff --git a/vendor/luasec/src/x509.h b/vendor/luasec/src/x509.h new file mode 100644 index 00000000..acdbe6c0 --- /dev/null +++ b/vendor/luasec/src/x509.h @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + * LuaSec 1.3.2 + * + * Copyright (C) 2014-2023 Kim Alvefur, Paul Aurich, Tobias Markmann, Matthew Wild + * Copyright (C) 2013-2023 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#ifndef LSEC_X509_H +#define LSEC_X509_H + +#include +#include + +#include "compat.h" + +/* We do not support UniversalString nor BMPString as ASN.1 String types */ +enum { LSEC_AI5_STRING, LSEC_UTF8_STRING }; + +typedef struct t_x509_ { + X509 *cert; + int encode; +} t_x509; +typedef t_x509* p_x509; + +void lsec_pushx509(lua_State* L, X509* cert); +X509* lsec_checkx509(lua_State* L, int idx); + +LSEC_API int luaopen_ssl_x509(lua_State *L); + +#endif diff --git a/vendor/luasocket/.editorconfig b/vendor/luasocket/.editorconfig new file mode 100644 index 00000000..56ad87df --- /dev/null +++ b/vendor/luasocket/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[{*.lua,*.rockspec,.luacheckrc}] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 4 + +[*.html] +indent_style = space +indent_size = 4 + +[*.{c,h}] +indent_style = space +indent_size = 4 diff --git a/vendor/luasocket/.gitignore b/vendor/luasocket/.gitignore new file mode 100644 index 00000000..9ed661cf --- /dev/null +++ b/vendor/luasocket/.gitignore @@ -0,0 +1,15 @@ +*.o +*.so +*.so.* +*.obj +*.lib +*.dll* +*.user +*.sdf +Debug +Release +*.manifest +*.swp +*.suo +x64 + diff --git a/vendor/luasocket/.luacheckrc b/vendor/luasocket/.luacheckrc new file mode 100644 index 00000000..8b25dd7a --- /dev/null +++ b/vendor/luasocket/.luacheckrc @@ -0,0 +1,31 @@ +unused_args = false +redefined = false +max_line_length = false + +not_globals = { + "string.len", + "table.getn", +} + +include_files = { + "**/*.lua", + "**/*.rockspec", + ".busted", + ".luacheckrc", +} + +exclude_files = { + "etc/*.lua", + "etc/**/*.lua", + "test/*.lua", + "test/**/*.lua", + "samples/*.lua", + "samples/**/*.lua", + "gem/*.lua", + "gem/**/*.lua", + -- GH Actions Lua Environment + ".lua", + ".luarocks", + ".install", +} + diff --git a/vendor/luasocket/CHANGELOG.md b/vendor/luasocket/CHANGELOG.md new file mode 100644 index 00000000..3a25186b --- /dev/null +++ b/vendor/luasocket/CHANGELOG.md @@ -0,0 +1,65 @@ +# Changelog + +## [v3.1.0](https://github.com/lunarmodules/luasocket/releases/v3.1.0) — 2022-07-27 + +* Add support for TCP Defer Accept – @Zash +* Add support for TCP Fast Open – @Zash +* Fix Windows (mingw32) builds – @goldenstein64 +* Avoid build warnings on 64-bit Windows – @rpatters1 + +## [v3.0.0](https://github.com/lunarmodules/luasocket/releases/v3.0.0) — 2022-03-25 + +The last time LuaSocket had a stable release tag was 14 years ago when 2.0.2 was tagged. +A v3 release candidate was tagged 9 years ago. +Since then it has been downloaded over 3 million times. +Additionally the Git repository regularly gets several hundred clones a day. +But 9 years is a long time and even the release candidate has grown a bit long in the tooth. +Many Linux distros have packaged the current Git HEAD or some specific tested point as dated or otherwise labeled releases. +256 commits later and having been migrated to the @lunarmodules org namespace on GitHub, please welcome v3. + +This release is a "safe-harbor" tag that represents a minimal amount of changes to get a release tagged. +Beyond some CI tooling, very little code has changed since migration to @lunarmodules ([5b18e47..e47d98f](https://github.com/lunarmodules/luasocket/compare/5b18e47..e47d98f?w=1)): + +* Lua 5.4.3+ support – @pkulchenko, @Zash +* Cleanup minor issues to get a code linter to pass – @Tieske, @jyoui, @alerque +* Update Visual Studio build rules for Lua 5.1 – @ewestbrook +* Set http transfer-encoding even without content-length – @tokenrove + +Prior to migration to @lunarmodules ([v3.0-rc1..5b18e47](https://github.com/lunarmodules/luasocket/compare/v3.0-rc1..5b18e47?w=1)) many things happened of which the author of this changelog is not fully apprised. +Your best bet if it affects your project somehow is to read the commit log & diffs yourself. + +## [v3.0-rc1](https://github.com/lunarmodules/luasocket/releases/v3.0-rc1) — 2013-06-14 + +Main changes for LuaSocket 3.0-rc1 are IPv6 support and Lua 5.2 compatibility. + +* Added: Compatible with Lua 5.2 + - Note that unless you define LUA_COMPAT_MODULE, package tables will not be exported as globals! +* Added: IPv6 support; + - Socket.connect and socket.bind support IPv6 addresses; + - Getpeername and getsockname support IPv6 addresses, and return the socket family as a third value; + - URL module updated to support IPv6 host names; + - New socket.tcp6 and socket.udp6 functions; + - New socket.dns.getaddrinfo and socket.dns.getnameinfo functions; +* Added: getoption method; +* Fixed: url.unescape was returning additional values; +* Fixed: mime.qp, mime.unqp, mime.b64, and mime.unb64 could mistaking their own stack slots for functions arguments; +* Fixed: Receiving zero-length datagram is now possible; +* Improved: Hidden all internal library symbols; +* Improved: Better error messages; +* Improved: Better documentation of socket options. +* Fixed: manual sample of HTTP authentication now uses correct "authorization" header (Alexandre Ittner); +* Fixed: failure on bind() was destroying the socket (Sam Roberts); +* Fixed: receive() returns immediatelly if prefix can satisfy bytes requested (M Joonas Pihlaja); +* Fixed: multicast didn't work on Windows, or anywhere else for that matter (Herbert Leuwer, Adrian Sietsma); +* Fixed: select() now reports an error when called with more sockets than FD_SETSIZE (Lorenzo Leonini); +* Fixed: manual links to home.html changed to index.html (Robert Hahn); +* Fixed: mime.unb64() would return an empty string on results that started with a null character (Robert Raschke); +* Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray); +* Fixed: calling sleep() with negative numbers could block forever, wasting CPU. Now it returns immediately (MPB); +* Improved: FTP commands are now sent in upper case to help buggy servers (Anders Eurenius); +* Improved: known headers now sent in canonic capitalization to help buggy servers (Joseph Stewart); +* Improved: Clarified tcp:receive() in the manual (MPB); +* Improved: Decent makefiles (LHF). +* Fixed: RFC links in documentation now point to IETF (Cosmin Apreutesei). + +## [v2.0.2](https://github.com/lunarmodules/luasocket/releases/v2.0.2) — 2007-09-11 diff --git a/vendor/luasocket/FIX b/vendor/luasocket/FIX new file mode 100644 index 00000000..40f30a15 --- /dev/null +++ b/vendor/luasocket/FIX @@ -0,0 +1,28 @@ + + + + + + +http was preserving old host header during redirects +fix smtp.send hang on source error +add create field to FTP and SMTP and fix HTTP ugliness +clean timeout argument to open functions in SMTP, HTTP and FTP +eliminate globals from namespaces created by module(). +url.absolute was not working when base_url was already parsed +http.request was redirecting even when the location header was empty +tcp{client}:shutdown() was checking for group instead of class. +tcp{client}:send() now returns i+sent-1... +get rid of a = socket.try() in the manual, except for protected cases. replace it with assert. +get rid of "base." kludge in package.loaded +check all "require("http")" etc in the manual. +make sure sock_gethostname.* only return success if the hp is not null! +change 'l' prefix in C libraries to 'c' to avoid clash with LHF libraries + don't forget the declarations in luasocket.h and mime.h!!! +setpeername was using udp{unconnected} +fixed a bug in http.lua that caused some requests to fail (Florian Berger) +fixed a bug in select.c that prevented sockets with descriptor 0 from working (Renato Maia) +fixed a "bug" that caused dns.toip to crash under uLinux +fixed a "bug" that caused a crash in gethostbyname under VMS +DEBUG and VERSION became _DEBUG and _VERSION +send returns the right value if input is "". Alexander Marinov diff --git a/vendor/luasocket/LICENSE b/vendor/luasocket/LICENSE new file mode 100644 index 00000000..a8ed03ea --- /dev/null +++ b/vendor/luasocket/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2004-2022 Diego Nehab + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/luasocket/README.md b/vendor/luasocket/README.md new file mode 100644 index 00000000..af722be6 --- /dev/null +++ b/vendor/luasocket/README.md @@ -0,0 +1,12 @@ +# LuaSocket + + +[![Build](https://img.shields.io/github/workflow/status/lunarmodules/luasocket/Build?label=Build=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Build) +[![Luacheck](https://img.shields.io/github/workflow/status/lunarmodules/luasocket/Luacheck?label=Luacheck&logo=Lua)](https://github.com/lunarmodules/luasocket/actions?workflow=Luacheck) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/lunarmodules/luasocket?label=Tag&logo=GitHub)](https://github.com/lunarmodules/luasocket/releases) +[![Luarocks](https://img.shields.io/luarocks/v/lunarmodules/luasocket?label=Luarocks&logo=Lua)](https://luarocks.org/modules/lunarmodules/luasocket) + +LuaSocket is a Lua extension library composed of two parts: + +1. a set of C modules that provide support for the TCP and UDP transport layers, and +2. a set of Lua modules that provide functions commonly needed by applications that deal with the Internet. diff --git a/vendor/luasocket/TODO b/vendor/luasocket/TODO new file mode 100644 index 00000000..a838fc02 --- /dev/null +++ b/vendor/luasocket/TODO @@ -0,0 +1,81 @@ +- bizarre default values for getnameinfo should throw error instead! + +> It's just too bad it can't talk to gmail - +> reason 1: they absolutely want TLS +> reason 2: unlike all the other SMTP implementations, they +> don't +> tolerate missing < > around adresses + +- document the new bind and connect behavior. +- shouldn't we instead make the code compatible to Lua 5.2 + without any compat stuff, and use a compatibility layer to + make it work on 5.1? +- add what's new to manual +- should there be an equivalent to tohostname for IPv6? +- should we add service name resolution as well to getaddrinfo? +- Maybe the sockaddr to presentation conversion should be done with getnameinfo()? + +- add http POST sample to manual + people keep asking stupid questions +- documentation of dirty/getfd/setfd is problematic because of portability + same for unix and serial. + what to do about this? add a stronger disclaimer? +- fix makefile with decent defaults? + +Done: + +- added IPv6 support to getsockname +- simplified getpeername implementation +- added family to return of getsockname and getpeername + and added modification to the manual to describe + +- connect and bind try all adresses returned by getaddrinfo +- document headers.lua? +- update copyright date everywhere? +- remove RCSID from files? +- move version to 2.1 rather than 2.1.1? +- fixed url package to support ipv6 hosts +- changed domain to family +- implement getfamily methods. + +- remove references to Lua 5.0 from documentation, add 5.2? +- update lua and luasocket version in samples in documentation +- document ipv5_v6only default option being set? +- document tcp6 and udp6 +- document dns.getaddrinfo +- documented zero-sized datagram change? + no. +- document unix socket and serial socket? add raw support? + no. +- document getoption +- merge luaL_typeerror into auxiliar to avoid using luaL prefix? + + + + + + + + + + +replace \r\n with \0xD\0xA in everything +New mime support + +ftp send should return server replies? +make sure there are no object files in the distribution tarball +http handling of 100-continue, see DB patch +DB ftp.lua bug. +test unix.c to return just a function and works with require"unix" +get rid of setmetatable(, nil) since packages don't need this anymore in 5.1 +compat-5.1 novo +ajeitar pra lua-5.1 + +adicionar exemplos de expansćo: pipe, local, named pipe +testar os options! + + +- Thread-unsafe functions to protect + gethostbyname(), gethostbyaddr(), gethostent(), +inet_ntoa(), strerror(), + diff --git a/vendor/luasocket/WISH b/vendor/luasocket/WISH new file mode 100644 index 00000000..e7e9c076 --- /dev/null +++ b/vendor/luasocket/WISH @@ -0,0 +1,22 @@ +... as an l-value to get all results of a function call? +at least ...[i] and #... +extend to full tuples? + +__and __or __not metamethods + +lua_tostring, lua_tonumber, lua_touseradta etc push values in stack +__tostring,__tonumber, __touserdata metamethods are checked +and expected to push an object of correct type on stack + +lua_rawtostring, lua_rawtonumber, lua_rawtouserdata don't +push anything on stack, return data of appropriate type, +skip metamethods and throw error if object not of exact type + +package.findfile exported +module not polluting the global namespace + +coxpcall with a coroutine pool for efficiency (reusing coroutines) + +exception mechanism formalized? just like the package system was. + +a nice bitlib in the core diff --git a/vendor/luasocket/logo.ps b/vendor/luasocket/logo.ps new file mode 100644 index 00000000..8b5809ab --- /dev/null +++ b/vendor/luasocket/logo.ps @@ -0,0 +1,210 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: Lua logo +%%Creator: lua@tecgraf.puc-rio.br +%%CreationDate: Wed Nov 29 19:04:04 EDT 2000 +%%BoundingBox: -45 0 1035 1080 +%%Pages: 1 +%%EndComments +%%EndProlog + +%------------------------------------------------------------------------------ +% +% Copyright (C) 1998-2000. All rights reserved. +% Graphic design by Alexandre Nakonechny (nako@openlink.com.br). +% PostScript programming by the Lua team (lua@tecgraf.puc-rio.br). +% +% Permission is hereby granted, without written agreement and without license +% or royalty fees, to use, copy, and distribute this logo for any purpose, +% including commercial applications, subject to the following conditions: +% +% * The origin of this logo must not be misrepresented; you must not +% claim that you drew the original logo. We recommend that you give credit +% to the graphics designer in all printed matter that includes the logo. +% +% * The only modification you can make is to adapt the orbiting text to +% your product name. +% +% * The logo can be used in any scale as long as the relative proportions +% of its elements are maintained. +% +%------------------------------------------------------------------------------ + +/LABEL (tekcoS) def + +%-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------ + +/PLANETCOLOR {0 0 0.5 setrgbcolor} bind def +/HOLECOLOR {1.0 setgray} bind def +/ORBITCOLOR {0.5 setgray} bind def +/LOGOFONT {/Helvetica 0.90} def +/LABELFONT {/Helvetica 0.36} def + +%------------------------------------------------------------------------------ + +/MOONCOLOR {PLANETCOLOR} bind def +/LOGOCOLOR {HOLECOLOR} bind def +/LABELCOLOR {ORBITCOLOR} bind def + +/LABELANGLE 325 def +/LOGO (Lua) def + +/DASHANGLE 10 def +/HALFDASHANGLE DASHANGLE 2 div def + +% moon radius. planet radius is 1. +/r 1 2 sqrt 2 div sub def + +/D {0 360 arc fill} bind def +/F {exch findfont exch scalefont setfont} bind def + +% place it nicely on the paper +/RESOLUTION 1024 def +RESOLUTION 2 div dup translate +RESOLUTION 2 div 2 sqrt div dup scale + +%-------------------------------------------------------------------- planet -- +PLANETCOLOR +0 0 1 D + +%---------------------------------------------------------------------- hole -- +HOLECOLOR +1 2 r mul sub dup r D + +%---------------------------------------------------------------------- moon -- +MOONCOLOR +1 1 r D + +%---------------------------------------------------------------------- logo -- +LOGOCOLOR +LOGOFONT +F +LOGO stringwidth pop 2 div neg +-0.5 moveto +LOGO show + +%------------------------------------------------------------------------------ +% based on code from Blue Book Program 10, on pages 167--169 +% available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar + +% str ptsize centerangle radius outsidecircletext -- +/outsidecircletext { + circtextdict begin + /radius exch def + /centerangle exch def + /ptsize exch def + /str exch def + + gsave + str radius ptsize findhalfangle + centerangle + add rotate + str + { /charcode exch def + ( ) dup 0 charcode put outsideplacechar + } forall + + grestore + end +} def + +% string radius ptsize findhalfangle halfangle +/findhalfangle { + 4 div add + exch + stringwidth pop 2 div + exch + 2 mul 3.1415926535 mul div 360 mul +} def + +/circtextdict 16 dict def +circtextdict begin + + /outsideplacechar { + /char exch def + /halfangle char radius ptsize findhalfangle def + gsave + halfangle neg rotate + 1.4 0 translate + 90 rotate + char stringwidth pop 2 div neg 0 moveto + char show + grestore + halfangle 2 mul neg rotate + } def + +end + +%--------------------------------------------------------------------- label -- +LABELFONT +F + +/LABELSIZE LABELFONT exch pop def +/LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def + + +/HALFANGLE + LABEL LABELRADIUS LABELSIZE findhalfangle + HALFDASHANGLE div ceiling HALFDASHANGLE mul +def + +/LABELANGLE + 60 LABELANGLE HALFANGLE sub + lt + { + HALFANGLE + HALFANGLE DASHANGLE div floor DASHANGLE mul + eq + {LABELANGLE DASHANGLE div ceiling DASHANGLE mul} + {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add} + ifelse + } + {HALFANGLE 60 add} + ifelse +def + +LABELCOLOR +LABEL +LABELSIZE +LABELANGLE +LABELRADIUS +outsidecircletext + +%--------------------------------------------------------------------- orbit -- +ORBITCOLOR +0.03 setlinewidth +[1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash +newpath +0 0 +1 r add +3 copy +30 +LABELANGLE HALFANGLE add +arcn +stroke +60 +LABELANGLE HALFANGLE sub +2 copy +lt {arc stroke} {4 {pop} repeat} ifelse + +%------------------------------------------------------------------ copyright -- +/COPYRIGHT +(Graphic design by A. Nakonechny. Copyright (c) 1998, All rights reserved.) +def + +LABELCOLOR +LOGOFONT +32 div +F +2 sqrt 0.99 mul +dup +neg +moveto +COPYRIGHT +90 rotate +%show + +%---------------------------------------------------------------------- done -- +showpage + +%%Trailer +%%EOF diff --git a/vendor/luasocket/luasocket-scm-3.rockspec b/vendor/luasocket/luasocket-scm-3.rockspec new file mode 100644 index 00000000..10452510 --- /dev/null +++ b/vendor/luasocket/luasocket-scm-3.rockspec @@ -0,0 +1,135 @@ +package = "LuaSocket" +version = "scm-3" +source = { + url = "git+https://github.com/lunarmodules/luasocket.git", + branch = "master" +} +description = { + summary = "Network support for the Lua language", + detailed = [[ + LuaSocket is a Lua extension library composed of two parts: a set of C + modules that provide support for the TCP and UDP transport layers, and a + set of Lua modules that provide functions commonly needed by applications + that deal with the Internet. + ]], + homepage = "https://github.com/lunarmodules/luasocket", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} + +local function make_plat(plat) + local defines = { + unix = { + "LUASOCKET_DEBUG" + }, + macosx = { + "LUASOCKET_DEBUG", + "UNIX_HAS_SUN_LEN" + }, + win32 = { + "LUASOCKET_DEBUG", + "NDEBUG" + }, + mingw32 = { + "LUASOCKET_DEBUG", + -- "LUASOCKET_INET_PTON", + "WINVER=0x0501" + } + } + local modules = { + ["socket.core"] = { + sources = { + "src/luasocket.c" + , "src/timeout.c" + , "src/buffer.c" + , "src/io.c" + , "src/auxiliar.c" + , "src/options.c" + , "src/inet.c" + , "src/except.c" + , "src/select.c" + , "src/tcp.c" + , "src/udp.c" + , "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["mime.core"] = { + sources = { "src/mime.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, + ["socket.http"] = "src/http.lua", + ["socket.url"] = "src/url.lua", + ["socket.tp"] = "src/tp.lua", + ["socket.ftp"] = "src/ftp.lua", + ["socket.headers"] = "src/headers.lua", + ["socket.smtp"] = "src/smtp.lua", + ltn12 = "src/ltn12.lua", + socket = "src/socket.lua", + mime = "src/mime.lua" + } + if plat == "unix" + or plat == "macosx" + or plat == "haiku" + then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c" + if plat == "haiku" then + modules["socket.core"].libraries = {"network"} + end + modules["socket.unix"] = { + sources = { + "src/buffer.c" + , "src/compat.c" + , "src/auxiliar.c" + , "src/options.c" + , "src/timeout.c" + , "src/io.c" + , "src/usocket.c" + , "src/unix.c" + , "src/unixdgram.c" + , "src/unixstream.c" }, + defines = defines[plat], + incdir = "/src" + } + modules["socket.serial"] = { + sources = { + "src/buffer.c" + , "src/compat.c" + , "src/auxiliar.c" + , "src/options.c" + , "src/timeout.c" + , "src/io.c" + , "src/usocket.c" + , "src/serial.c" }, + defines = defines[plat], + incdir = "/src" + } + end + if plat == "win32" + or plat == "mingw32" + then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/wsocket.c" + modules["socket.core"].libraries = { "ws2_32" } + modules["socket.core"].libdirs = {} + end + return { modules = modules } +end + +build = { + type = "builtin", + platforms = { + unix = make_plat("unix"), + macosx = make_plat("macosx"), + haiku = make_plat("haiku"), + win32 = make_plat("win32"), + mingw32 = make_plat("mingw32") + }, + copy_directories = { + "docs" + , "samples" + , "etc" + , "test" } +} diff --git a/vendor/luasocket/luasocket.sln b/vendor/luasocket/luasocket.sln new file mode 100644 index 00000000..0e5cdc73 --- /dev/null +++ b/vendor/luasocket/luasocket.sln @@ -0,0 +1,35 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socket", "socket.vcxproj", "{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mime", "mime.vcxproj", "{128E8BD0-174A-48F0-8771-92B1E8D18713}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|Win32.ActiveCfg = Debug|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|Win32.Build.0 = Debug|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|x64.ActiveCfg = Debug|x64 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|x64.Build.0 = Debug|x64 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|Win32.ActiveCfg = Release|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|Win32.Build.0 = Release|Win32 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|x64.ActiveCfg = Release|x64 + {66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|x64.Build.0 = Release|x64 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|Win32.ActiveCfg = Debug|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|Win32.Build.0 = Debug|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|x64.ActiveCfg = Debug|x64 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|x64.Build.0 = Debug|x64 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|Win32.ActiveCfg = Release|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|Win32.Build.0 = Release|Win32 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|x64.ActiveCfg = Release|x64 + {128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vendor/luasocket/src/auxiliar.c b/vendor/luasocket/src/auxiliar.c new file mode 100644 index 00000000..93a66a09 --- /dev/null +++ b/vendor/luasocket/src/auxiliar.c @@ -0,0 +1,154 @@ +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "auxiliar.h" +#include +#include + +/*-------------------------------------------------------------------------*\ +* Initializes the module +\*-------------------------------------------------------------------------*/ +int auxiliar_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Creates a new class with given methods +* Methods whose names start with __ are passed directly to the metatable. +\*-------------------------------------------------------------------------*/ +void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func) { + luaL_newmetatable(L, classname); /* mt */ + /* create __index table to place methods */ + lua_pushstring(L, "__index"); /* mt,"__index" */ + lua_newtable(L); /* mt,"__index",it */ + /* put class name into class metatable */ + lua_pushstring(L, "class"); /* mt,"__index",it,"class" */ + lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */ + lua_rawset(L, -3); /* mt,"__index",it */ + /* pass all methods that start with _ to the metatable, and all others + * to the index table */ + for (; func->name; func++) { /* mt,"__index",it */ + lua_pushstring(L, func->name); + lua_pushcfunction(L, func->func); + lua_rawset(L, func->name[0] == '_' ? -5: -3); + } + lua_rawset(L, -3); /* mt */ + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Prints the value of a class in a nice way +\*-------------------------------------------------------------------------*/ +int auxiliar_tostring(lua_State *L) { + char buf[32]; + if (!lua_getmetatable(L, 1)) goto error; + lua_pushstring(L, "__index"); + lua_gettable(L, -2); + if (!lua_istable(L, -1)) goto error; + lua_pushstring(L, "class"); + lua_gettable(L, -2); + if (!lua_isstring(L, -1)) goto error; + sprintf(buf, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf); + return 1; +error: + lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'"); + lua_error(L); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Insert class into group +\*-------------------------------------------------------------------------*/ +void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) { + luaL_getmetatable(L, classname); + lua_pushstring(L, groupname); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pop(L, 1); +} + +/*-------------------------------------------------------------------------*\ +* Make sure argument is a boolean +\*-------------------------------------------------------------------------*/ +int auxiliar_checkboolean(lua_State *L, int objidx) { + if (!lua_isboolean(L, objidx)) + auxiliar_typeerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); + return lua_toboolean(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given class, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) { + void *data = auxiliar_getclassudata(L, classname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", classname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Return userdata pointer if object belongs to a given group, abort with +* error otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) { + void *data = auxiliar_getgroupudata(L, groupname, objidx); + if (!data) { + char msg[45]; + sprintf(msg, "%.35s expected", groupname); + luaL_argerror(L, objidx, msg); + } + return data; +} + +/*-------------------------------------------------------------------------*\ +* Set object class +\*-------------------------------------------------------------------------*/ +void auxiliar_setclass(lua_State *L, const char *classname, int objidx) { + luaL_getmetatable(L, classname); + if (objidx < 0) objidx--; + lua_setmetatable(L, objidx); +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given group. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { + if (!lua_getmetatable(L, objidx)) + return NULL; + lua_pushstring(L, groupname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + return NULL; + } else { + lua_pop(L, 2); + return lua_touserdata(L, objidx); + } +} + +/*-------------------------------------------------------------------------*\ +* Get a userdata pointer if object belongs to a given class. Return NULL +* otherwise +\*-------------------------------------------------------------------------*/ +void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { + return luaL_testudata(L, objidx, classname); +} + +/*-------------------------------------------------------------------------*\ +* Throws error when argument does not have correct type. +* Used to be part of lauxlib in Lua 5.1, was dropped from 5.2. +\*-------------------------------------------------------------------------*/ +int auxiliar_typeerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, + luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} diff --git a/vendor/luasocket/src/auxiliar.h b/vendor/luasocket/src/auxiliar.h new file mode 100644 index 00000000..e8c3ead8 --- /dev/null +++ b/vendor/luasocket/src/auxiliar.h @@ -0,0 +1,54 @@ +#ifndef AUXILIAR_H +#define AUXILIAR_H +/*=========================================================================*\ +* Auxiliar routines for class hierarchy manipulation +* LuaSocket toolkit (but completely independent of other LuaSocket modules) +* +* A LuaSocket class is a name associated with Lua metatables. A LuaSocket +* group is a name associated with a class. A class can belong to any number +* of groups. This module provides the functionality to: +* +* - create new classes +* - add classes to groups +* - set the class of objects +* - check if an object belongs to a given class or group +* - get the userdata associated to objects +* - print objects in a pretty way +* +* LuaSocket class names follow the convention {}. Modules +* can define any number of classes and groups. The module tcp.c, for +* example, defines the classes tcp{master}, tcp{client} and tcp{server} and +* the groups tcp{client,server} and tcp{any}. Module functions can then +* perform type-checking on their arguments by either class or group. +* +* LuaSocket metatables define the __index metamethod as being a table. This +* table has one field for each method supported by the class, and a field +* "class" with the class name. +* +* The mapping from class name to the corresponding metatable and the +* reverse mapping are done using lauxlib. +\*=========================================================================*/ + +#include "luasocket.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int auxiliar_open(lua_State *L); +void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func); +int auxiliar_tostring(lua_State *L); +void auxiliar_add2group(lua_State *L, const char *classname, const char *group); +int auxiliar_checkboolean(lua_State *L, int objidx); +void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx); +void auxiliar_setclass(lua_State *L, const char *classname, int objidx); +void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx); +void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); +int auxiliar_typeerror(lua_State *L, int narg, const char *tname); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* AUXILIAR_H */ diff --git a/vendor/luasocket/src/buffer.c b/vendor/luasocket/src/buffer.c new file mode 100644 index 00000000..7148be34 --- /dev/null +++ b/vendor/luasocket/src/buffer.c @@ -0,0 +1,273 @@ +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "buffer.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); +static int recvline(p_buffer buf, luaL_Buffer *b); +static int recvall(p_buffer buf, luaL_Buffer *b); +static int buffer_get(p_buffer buf, const char **data, size_t *count); +static void buffer_skip(p_buffer buf, size_t count); +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int buffer_open(lua_State *L) { + (void) L; + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; + buf->io = io; + buf->tm = tm; + buf->received = buf->sent = 0; + buf->birthday = timeout_gettime(); +} + +/*-------------------------------------------------------------------------*\ +* object:getstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_getstats(lua_State *L, p_buffer buf) { + lua_pushnumber(L, (lua_Number) buf->received); + lua_pushnumber(L, (lua_Number) buf->sent); + lua_pushnumber(L, timeout_gettime() - buf->birthday); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* object:setstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_setstats(lua_State *L, p_buffer buf) { + buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); + buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* object:send() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_send(lua_State *L, p_buffer buf) { + int top = lua_gettop(L); + int err = IO_DONE; + size_t size = 0, sent = 0; + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); + timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; + if (end > (long) size) end = (long) size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, (lua_Number) (sent+start-1)); + } else { + lua_pushnumber(L, (lua_Number) (sent+start-1)); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* object:receive() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_receive(lua_State *L, p_buffer buf) { + int err = IO_DONE, top; + luaL_Buffer b; + size_t size; + const char *part = luaL_optlstring(L, 3, "", &size); + timeout_markstart(buf->tm); + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 3); + top = lua_gettop(L); + /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); + /* receive new patterns */ + if (!lua_isnumber(L, 2)) { + const char *p= luaL_optstring(L, 2, "*l"); + if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else luaL_argcheck(L, 0, 2, "invalid receive pattern"); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else { + double n = lua_tonumber(L, 2); + size_t wanted = (size_t) n; + luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); + if (size == 0 || wanted > size) + err = recvraw(buf, wanted-size, &b); + } + /* check if there was an error */ + if (err != IO_DONE) { + /* we can't push anyting in the stack before pushing the + * contents of the buffer. this is the reason for the complication */ + luaL_pushresult(&b); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { + luaL_pushresult(&b); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* Determines if there is any data in the read buffer +\*-------------------------------------------------------------------------*/ +int buffer_isempty(p_buffer buf) { + return buf->first >= buf->last; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sends a block of data (unbuffered) +\*-------------------------------------------------------------------------*/ +#define STEPSIZE 8192 +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { + p_io io = buf->io; + p_timeout tm = buf->tm; + size_t total = 0; + int err = IO_DONE; + while (total < count && err == IO_DONE) { + size_t done = 0; + size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; + err = io->send(io->ctx, data+total, step, &done, tm); + total += done; + } + *sent = total; + buf->sent += total; + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a fixed number of bytes (buffered) +\*-------------------------------------------------------------------------*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + size_t count; const char *data; + err = buffer_get(buf, &data, &count); + count = MIN(count, wanted - total); + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + total += count; + if (total >= wanted) break; + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads everything until the connection is closed (buffered) +\*-------------------------------------------------------------------------*/ +static int recvall(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + const char *data; size_t count; + err = buffer_get(buf, &data, &count); + total += count; + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + } + if (err == IO_CLOSED) { + if (total > 0) return IO_DONE; + else return IO_CLOSED; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* are not returned by the function and are discarded from the buffer +\*-------------------------------------------------------------------------*/ +static int recvline(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + while (err == IO_DONE) { + size_t count, pos; const char *data; + err = buffer_get(buf, &data, &count); + pos = 0; + while (pos < count && data[pos] != '\n') { + /* we ignore all \r's */ + if (data[pos] != '\r') luaL_addchar(b, data[pos]); + pos++; + } + if (pos < count) { /* found '\n' */ + buffer_skip(buf, pos+1); /* skip '\n' too */ + break; /* we are done */ + } else /* reached the end of the buffer */ + buffer_skip(buf, pos); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Skips a given number of bytes from read buffer. No data is read from the +* transport layer +\*-------------------------------------------------------------------------*/ +static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; + buf->first += count; + if (buffer_isempty(buf)) + buf->first = buf->last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data available in buffer, or get more data from transport layer +* if buffer is empty +\*-------------------------------------------------------------------------*/ +static int buffer_get(p_buffer buf, const char **data, size_t *count) { + int err = IO_DONE; + p_io io = buf->io; + p_timeout tm = buf->tm; + if (buffer_isempty(buf)) { + size_t got; + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); + buf->first = 0; + buf->last = got; + } + *count = buf->last - buf->first; + *data = buf->data + buf->first; + return err; +} diff --git a/vendor/luasocket/src/buffer.h b/vendor/luasocket/src/buffer.h new file mode 100644 index 00000000..a0901fcc --- /dev/null +++ b/vendor/luasocket/src/buffer.h @@ -0,0 +1,52 @@ +#ifndef BUF_H +#define BUF_H +/*=========================================================================*\ +* Input/Output interface for Lua programs +* LuaSocket toolkit +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. +\*=========================================================================*/ +#include "luasocket.h" +#include "io.h" +#include "timeout.h" + +/* buffer size in bytes */ +#define BUF_SIZE 8192 + +/* buffer control structure */ +typedef struct t_buffer_ { + double birthday; /* throttle support info: creation time, */ + size_t sent, received; /* bytes sent, and bytes received */ + p_io io; /* IO driver used for this buffer */ + p_timeout tm; /* timeout management for this buffer */ + size_t first, last; /* index of first and last bytes of stored data */ + char data[BUF_SIZE]; /* storage space for buffer data */ +} t_buffer; +typedef t_buffer *p_buffer; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int buffer_open(lua_State *L); +void buffer_init(p_buffer buf, p_io io, p_timeout tm); +int buffer_meth_getstats(lua_State *L, p_buffer buf); +int buffer_meth_setstats(lua_State *L, p_buffer buf); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_isempty(p_buffer buf); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* BUF_H */ diff --git a/vendor/luasocket/src/compat.c b/vendor/luasocket/src/compat.c new file mode 100644 index 00000000..34ffdaf7 --- /dev/null +++ b/vendor/luasocket/src/compat.c @@ -0,0 +1,39 @@ +#include "luasocket.h" +#include "compat.h" + +#if LUA_VERSION_NUM==501 + +/* +** Adapted from Lua 5.2 +*/ +void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup+1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); + } + lua_pop(L, nup); /* remove upvalues */ +} + +/* +** Duplicated from Lua 5.2 +*/ +void *luasocket_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + +#endif diff --git a/vendor/luasocket/src/compat.h b/vendor/luasocket/src/compat.h new file mode 100644 index 00000000..fa2d7d7c --- /dev/null +++ b/vendor/luasocket/src/compat.h @@ -0,0 +1,22 @@ +#ifndef COMPAT_H +#define COMPAT_H + +#if LUA_VERSION_NUM==501 + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup); +void *luasocket_testudata ( lua_State *L, int arg, const char *tname); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#define luaL_setfuncs luasocket_setfuncs +#define luaL_testudata luasocket_testudata + +#endif + +#endif diff --git a/vendor/luasocket/src/except.c b/vendor/luasocket/src/except.c new file mode 100644 index 00000000..9c3317f2 --- /dev/null +++ b/vendor/luasocket/src/except.c @@ -0,0 +1,129 @@ +/*=========================================================================*\ +* Simple exception support +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "except.h" +#include + +#if LUA_VERSION_NUM < 502 +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + (((void)ctx),((void)cont),lua_pcall(L, na, nr, err)) +#endif + +#if LUA_VERSION_NUM < 503 +typedef int lua_KContext; +#endif + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int global_protect(lua_State *L); +static int global_newtry(lua_State *L); +static int protected_(lua_State *L); +static int finalize(lua_State *L); +static int do_nothing(lua_State *L); + +/* except functions */ +static luaL_Reg func[] = { + {"newtry", global_newtry}, + {"protect", global_protect}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Try factory +\*-------------------------------------------------------------------------*/ +static void wrap(lua_State *L) { + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_rawseti(L, -2, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); +} + +static int finalize(lua_State *L) { + if (!lua_toboolean(L, 1)) { + lua_pushvalue(L, lua_upvalueindex(2)); + lua_call(L, 0, 0); + lua_settop(L, 2); + wrap(L); + lua_error(L); + return 0; + } else return lua_gettop(L); +} + +static int do_nothing(lua_State *L) { + (void) L; + return 0; +} + +static int global_newtry(lua_State *L) { + lua_settop(L, 1); + if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, -2); + lua_pushcclosure(L, finalize, 2); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Protect factory +\*-------------------------------------------------------------------------*/ +static int unwrap(lua_State *L) { + if (lua_istable(L, -1) && lua_getmetatable(L, -1)) { + int r = lua_rawequal(L, -1, lua_upvalueindex(1)); + lua_pop(L, 1); + if (r) { + lua_pushnil(L); + lua_rawgeti(L, -2, 1); + return 1; + } + } + return 0; +} + +static int protected_finish(lua_State *L, int status, lua_KContext ctx) { + (void)ctx; + if (status != 0 && status != LUA_YIELD) { + if (unwrap(L)) return 2; + else return lua_error(L); + } else return lua_gettop(L); +} + +#if LUA_VERSION_NUM == 502 +static int protected_cont(lua_State *L) { + int ctx = 0; + int status = lua_getctx(L, &ctx); + return protected_finish(L, status, ctx); +} +#else +#define protected_cont protected_finish +#endif + +static int protected_(lua_State *L) { + int status; + lua_pushvalue(L, lua_upvalueindex(2)); + lua_insert(L, 1); + status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont); + return protected_finish(L, status, 0); +} + +static int global_protect(lua_State *L) { + lua_settop(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + lua_pushcclosure(L, protected_, 2); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Init module +\*-------------------------------------------------------------------------*/ +int except_open(lua_State *L) { + lua_newtable(L); /* metatable for wrapped exceptions */ + lua_pushboolean(L, 0); + lua_setfield(L, -2, "__metatable"); + luaL_setfuncs(L, func, 1); + return 0; +} diff --git a/vendor/luasocket/src/except.h b/vendor/luasocket/src/except.h new file mode 100644 index 00000000..71c31fd4 --- /dev/null +++ b/vendor/luasocket/src/except.h @@ -0,0 +1,46 @@ +#ifndef EXCEPT_H +#define EXCEPT_H +/*=========================================================================*\ +* Exception control +* LuaSocket toolkit (but completely independent from other modules) +* +* This provides support for simple exceptions in Lua. During the +* development of the HTTP/FTP/SMTP support, it became aparent that +* error checking was taking a substantial amount of the coding. These +* function greatly simplify the task of checking errors. +* +* The main idea is that functions should return nil as their first return +* values when they find an error, and return an error message (or value) +* following nil. In case of success, as long as the first value is not nil, +* the other values don't matter. +* +* The idea is to nest function calls with the "try" function. This function +* checks the first value, and, if it's falsy, wraps the second value in a +* table with metatable and calls "error" on it. Otherwise, it returns all +* values it received. Basically, it works like the Lua "assert" function, +* but it creates errors targeted specifically at "protect". +* +* The "newtry" function is a factory for "try" functions that call a +* finalizer in protected mode before calling "error". +* +* The "protect" function returns a new function that behaves exactly like +* the function it receives, but the new function catches exceptions thrown +* by "try" functions and returns nil followed by the error message instead. +* +* With these three functions, it's easy to write functions that throw +* exceptions on error, but that don't interrupt the user script. +\*=========================================================================*/ + +#include "luasocket.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int except_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif diff --git a/vendor/luasocket/src/ftp.lua b/vendor/luasocket/src/ftp.lua new file mode 100644 index 00000000..0ebc5086 --- /dev/null +++ b/vendor/luasocket/src/ftp.lua @@ -0,0 +1,329 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +socket.ftp = {} +local _M = socket.ftp +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +_M.TIMEOUT = 60 +-- default port for ftp service +local PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +_M.USER = "ftp" +_M.PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(_M.TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(_M.TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(_M.TIMEOUT)) + self.try(self.data:connect(self.pasvt.address, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or _M.USER)) + local code, _ = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or _M.PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local _, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + address = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + +function metat.__index:epsv() + self.try(self.tp:command("epsv")) + local _, reply = self.try(self.tp:check("229")) + local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" + local _, _, _, port = string.match(reply, pattern) + self.try(port, "invalid epsv response") + self.pasvt = { + address = self.tp:getpeername(), + port = port + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + + +function metat.__index:port(address, port) + self.pasvt = nil + if not address then + address = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:eprt(family, address, port) + self.pasvt = nil + if not address then + address = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local arg = string.format("|%s|%s|%d|", family, address, port) + self.try(self.tp:command("eprt", arg)) + self.try(self.tp:check("2..")) + return 1 +end + + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, _ = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = { self.tp } + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code,reply = self.try(self.tp:check{"1..", "2.."}) + if (code >= 200) and (code <= 299) then + recvt.sink(reply) + return 1 + end + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = _M.open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:epsv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function genericform(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +_M.genericform = genericform + +local function sput(u, body) + local putt = genericform(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +_M.put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = _M.open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:epsv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = genericform(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +_M.command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = _M.open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + if type(cmdt.command) == "table" then + local argument = cmdt.argument or {} + local check = cmdt.check or {} + for i,cmd in ipairs(cmdt.command) do + f.try(f.tp:command(cmd, argument[i])) + if check[i] then f.try(f.tp:check(check[i])) end + end + else + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + end + f:quit() + return f:close() +end) + +_M.get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + +return _M diff --git a/vendor/luasocket/src/headers.lua b/vendor/luasocket/src/headers.lua new file mode 100644 index 00000000..1eb8223b --- /dev/null +++ b/vendor/luasocket/src/headers.lua @@ -0,0 +1,104 @@ +----------------------------------------------------------------------------- +-- Canonic header field capitalization +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +socket.headers = {} +local _M = socket.headers + +_M.canonic = { + ["accept"] = "Accept", + ["accept-charset"] = "Accept-Charset", + ["accept-encoding"] = "Accept-Encoding", + ["accept-language"] = "Accept-Language", + ["accept-ranges"] = "Accept-Ranges", + ["action"] = "Action", + ["alternate-recipient"] = "Alternate-Recipient", + ["age"] = "Age", + ["allow"] = "Allow", + ["arrival-date"] = "Arrival-Date", + ["authorization"] = "Authorization", + ["bcc"] = "Bcc", + ["cache-control"] = "Cache-Control", + ["cc"] = "Cc", + ["comments"] = "Comments", + ["connection"] = "Connection", + ["content-description"] = "Content-Description", + ["content-disposition"] = "Content-Disposition", + ["content-encoding"] = "Content-Encoding", + ["content-id"] = "Content-ID", + ["content-language"] = "Content-Language", + ["content-length"] = "Content-Length", + ["content-location"] = "Content-Location", + ["content-md5"] = "Content-MD5", + ["content-range"] = "Content-Range", + ["content-transfer-encoding"] = "Content-Transfer-Encoding", + ["content-type"] = "Content-Type", + ["cookie"] = "Cookie", + ["date"] = "Date", + ["diagnostic-code"] = "Diagnostic-Code", + ["dsn-gateway"] = "DSN-Gateway", + ["etag"] = "ETag", + ["expect"] = "Expect", + ["expires"] = "Expires", + ["final-log-id"] = "Final-Log-ID", + ["final-recipient"] = "Final-Recipient", + ["from"] = "From", + ["host"] = "Host", + ["if-match"] = "If-Match", + ["if-modified-since"] = "If-Modified-Since", + ["if-none-match"] = "If-None-Match", + ["if-range"] = "If-Range", + ["if-unmodified-since"] = "If-Unmodified-Since", + ["in-reply-to"] = "In-Reply-To", + ["keywords"] = "Keywords", + ["last-attempt-date"] = "Last-Attempt-Date", + ["last-modified"] = "Last-Modified", + ["location"] = "Location", + ["max-forwards"] = "Max-Forwards", + ["message-id"] = "Message-ID", + ["mime-version"] = "MIME-Version", + ["original-envelope-id"] = "Original-Envelope-ID", + ["original-recipient"] = "Original-Recipient", + ["pragma"] = "Pragma", + ["proxy-authenticate"] = "Proxy-Authenticate", + ["proxy-authorization"] = "Proxy-Authorization", + ["range"] = "Range", + ["received"] = "Received", + ["received-from-mta"] = "Received-From-MTA", + ["references"] = "References", + ["referer"] = "Referer", + ["remote-mta"] = "Remote-MTA", + ["reply-to"] = "Reply-To", + ["reporting-mta"] = "Reporting-MTA", + ["resent-bcc"] = "Resent-Bcc", + ["resent-cc"] = "Resent-Cc", + ["resent-date"] = "Resent-Date", + ["resent-from"] = "Resent-From", + ["resent-message-id"] = "Resent-Message-ID", + ["resent-reply-to"] = "Resent-Reply-To", + ["resent-sender"] = "Resent-Sender", + ["resent-to"] = "Resent-To", + ["retry-after"] = "Retry-After", + ["return-path"] = "Return-Path", + ["sender"] = "Sender", + ["server"] = "Server", + ["smtp-remote-recipient"] = "SMTP-Remote-Recipient", + ["status"] = "Status", + ["subject"] = "Subject", + ["te"] = "TE", + ["to"] = "To", + ["trailer"] = "Trailer", + ["transfer-encoding"] = "Transfer-Encoding", + ["upgrade"] = "Upgrade", + ["user-agent"] = "User-Agent", + ["vary"] = "Vary", + ["via"] = "Via", + ["warning"] = "Warning", + ["will-retry-until"] = "Will-Retry-Until", + ["www-authenticate"] = "WWW-Authenticate", + ["x-mailer"] = "X-Mailer", +} + +return _M \ No newline at end of file diff --git a/vendor/luasocket/src/http.lua b/vendor/luasocket/src/http.lua new file mode 100644 index 00000000..1330355f --- /dev/null +++ b/vendor/luasocket/src/http.lua @@ -0,0 +1,424 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require("socket") +local url = require("socket.url") +local ltn12 = require("ltn12") +local mime = require("mime") +local string = require("string") +local headers = require("socket.headers") +local base = _G +local table = require("table") +socket.http = {} +local _M = socket.http + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +_M.TIMEOUT = 60 +-- user agent field sent in request +_M.USERAGENT = socket._VERSION + +-- supported schemes and their particulars +local SCHEMES = { + http = { + port = 80 + , create = function(t) + return socket.tcp end } + , https = { + port = 443 + , create = function(t) + local https = assert( + require("ssl.https"), 'LuaSocket: LuaSec not found') + local tcp = assert( + https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') + return tcp(t) end }} + +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, _ = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try(create()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(_M.TIMEOUT)) + h.try(c:connect(host, port)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f, v in base.pairs(tosend) do + h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status,ec = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then + if ec == "timeout" then + return 408 + end + return nil, status + end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not _M.PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local host = reqt.host + local port = tostring(reqt.port) + if port ~= tostring(SCHEMES[reqt.scheme].port) then + host = host .. ':' .. port end + local lower = { + ["user-agent"] = _M.USERAGENT, + ["host"] = host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. + url.unescape(reqt.password))) + end + -- if we have proxy authentication information, pass it along + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + if proxy.user and proxy.password then + lower["proxy-authorization"] = + "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password)) + end + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + path ="/" + , scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + -- default to scheme particulars + local schemedefs, host, port, method + = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method + if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end + if not (port and port ~= '') then nreqt.port = schemedefs.port end + if not (method and method ~= '') then nreqt.method = 'GET' end + if not (host and host ~= "") then + socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") + end + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + if nreqt.source + and not nreqt.headers["content-length"] + and not nreqt.headers["transfer-encoding"] + then + nreqt.headers["transfer-encoding"] = "chunked" + end + + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + local location = headers.location + if not location then return false end + location = string.gsub(location, "%s", "") + if location == "" then return false end + local scheme = url.parse(location).scheme + if scheme and (not SCHEMES[scheme]) then return false end + -- avoid https downgrades + if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end + return (reqt.redirect ~= false) and + (code == 301 or code == 302 or code == 303 or code == 307) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and ((false == reqt.maxredirects) + or ((reqt.nredirects or 0) + < (reqt.maxredirects or 5))) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +--[[local]] function tredirect(reqt, location) + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + local newurl = url.absolute(reqt.url, location) + -- if switching schemes, reset port and create function + if url.parse(newurl).scheme ~= reqt.scheme then + reqt.port = nil + reqt.create = nil end + -- make new request + local result, code, headers, status = trequest { + url = newurl, + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + maxredirects = reqt.maxredirects, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +--[[local]] function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = _M.open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + elseif code == 408 then + return 1, code + end + local headers + -- ignore any 100-continue messages + while code == 100 do + h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +-- turns an url and a body into a generic request +local function genericform(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t), + target = t + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + return reqt +end + +_M.genericform = genericform + +local function srequest(u, b) + local reqt = genericform(u, b) + local _, code, headers, status = trequest(reqt) + return table.concat(reqt.target), code, headers, status +end + +_M.request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end) + +_M.schemes = SCHEMES +return _M diff --git a/vendor/luasocket/src/inet.c b/vendor/luasocket/src/inet.c new file mode 100755 index 00000000..138c9abe --- /dev/null +++ b/vendor/luasocket/src/inet.c @@ -0,0 +1,537 @@ +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "inet.h" + +#include +#include +#include + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int inet_global_toip(lua_State *L); +static int inet_global_getaddrinfo(lua_State *L); +static int inet_global_tohostname(lua_State *L); +static int inet_global_getnameinfo(lua_State *L); +static void inet_pushresolved(lua_State *L, struct hostent *hp); +static int inet_global_gethostname(lua_State *L); + +/* DNS functions */ +static luaL_Reg func[] = { + { "toip", inet_global_toip}, + { "getaddrinfo", inet_global_getaddrinfo}, + { "tohostname", inet_global_tohostname}, + { "getnameinfo", inet_global_getnameinfo}, + { "gethostname", inet_global_gethostname}, + { NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int inet_open(lua_State *L) +{ + lua_pushstring(L, "dns"); + lua_newtable(L); + luaL_setfuncs(L, func, 0); + lua_settable(L, -3); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_gethost(const char *address, struct hostent **hp) { + struct in_addr addr; + if (inet_aton(address, &addr)) + return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp); + else + return socket_gethostbyname(address, hp); +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_tohostname(lua_State *L) { + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, hp->h_name); + inet_pushresolved(L, hp); + return 2; +} + +static int inet_global_getnameinfo(lua_State *L) { + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int i, ret; + struct addrinfo hints; + struct addrinfo *resolved, *iter; + const char *host = luaL_optstring(L, 1, NULL); + const char *serv = luaL_optstring(L, 2, NULL); + + if (!(host || serv)) + luaL_error(L, "host and serv cannot be both nil"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + + ret = getaddrinfo(host, serv, &hints, &resolved); + if (ret != 0) { + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + + lua_newtable(L); + for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { + getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, + hbuf, host? (socklen_t) sizeof(hbuf): 0, + sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); + if (host) { + lua_pushnumber(L, i); + lua_pushstring(L, hbuf); + lua_settable(L, -3); + } + } + freeaddrinfo(resolved); + + if (serv) { + lua_pushstring(L, sbuf); + return 2; + } else { + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Returns all information provided by the resolver given a host name +* or ip address +\*-------------------------------------------------------------------------*/ +static int inet_global_toip(lua_State *L) +{ + const char *address = luaL_checkstring(L, 1); + struct hostent *hp = NULL; + int err = inet_gethost(address, &hp); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_hoststrerror(err)); + return 2; + } + lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr))); + inet_pushresolved(L, hp); + return 2; +} + +int inet_optfamily(lua_State* L, int narg, const char* def) +{ + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; + + return optvalue[luaL_checkoption(L, narg, def, optname)]; +} + +int inet_optsocktype(lua_State* L, int narg, const char* def) +{ + static const char* optname[] = { "stream", "dgram", NULL }; + static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 }; + + return optvalue[luaL_checkoption(L, narg, def, optname)]; +} + +static int inet_global_getaddrinfo(lua_State *L) +{ + const char *hostname = luaL_checkstring(L, 1); + struct addrinfo *iterator = NULL, *resolved = NULL; + struct addrinfo hints; + int i = 1, ret = 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + ret = getaddrinfo(hostname, NULL, &hints, &resolved); + if (ret != 0) { + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + lua_newtable(L); + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + char hbuf[NI_MAXHOST]; + ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, + hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (ret){ + freeaddrinfo(resolved); + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; + } + lua_pushnumber(L, i); + lua_newtable(L); + switch (iterator->ai_family) { + case AF_INET: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet"); + lua_settable(L, -3); + break; + case AF_INET6: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet6"); + lua_settable(L, -3); + break; + case AF_UNSPEC: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unspec"); + lua_settable(L, -3); + break; + default: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unknown"); + lua_settable(L, -3); + break; + } + lua_pushliteral(L, "addr"); + lua_pushstring(L, hbuf); + lua_settable(L, -3); + lua_settable(L, -3); + i++; + } + freeaddrinfo(resolved); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Gets the host name +\*-------------------------------------------------------------------------*/ +static int inet_global_gethostname(lua_State *L) +{ + char name[257]; + name[256] = '\0'; + if (gethostname(name, 256) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } else { + lua_pushstring(L, name); + return 1; + } +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Retrieves socket peer name +\*-------------------------------------------------------------------------*/ +int inet_meth_getpeername(lua_State *L, p_socket ps, int family) +{ + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + err = getnameinfo((struct sockaddr *) &peer, peer_len, + name, INET6_ADDRSTRLEN, + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } + return 3; +} + +/*-------------------------------------------------------------------------*\ +* Retrieves socket local name +\*-------------------------------------------------------------------------*/ +int inet_meth_getsockname(lua_State *L, p_socket ps, int family) +{ + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); + char name[INET6_ADDRSTRLEN]; + char port[6]; /* 65535 = 5 bytes + 0 to terminate it */ + if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); + return 2; + } + lua_pushstring(L, name); + lua_pushstring(L, port); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } + return 3; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Passes all resolver information to Lua as a table +\*-------------------------------------------------------------------------*/ +static void inet_pushresolved(lua_State *L, struct hostent *hp) +{ + char **alias; + struct in_addr **addr; + int i, resolved; + lua_newtable(L); resolved = lua_gettop(L); + lua_pushstring(L, "name"); + lua_pushstring(L, hp->h_name); + lua_settable(L, resolved); + lua_pushstring(L, "ip"); + lua_pushstring(L, "alias"); + i = 1; + alias = hp->h_aliases; + lua_newtable(L); + if (alias) { + while (*alias) { + lua_pushnumber(L, i); + lua_pushstring(L, *alias); + lua_settable(L, -3); + i++; alias++; + } + } + lua_settable(L, resolved); + i = 1; + lua_newtable(L); + addr = (struct in_addr **) hp->h_addr_list; + if (addr) { + while (*addr) { + lua_pushnumber(L, i); + lua_pushstring(L, inet_ntoa(**addr)); + lua_settable(L, -3); + i++; addr++; + } + } + lua_settable(L, resolved); +} + +/*-------------------------------------------------------------------------*\ +* Tries to create a new inet socket +\*-------------------------------------------------------------------------*/ +const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* "Disconnects" a DGRAM socket +\*-------------------------------------------------------------------------*/ +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) +{ + switch (family) { + case AF_INET: { + struct sockaddr_in sin; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_UNSPEC; + sin.sin_addr.s_addr = INADDR_ANY; + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } + } + return NULL; +} + +/*-------------------------------------------------------------------------*\ +* Tries to connect to remote address (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_tryconnect(p_socket ps, int *family, const char *address, + const char *serv, p_timeout tm, struct addrinfo *connecthints) +{ + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; + int current_family = *family; + /* try resolving */ + err = socket_gaistrerror(getaddrinfo(address, serv, + connecthints, &resolved)); + if (err != NULL) { + if (resolved) freeaddrinfo(resolved); + return err; + } + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + timeout_markstart(tm); + /* create new socket if necessary. if there was no + * bind, we need to create one for every new family + * that shows up while iterating. if there was a + * bind, all families will be the same and we will + * not enter this branch. */ + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); + if (err) continue; + current_family = iterator->ai_family; + /* set non-blocking before connect */ + socket_setnonblocking(ps); + } + /* try connecting to remote address */ + err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen, tm)); + /* if success or timeout is zero, break out of loop */ + if (err == NULL || timeout_iszero(tm)) { + *family = current_family; + break; + } + } + freeaddrinfo(resolved); + /* here, if err is set, we failed */ + return err; +} + +/*-------------------------------------------------------------------------*\ +* Tries to accept a socket +\*-------------------------------------------------------------------------*/ +const char *inet_tryaccept(p_socket server, int family, p_socket client, + p_timeout tm) { + socklen_t len; + t_sockaddr_storage addr; + switch (family) { + case AF_INET6: len = sizeof(struct sockaddr_in6); break; + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } + return socket_strerror(socket_accept(server, client, (SA *) &addr, + &len, tm)); +} + +/*-------------------------------------------------------------------------*\ +* Tries to bind socket to (address, port) +\*-------------------------------------------------------------------------*/ +const char *inet_trybind(p_socket ps, int *family, const char *address, + const char *serv, struct addrinfo *bindhints) { + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; + int current_family = *family; + /* translate luasocket special values to C */ + if (strcmp(address, "*") == 0) address = NULL; + if (!serv) serv = "0"; + /* try resolving */ + err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved)); + if (err) { + if (resolved) freeaddrinfo(resolved); + return err; + } + /* iterate over resolved addresses until one is good */ + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); + if (err) continue; + current_family = iterator->ai_family; + } + /* try binding to local address */ + err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen)); + /* keep trying unless bind succeeded */ + if (err == NULL) { + *family = current_family; + /* set to non-blocking after bind */ + socket_setnonblocking(ps); + break; + } + } + /* cleanup and return error */ + freeaddrinfo(resolved); + /* here, if err is set, we failed */ + return err; +} + +/*-------------------------------------------------------------------------*\ +* Some systems do not provide these so that we provide our own. +\*-------------------------------------------------------------------------*/ +#ifdef LUASOCKET_INET_ATON +int inet_aton(const char *cp, struct in_addr *inp) +{ + unsigned int a = 0, b = 0, c = 0, d = 0; + int n = 0, r; + unsigned long int addr = 0; + r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n); + if (r == 0 || n == 0) return 0; + cp += n; + if (*cp) return 0; + if (a > 255 || b > 255 || c > 255 || d > 255) return 0; + if (inp) { + addr += a; addr <<= 8; + addr += b; addr <<= 8; + addr += c; addr <<= 8; + addr += d; + inp->s_addr = htonl(addr); + } + return 1; +} +#endif + +#ifdef LUASOCKET_INET_PTON +int inet_pton(int af, const char *src, void *dst) +{ + struct addrinfo hints, *res; + int ret = 1; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = af; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(src, NULL, &hints, &res) != 0) return -1; + if (af == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr; + memcpy(dst, &in->sin_addr, sizeof(in->sin_addr)); + } else if (af == AF_INET6) { + struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr; + memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr)); + } else { + ret = -1; + } + freeaddrinfo(res); + return ret; +} + +#endif diff --git a/vendor/luasocket/src/inet.h b/vendor/luasocket/src/inet.h new file mode 100644 index 00000000..5618b61b --- /dev/null +++ b/vendor/luasocket/src/inet.h @@ -0,0 +1,56 @@ +#ifndef INET_H +#define INET_H +/*=========================================================================*\ +* Internet domain functions +* LuaSocket toolkit +* +* This module implements the creation and connection of internet domain +* sockets, on top of the socket.h interface, and the interface of with the +* resolver. +* +* The function inet_aton is provided for the platforms where it is not +* available. The module also implements the interface of the internet +* getpeername and getsockname functions as seen by Lua programs. +* +* The Lua functions toip and tohostname are also implemented here. +\*=========================================================================*/ +#include "luasocket.h" +#include "socket.h" +#include "timeout.h" + +#ifdef _WIN32 +#define LUASOCKET_INET_ATON +#endif + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int inet_open(lua_State *L); + +int inet_optfamily(lua_State* L, int narg, const char* def); +int inet_optsocktype(lua_State* L, int narg, const char* def); + +int inet_meth_getpeername(lua_State *L, p_socket ps, int family); +int inet_meth_getsockname(lua_State *L, p_socket ps, int family); + +const char *inet_trycreate(p_socket ps, int family, int type, int protocol); +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); +const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); +const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); +const char *inet_trybind(p_socket ps, int *family, const char *address, const char *serv, struct addrinfo *bindhints); + +#ifdef LUASOCKET_INET_ATON +int inet_aton(const char *cp, struct in_addr *inp); +#endif + +#ifdef LUASOCKET_INET_PTON +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); +int inet_pton(int af, const char *src, void *dst); +#endif + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* INET_H */ diff --git a/vendor/luasocket/src/io.c b/vendor/luasocket/src/io.c new file mode 100644 index 00000000..5ad4b3af --- /dev/null +++ b/vendor/luasocket/src/io.c @@ -0,0 +1,28 @@ +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "io.h" + +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { + io->send = send; + io->recv = recv; + io->error = error; + io->ctx = ctx; +} + +/*-------------------------------------------------------------------------*\ +* I/O error strings +\*-------------------------------------------------------------------------*/ +const char *io_strerror(int err) { + switch (err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; + default: return "unknown error"; + } +} diff --git a/vendor/luasocket/src/io.h b/vendor/luasocket/src/io.h new file mode 100644 index 00000000..b8a54df6 --- /dev/null +++ b/vendor/luasocket/src/io.h @@ -0,0 +1,70 @@ +#ifndef IO_H +#define IO_H +/*=========================================================================*\ +* Input/Output abstraction +* LuaSocket toolkit +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +\*=========================================================================*/ +#include "luasocket.h" +#include "timeout.h" + +/* IO error codes */ +enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 +}; + +/* interface to error message function */ +typedef const char *(*p_error) ( + void *ctx, /* context needed by send */ + int err /* error code */ +); + +/* interface to send function */ +typedef int (*p_send) ( + void *ctx, /* context needed by send */ + const char *data, /* pointer to buffer with data to send */ + size_t count, /* number of bytes to send from buffer */ + size_t *sent, /* number of bytes sent uppon return */ + p_timeout tm /* timeout control */ +); + +/* interface to recv function */ +typedef int (*p_recv) ( + void *ctx, /* context needed by recv */ + char *data, /* pointer to buffer where data will be writen */ + size_t count, /* number of bytes to receive into buffer */ + size_t *got, /* number of bytes received uppon return */ + p_timeout tm /* timeout control */ +); + +/* IO driver definition */ +typedef struct t_io_ { + void *ctx; /* context needed by send/recv */ + p_send send; /* send function pointer */ + p_recv recv; /* receive function pointer */ + p_error error; /* strerror function */ +} t_io; +typedef t_io *p_io; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); +const char *io_strerror(int err); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* IO_H */ diff --git a/vendor/luasocket/src/ltn12.lua b/vendor/luasocket/src/ltn12.lua new file mode 100644 index 00000000..4cb17f53 --- /dev/null +++ b/vendor/luasocket/src/ltn12.lua @@ -0,0 +1,318 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local table = require("table") +local unpack = unpack or table.unpack +local base = _G +local select = select + +local _M = {} +if module then -- heuristic for exporting a global package table + ltn12 = _M -- luacheck: ignore +end +local filter,source,sink,pump = {},{},{},{} + +_M.filter = filter +_M.source = source +_M.sink = sink +_M.pump = pump + +-- 2048 seems to be better in windows... +_M.BLOCKSIZE = 2048 +_M._VERSION = "LTN12 1.0.3" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local arg = {...} + local n = select('#',...) + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(_M.BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+_M.BLOCKSIZE-1) + i = i + _M.BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates table source +function source.table(t) + base.assert('table' == type(t)) + local i = 0 + return function() + i = i + 1 + return t[i] + end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +-- chains a source with one or several filter(s) +function source.chain(src, f, ...) + if ... then f=filter.chain(f, ...) end + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local arg = {...} + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with one or several filter(s) +function sink.chain(f, snk, ...) + if ... then + local args = { f, snk, ... } + snk = table.remove(args, #args) + f = filter.chain(unpack(args)) + end + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +-- pumps all data from a source to a sink, using a step function +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end + +return _M diff --git a/vendor/luasocket/src/luasocket.c b/vendor/luasocket/src/luasocket.c new file mode 100755 index 00000000..0fd99f70 --- /dev/null +++ b/vendor/luasocket/src/luasocket.c @@ -0,0 +1,104 @@ +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 26/11/1999 +* +* This library is part of an effort to progressively increase the network +* connectivity of the Lua language. The Lua interface to networking +* functions follows the Sockets API closely, trying to simplify all tasks +* involved in setting up both client and server connections. The provided +* IO routines, however, follow the Lua style, being very similar to the +* standard Lua read and write functions. +\*=========================================================================*/ + +#include "luasocket.h" +#include "auxiliar.h" +#include "except.h" +#include "timeout.h" +#include "buffer.h" +#include "inet.h" +#include "tcp.h" +#include "udp.h" +#include "select.h" + +/*-------------------------------------------------------------------------*\ +* Internal function prototypes +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L); +static int global_unload(lua_State *L); +static int base_open(lua_State *L); + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"auxiliar", auxiliar_open}, + {"except", except_open}, + {"timeout", timeout_open}, + {"buffer", buffer_open}, + {"inet", inet_open}, + {"tcp", tcp_open}, + {"udp", udp_open}, + {"select", select_open}, + {NULL, NULL} +}; + +static luaL_Reg func[] = { + {"skip", global_skip}, + {"__unload", global_unload}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Skip a few arguments +\*-------------------------------------------------------------------------*/ +static int global_skip(lua_State *L) { + int amount = (int) luaL_checkinteger(L, 1); + int ret = lua_gettop(L) - amount - 1; + return ret >= 0 ? ret : 0; +} + +/*-------------------------------------------------------------------------*\ +* Unloads the library +\*-------------------------------------------------------------------------*/ +static int global_unload(lua_State *L) { + (void) L; + socket_close(); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Setup basic stuff. +\*-------------------------------------------------------------------------*/ +static int base_open(lua_State *L) { + if (socket_open()) { + /* export functions (and leave namespace table on top of stack) */ + lua_newtable(L); + luaL_setfuncs(L, func, 0); +#ifdef LUASOCKET_DEBUG + lua_pushstring(L, "_DEBUG"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); +#endif + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, LUASOCKET_VERSION); + lua_rawset(L, -3); + return 1; + } else { + lua_pushstring(L, "unable to initialize library"); + lua_error(L); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Initializes all library modules. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L) { + int i; + base_open(L); + for (i = 0; mod[i].name; i++) mod[i].func(L); + return 1; +} diff --git a/vendor/luasocket/src/luasocket.h b/vendor/luasocket/src/luasocket.h new file mode 100644 index 00000000..1017fbaa --- /dev/null +++ b/vendor/luasocket/src/luasocket.h @@ -0,0 +1,36 @@ +#ifndef LUASOCKET_H +#define LUASOCKET_H +/*=========================================================================*\ +* LuaSocket toolkit +* Networking support for the Lua language +* Diego Nehab +* 9/11/1999 +\*=========================================================================*/ + +/*-------------------------------------------------------------------------* \ +* Current socket library version +\*-------------------------------------------------------------------------*/ +#define LUASOCKET_VERSION "LuaSocket 3.0.0" +#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab" + +/*-------------------------------------------------------------------------*\ +* This macro prefixes all exported API functions +\*-------------------------------------------------------------------------*/ +#ifndef LUASOCKET_API +#ifdef _WIN32 +#define LUASOCKET_API __declspec(dllexport) +#else +#define LUASOCKET_API __attribute__ ((visibility ("default"))) +#endif +#endif + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +/*-------------------------------------------------------------------------*\ +* Initializes the library. +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_core(lua_State *L); + +#endif /* LUASOCKET_H */ diff --git a/vendor/luasocket/src/makefile b/vendor/luasocket/src/makefile new file mode 100755 index 00000000..06f4d192 --- /dev/null +++ b/vendor/luasocket/src/makefile @@ -0,0 +1,461 @@ +# luasocket src/makefile +# +# Definitions in this section can be overriden on the command line or in the +# environment. +# +# These are equivalent: +# +# export PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw +# make +# +# and +# +# make PLAT=linux DEBUG=DEBUG LUAV=5.2 prefix=/sw + +# PLAT: linux macosx win32 win64 mingw +# platform to build for +PLAT?=linux + +# LUAV: 5.1 5.2 5.3 5.4 +# lua version to build against +LUAV?=5.1 + +# MYCFLAGS: to be set by user if needed +MYCFLAGS?= + +# MYLDFLAGS: to be set by user if needed +MYLDFLAGS?= + +# DEBUG: NODEBUG DEBUG +# debug mode causes luasocket to collect and returns timing information useful +# for testing and debugging luasocket itself +DEBUG?=NODEBUG + +# where lua headers are found for macosx builds +# LUAINC_macosx: +# /opt/local/include +LUAINC_macosx_base?=/opt/local/include +LUAINC_macosx?=$(LUAINC_macosx_base)/lua/$(LUAV) $(LUAINC_macosx_base)/lua$(LUAV) $(LUAINC_macosx_base)/lua-$(LUAV) + +# FIXME default should this default to fink or to macports? +# What happens when more than one Lua version is installed? +LUAPREFIX_macosx?=/opt/local +CDIR_macosx?=lib/lua/$(LUAV) +LDIR_macosx?=share/lua/$(LUAV) + +# LUAINC_linux: +# /usr/include/lua$(LUAV) +# /usr/local/include +# /usr/local/include/lua$(LUAV) +# where lua headers are found for linux builds +LUAINC_linux_base?=/usr/include +LUAINC_linux?=$(LUAINC_linux_base)/lua/$(LUAV) $(LUAINC_linux_base)/lua$(LUAV) +LUAPREFIX_linux?=/usr/local +CDIR_linux?=lib/lua/$(LUAV) +LDIR_linux?=share/lua/$(LUAV) + +# LUAINC_freebsd: +# /usr/local/include/lua$(LUAV) +# where lua headers are found for freebsd builds +LUAINC_freebsd_base?=/usr/local/include/ +LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua/$(LUAV) $(LUAINC_freebsd_base)/lua$(LUAV) +LUAPREFIX_freebsd?=/usr/local/ +CDIR_freebsd?=lib/lua/$(LUAV) +LDIR_freebsd?=share/lua/$(LUAV) + +# where lua headers are found for mingw builds +# LUAINC_mingw: +# /opt/local/include +LUAINC_mingw_base?=/usr/include +LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) $(LUAINC_mingw_base)/lua$(LUAV) +LUALIB_mingw_base?=/usr/bin +LUALIB_mingw?=$(LUALIB_mingw_base)/lua/$(LUAV)/lua$(subst .,,$(LUAV)).dll +LUAPREFIX_mingw?=/usr +CDIR_mingw?=lua/$(LUAV) +LDIR_mingw?=lua/$(LUAV)/lua + + +# LUAINC_win32: +# LUALIB_win32: +# where lua headers and libraries are found for win32 builds +LUAPREFIX_win32?= +LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) $(LUAPREFIX_win32)/include/lua$(LUAV) +PLATFORM_win32?=Release +CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) +LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua +LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) +LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib + +# LUAINC_win64: +# LUALIB_win64: +# where lua headers and libraries are found for win64 builds +LUAPREFIX_win64?= +LUAINC_win64?=$(LUAPREFIX_win64)/include/lua/$(LUAV) $(LUAPREFIX_win64)/include/lua$(LUAV) +PLATFORM_win64?=x64/Release +CDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64) +LDIR_win64?=bin/lua/$(LUAV)/$(PLATFORM_win64)/lua +LUALIB_win64?=$(LUAPREFIX_win64)/lib/lua/$(LUAV)/$(PLATFORM_win64) +LUALIBNAME_win64?=lua$(subst .,,$(LUAV)).lib + + +# LUAINC_solaris: +LUAINC_solaris_base?=/usr/include +LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) $(LUAINC_solaris_base)/lua$(LUAV) +LUAPREFIX_solaris?=/usr/local +CDIR_solaris?=lib/lua/$(LUAV) +LDIR_solaris?=share/lua/$(LUAV) + +# prefix: /usr/local /usr /opt/local /sw +# the top of the default install tree +prefix?=$(LUAPREFIX_$(PLAT)) + +CDIR?=$(CDIR_$(PLAT)) +LDIR?=$(LDIR_$(PLAT)) + +# DESTDIR: (no default) +# used by package managers to install into a temporary destination +DESTDIR?= + +#------ +# Definitions below can be overridden on the make command line, but +# shouldn't have to be. + + +#------ +# Install directories +# + +INSTALL_DIR=install -d +INSTALL_DATA=install -m644 +INSTALL_EXEC=install +INSTALL_TOP=$(DESTDIR)$(prefix) + +INSTALL_TOP_LDIR=$(INSTALL_TOP)/$(LDIR) +INSTALL_TOP_CDIR=$(INSTALL_TOP)/$(CDIR) + +INSTALL_SOCKET_LDIR=$(INSTALL_TOP_LDIR)/socket +INSTALL_SOCKET_CDIR=$(INSTALL_TOP_CDIR)/socket +INSTALL_MIME_LDIR=$(INSTALL_TOP_LDIR)/mime +INSTALL_MIME_CDIR=$(INSTALL_TOP_CDIR)/mime + +print: + @echo PLAT=$(PLAT) + @echo LUAV=$(LUAV) + @echo DEBUG=$(DEBUG) + @echo prefix=$(prefix) + @echo LUAINC_$(PLAT)=$(LUAINC_$(PLAT)) + @echo LUALIB_$(PLAT)=$(LUALIB_$(PLAT)) + @echo INSTALL_TOP_CDIR=$(INSTALL_TOP_CDIR) + @echo INSTALL_TOP_LDIR=$(INSTALL_TOP_LDIR) + @echo CFLAGS=$(CFLAGS) + @echo LDFLAGS=$(LDFLAGS) + +#------ +# Supported platforms +# +PLATS= macosx linux win32 win64 mingw solaris + +#------ +# Compiler and linker settings +# for Mac OS X +SO_macosx=so +O_macosx=o +CC_macosx=gcc +DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +CFLAGS_macosx=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o +LD_macosx=gcc +SOCKET_macosx=usocket.o + +#------ +# Compiler and linker settings +# for Linux +SO_linux=so +O_linux=o +CC_linux=gcc +DEF_linux=-DLUASOCKET_$(DEBUG) +CFLAGS_linux=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_linux=-O -shared -fpic -o +LD_linux=gcc +SOCKET_linux=usocket.o + +#------ +# Compiler and linker settings +# for FreeBSD +SO_freebsd=so +O_freebsd=o +CC_freebsd=gcc +DEF_freebsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN +CFLAGS_freebsd=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_freebsd=-O -shared -fpic -o +LD_freebsd=gcc +SOCKET_freebsd=usocket.o + +#------ +# Compiler and linker settings +# for Solaris +SO_solaris=so +O_solaris=o +CC_solaris=gcc +DEF_solaris=-DLUASOCKET_$(DEBUG) +CFLAGS_solaris=$(LUAINC:%=-I%) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic +LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o +LD_solaris=gcc +SOCKET_solaris=usocket.o + +#------ +# Compiler and linker settings +# for MingW +SO_mingw=dll +O_mingw=o +CC_mingw=gcc +DEF_mingw= -DLUASOCKET_$(DEBUG) \ + -DWINVER=0x0501 +CFLAGS_mingw=$(LUAINC:%=-I%) $(DEF) -Wall -O2 -fno-common +LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o +LD_mingw=gcc +SOCKET_mingw=wsocket.o + + +#------ +# Compiler and linker settings +# for Win32 +SO_win32=dll +O_win32=obj +CC_win32=cl +DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ + //D "_CRT_SECURE_NO_WARNINGS" \ + //D "_WINDLL" \ + //D "LUASOCKET_$(DEBUG)" +CFLAGS_win32=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ + //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ + /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ + //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ + //MACHINE:X86 /LIBPATH:"$(LUALIB)" \ + $(LUALIBNAME_win32) ws2_32.lib //OUT: + +LD_win32=cl +SOCKET_win32=wsocket.obj + +#------ +# Compiler and linker settings +# for Win64 +SO_win64=dll +O_win64=obj +CC_win64=cl +DEF_win64= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ + //D "_CRT_SECURE_NO_WARNINGS" \ + //D "_WINDLL" \ + //D "LUASOCKET_$(DEBUG)" +CFLAGS_win64=$(LUAINC:%=//I "%") $(DEF) //O2 //Ot //MD //W3 //nologo +LDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ + //MANIFEST //MANIFESTFILE:"intermediate.manifest" \ + /MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ + //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ + /LIBPATH:"$(LUALIB)" \ + $(LUALIBNAME_win64) ws2_32.lib //OUT: + +LD_win64=cl +SOCKET_win64=wsocket.obj + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) //Fo"$@" //c $< + +#------ +# Output file names +# +SO=$(SO_$(PLAT)) +O=$(O_$(PLAT)) +SOCKET_V=3.0.0 +MIME_V=1.0.3 +SOCKET_SO=socket-$(SOCKET_V).$(SO) +MIME_SO=mime-$(MIME_V).$(SO) +UNIX_SO=unix.$(SO) +SERIAL_SO=serial.$(SO) +SOCKET=$(SOCKET_$(PLAT)) + +#------ +# Settings selected for platform +# +CC=$(CC_$(PLAT)) +DEF=$(DEF_$(PLAT)) +CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) +LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) +LD=$(LD_$(PLAT)) +LUAINC= $(LUAINC_$(PLAT)) +LUALIB= $(LUALIB_$(PLAT)) + +#------ +# Modules belonging to socket-core +# +SOCKET_OBJS= \ + luasocket.$(O) \ + timeout.$(O) \ + buffer.$(O) \ + io.$(O) \ + auxiliar.$(O) \ + compat.$(O) \ + options.$(O) \ + inet.$(O) \ + $(SOCKET) \ + except.$(O) \ + select.$(O) \ + tcp.$(O) \ + udp.$(O) + +#------ +# Modules belonging mime-core +# +MIME_OBJS= \ + mime.$(O) \ + compat.$(O) + +#------ +# Modules belonging unix (local domain sockets) +# +UNIX_OBJS=\ + buffer.$(O) \ + auxiliar.$(O) \ + options.$(O) \ + timeout.$(O) \ + io.$(O) \ + usocket.$(O) \ + unixstream.$(O) \ + unixdgram.$(O) \ + compat.$(O) \ + unix.$(O) + +#------ +# Modules belonging to serial (device streams) +# +SERIAL_OBJS=\ + buffer.$(O) \ + compat.$(O) \ + auxiliar.$(O) \ + options.$(O) \ + timeout.$(O) \ + io.$(O) \ + usocket.$(O) \ + serial.$(O) + +#------ +# Files to install +# +TO_SOCKET_LDIR= \ + http.lua \ + url.lua \ + tp.lua \ + ftp.lua \ + headers.lua \ + smtp.lua + +TO_TOP_LDIR= \ + ltn12.lua \ + socket.lua \ + mime.lua + +#------ +# Targets +# +default: $(PLAT) + + +freebsd: + $(MAKE) all-unix PLAT=freebsd + +macosx: + $(MAKE) all-unix PLAT=macosx + +win32: + $(MAKE) all PLAT=win32 + +win64: + $(MAKE) all PLAT=win64 + +linux: + $(MAKE) all-unix PLAT=linux + +mingw: + $(MAKE) all PLAT=mingw + +solaris: + $(MAKE) all-unix PLAT=solaris + +none: + @echo "Please run" + @echo " make PLATFORM" + @echo "where PLATFORM is one of these:" + @echo " $(PLATS)" + +all: $(SOCKET_SO) $(MIME_SO) + +$(SOCKET_SO): $(SOCKET_OBJS) + $(LD) $(SOCKET_OBJS) $(LDFLAGS)$@ + +$(MIME_SO): $(MIME_OBJS) + $(LD) $(MIME_OBJS) $(LDFLAGS)$@ + +all-unix: all $(UNIX_SO) $(SERIAL_SO) + +$(UNIX_SO): $(UNIX_OBJS) + $(LD) $(UNIX_OBJS) $(LDFLAGS)$@ + +$(SERIAL_SO): $(SERIAL_OBJS) + $(LD) $(SERIAL_OBJS) $(LDFLAGS)$@ + +install: + $(INSTALL_DIR) $(INSTALL_TOP_LDIR) + $(INSTALL_DATA) $(TO_TOP_LDIR) $(INSTALL_TOP_LDIR) + $(INSTALL_DIR) $(INSTALL_SOCKET_LDIR) + $(INSTALL_DATA) $(TO_SOCKET_LDIR) $(INSTALL_SOCKET_LDIR) + $(INSTALL_DIR) $(INSTALL_SOCKET_CDIR) + $(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_CDIR)/core.$(SO) + $(INSTALL_DIR) $(INSTALL_MIME_CDIR) + $(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_CDIR)/core.$(SO) + +install-unix: install + $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_CDIR)/$(UNIX_SO) + $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_CDIR)/$(SERIAL_SO) + +local: + $(MAKE) install INSTALL_TOP_CDIR=.. INSTALL_TOP_LDIR=.. + +clean: + rm -f $(SOCKET_SO) $(SOCKET_OBJS) $(SERIAL_OBJS) + rm -f $(MIME_SO) $(UNIX_SO) $(SERIAL_SO) $(MIME_OBJS) $(UNIX_OBJS) + +.PHONY: all $(PLATS) default clean echo none + +#------ +# List of dependencies +# +compat.$(O): compat.c compat.h +auxiliar.$(O): auxiliar.c auxiliar.h +buffer.$(O): buffer.c buffer.h io.h timeout.h +except.$(O): except.c except.h +inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h +io.$(O): io.c io.h timeout.h +luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \ + timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \ + udp.h select.h +mime.$(O): mime.c mime.h +options.$(O): options.c auxiliar.h options.h socket.h io.h \ + timeout.h usocket.h inet.h +select.$(O): select.c socket.h io.h timeout.h usocket.h select.h +serial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ + options.h unix.h buffer.h +tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \ + inet.h options.h tcp.h buffer.h +timeout.$(O): timeout.c auxiliar.h timeout.h +udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \ + inet.h options.h udp.h +unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \ + options.h unix.h buffer.h +usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h +wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h diff --git a/vendor/luasocket/src/mbox.lua b/vendor/luasocket/src/mbox.lua new file mode 100644 index 00000000..12823b0a --- /dev/null +++ b/vendor/luasocket/src/mbox.lua @@ -0,0 +1,93 @@ +local _M = {} + +if module then + mbox = _M -- luacheck: ignore +end + +function _M.split_message(message_s) + local message = {} + message_s = string.gsub(message_s, "\r\n", "\n") + string.gsub(message_s, "^(.-\n)\n", function (h) message.headers = h end) + string.gsub(message_s, "^.-\n\n(.*)", function (b) message.body = b end) + if not message.body then + string.gsub(message_s, "^\n(.*)", function (b) message.body = b end) + end + if not message.headers and not message.body then + message.headers = message_s + end + return message.headers or "", message.body or "" +end + +function _M.split_headers(headers_s) + local headers = {} + headers_s = string.gsub(headers_s, "\r\n", "\n") + headers_s = string.gsub(headers_s, "\n[ ]+", " ") + string.gsub("\n" .. headers_s, "\n([^\n]+)", function (h) table.insert(headers, h) end) + return headers +end + +function _M.parse_header(header_s) + header_s = string.gsub(header_s, "\n[ ]+", " ") + header_s = string.gsub(header_s, "\n+", "") + local _, _, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") + return name, value +end + +function _M.parse_headers(headers_s) + local headers_t = _M.split_headers(headers_s) + local headers = {} + for i = 1, #headers_t do + local name, value = _M.parse_header(headers_t[i]) + if name then + name = string.lower(name) + if headers[name] then + headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + end + return headers +end + +function _M.parse_from(from) + local _, _, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") + if not address then + _, _, address = string.find(from, "%s*(.+)%s*") + end + name = name or "" + address = address or "" + if name == "" then name = address end + name = string.gsub(name, '"', "") + return name, address +end + +function _M.split_mbox(mbox_s) + local mbox = {} + mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" + local nj, i + local j = 1 + while 1 do + i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) + if not i then break end + local message = string.sub(mbox_s, j, i-1) + table.insert(mbox, message) + j = nj+1 + end + return mbox +end + +function _M.parse(mbox_s) + local mbox = _M.split_mbox(mbox_s) + for i = 1, #mbox do + mbox[i] = _M.parse_message(mbox[i]) + end + return mbox +end + +function _M.parse_message(message_s) + local message = {} + message.headers, message.body = _M.split_message(message_s) + message.headers = _M.parse_headers(message.headers) + return message +end + +return _M diff --git a/vendor/luasocket/src/mime.c b/vendor/luasocket/src/mime.c new file mode 100755 index 00000000..05602f56 --- /dev/null +++ b/vendor/luasocket/src/mime.c @@ -0,0 +1,852 @@ +/*=========================================================================*\ +* MIME support functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "mime.h" +#include +#include + +/*=========================================================================*\ +* Don't want to trust escape character constants +\*=========================================================================*/ +typedef unsigned char UC; +static const char CRLF[] = "\r\n"; +static const char EQCRLF[] = "=\r\n"; + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int mime_global_wrp(lua_State *L); +static int mime_global_b64(lua_State *L); +static int mime_global_unb64(lua_State *L); +static int mime_global_qp(lua_State *L); +static int mime_global_unqp(lua_State *L); +static int mime_global_qpwrp(lua_State *L); +static int mime_global_eol(lua_State *L); +static int mime_global_dot(lua_State *L); + +static size_t dot(int c, size_t state, luaL_Buffer *buffer); +/*static void b64setup(UC *base);*/ +static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer); +static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); + +/*static void qpsetup(UC *class, UC *unbase);*/ +static void qpquote(UC c, luaL_Buffer *buffer); +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer); +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer); + +/* code support functions */ +static luaL_Reg func[] = { + { "dot", mime_global_dot }, + { "b64", mime_global_b64 }, + { "eol", mime_global_eol }, + { "qp", mime_global_qp }, + { "qpwrp", mime_global_qpwrp }, + { "unb64", mime_global_unb64 }, + { "unqp", mime_global_unqp }, + { "wrp", mime_global_wrp }, + { NULL, NULL } +}; + +/*-------------------------------------------------------------------------*\ +* Quoted-printable globals +\*-------------------------------------------------------------------------*/ +enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; + +static const UC qpclass[] = { + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_CR, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_QUOTED, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, + QP_PLAIN, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, + QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED +}; + +static const UC qpbase[] = "0123456789ABCDEF"; + +static const UC qpunbase[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, + 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255 +}; + +/*-------------------------------------------------------------------------*\ +* Base64 globals +\*-------------------------------------------------------------------------*/ +static const UC b64base[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const UC b64unbase[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, + 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255 +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_mime_core(lua_State *L) +{ + lua_newtable(L); + luaL_setfuncs(L, func, 0); + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, MIME_VERSION); + lua_rawset(L, -3); + /* initialize lookup tables */ + /*qpsetup(qpclass, qpunbase);*/ + /*b64setup(b64unbase);*/ + return 1; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Incrementaly breaks a string into lines. The string can have CRLF breaks. +* A, n = wrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +\*-------------------------------------------------------------------------*/ +static int mime_global_wrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end of input black-hole */ + if (!input) { + /* if last line has not been terminated, add a line break */ + if (left < length) lua_pushstring(L, CRLF); + /* otherwise, we are done */ + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + luaL_addstring(&buffer, CRLF); + left = length; + break; + default: + if (left <= 0) { + left = length; + luaL_addstring(&buffer, CRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +#if 0 +/*-------------------------------------------------------------------------*\ +* Fill base64 decode map. +\*-------------------------------------------------------------------------*/ +static void b64setup(UC *unbase) +{ + int i; + for (i = 0; i <= 255; i++) unbase[i] = (UC) 255; + for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i; + unbase['='] = 0; + + printf("static const UC b64unbase[] = {\n"); + for (int i = 0; i < 256; i++) { + printf("%d, ", unbase[i]); + } + printf("\n}\n;"); +} +#endif + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 3 bytes are available. +* Translate the 3 bytes into Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64encode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + input[size++] = c; + if (size == 3) { + UC code[4]; + unsigned long value = 0; + value += input[0]; value <<= 8; + value += input[1]; value <<= 8; + value += input[2]; + code[3] = b64base[value & 0x3f]; value >>= 6; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + size = 0; + } + return size; +} + +/*-------------------------------------------------------------------------*\ +* Encodes the Base64 last 1 or 2 bytes and adds padding '=' +* Result, if any, is appended to buffer. +* Returns 0. +\*-------------------------------------------------------------------------*/ +static size_t b64pad(const UC *input, size_t size, + luaL_Buffer *buffer) +{ + unsigned long value = 0; + UC code[4] = {'=', '=', '=', '='}; + switch (size) { + case 1: + value = input[0] << 4; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + case 2: + value = input[0]; value <<= 8; + value |= input[1]; value <<= 2; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; + code[0] = b64base[value]; + luaL_addlstring(buffer, (char *) code, 4); + break; + default: + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Acumulates bytes in input buffer until 4 bytes are available. +* Translate the 4 bytes from Base64 form and append to buffer. +* Returns new number of bytes in buffer. +\*-------------------------------------------------------------------------*/ +static size_t b64decode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) +{ + /* ignore invalid characters */ + if (b64unbase[c] > 64) return size; + input[size++] = c; + /* decode atom */ + if (size == 4) { + UC decoded[3]; + int valid, value = 0; + value = b64unbase[input[0]]; value <<= 6; + value |= b64unbase[input[1]]; value <<= 6; + value |= b64unbase[input[2]]; value <<= 6; + value |= b64unbase[input[3]]; + decoded[2] = (UC) (value & 0xff); value >>= 8; + decoded[1] = (UC) (value & 0xff); value >>= 8; + decoded[0] = (UC) value; + /* take care of paddding */ + valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; + luaL_addlstring(buffer, (char *) decoded, valid); + return 0; + /* need more data */ + } else return size; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies the Base64 transfer content encoding to a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 3. B has the remaining bytes of C .. D, *without* encoding. +* The easiest thing would be to concatenate the two strings and +* encode the result, but we can't afford that or Lua would dupplicate +* every chunk we received. +\*-------------------------------------------------------------------------*/ +static int mime_global_b64(lua_State *L) +{ + UC atom[3]; + size_t isize = 0, asize = 0; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + size_t osize = 0; + asize = b64pad(atom, asize, &buffer); + luaL_pushresult(&buffer); + /* if the output is empty and the input is nil, return nil */ + lua_tolstring(L, -1, &osize); + if (osize == 0) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process the second part */ + last = input + isize; + while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally removes the Base64 transfer content encoding from a string +* A, B = b64(C, D) +* A is the encoded version of the largest prefix of C .. D that is +* divisible by 4. B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unb64(lua_State *L) +{ + UC atom[4]; + size_t isize = 0, asize = 0; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second is nil, we are done */ + if (!input) { + size_t osize = 0; + luaL_pushresult(&buffer); + /* if the output is empty and the input is nil, return nil */ + lua_tolstring(L, -1, &osize); + if (osize == 0) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise, process the rest of the input */ + last = input + isize; + while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Quoted-printable encoding scheme +* all (except CRLF in text) can be =XX +* CLRL in not text must be =XX=XX +* 33 through 60 inclusive can be plain +* 62 through 126 inclusive can be plain +* 9 and 32 can be plain, unless in the end of a line, where must be =XX +* encoded lines must be no longer than 76 not counting CRLF +* soft line-break are =CRLF +* To encode one byte, we need to see the next two. +* Worst case is when we see a space, and wonder if a CRLF is comming +\*-------------------------------------------------------------------------*/ +#if 0 +/*-------------------------------------------------------------------------*\ +* Split quoted-printable characters into classes +* Precompute reverse map for encoding +\*-------------------------------------------------------------------------*/ +static void qpsetup(UC *cl, UC *unbase) +{ + + int i; + for (i = 0; i < 256; i++) cl[i] = QP_QUOTED; + for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN; + for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN; + cl['\t'] = QP_IF_LAST; + cl[' '] = QP_IF_LAST; + cl['\r'] = QP_CR; + for (i = 0; i < 256; i++) unbase[i] = 255; + unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2; + unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5; + unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8; + unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10; + unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12; + unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13; + unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15; + unbase['f'] = 15; + +printf("static UC qpclass[] = {"); + for (int i = 0; i < 256; i++) { + if (i % 6 == 0) { + printf("\n "); + } + switch(cl[i]) { + case QP_QUOTED: + printf("QP_QUOTED, "); + break; + case QP_PLAIN: + printf("QP_PLAIN, "); + break; + case QP_CR: + printf("QP_CR, "); + break; + case QP_IF_LAST: + printf("QP_IF_LAST, "); + break; + } + } +printf("\n};\n"); + +printf("static const UC qpunbase[] = {"); + for (int i = 0; i < 256; i++) { + int c = qpunbase[i]; + printf("%d, ", c); + } +printf("\";\n"); +} +#endif + +/*-------------------------------------------------------------------------*\ +* Output one character in form =XX +\*-------------------------------------------------------------------------*/ +static void qpquote(UC c, luaL_Buffer *buffer) +{ + luaL_addchar(buffer, '='); + luaL_addchar(buffer, qpbase[c >> 4]); + luaL_addchar(buffer, qpbase[c & 0x0F]); +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer) +{ + input[size++] = c; + /* deal with all characters we can have */ + while (size > 0) { + switch (qpclass[input[0]]) { + /* might be the CR of a CRLF sequence */ + case QP_CR: + if (size < 2) return size; + if (input[1] == '\n') { + luaL_addstring(buffer, marker); + return 0; + } else qpquote(input[0], buffer); + break; + /* might be a space and that has to be quoted if last in line */ + case QP_IF_LAST: + if (size < 3) return size; + /* if it is the last, quote it and we are done */ + if (input[1] == '\r' && input[2] == '\n') { + qpquote(input[0], buffer); + luaL_addstring(buffer, marker); + return 0; + } else luaL_addchar(buffer, input[0]); + break; + /* might have to be quoted always */ + case QP_QUOTED: + qpquote(input[0], buffer); + break; + /* might never have to be quoted */ + default: + luaL_addchar(buffer, input[0]); + break; + } + input[0] = input[1]; input[1] = input[2]; + size--; + } + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Deal with the final characters +\*-------------------------------------------------------------------------*/ +static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) +{ + size_t i; + for (i = 0; i < size; i++) { + if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]); + else qpquote(input[i], buffer); + } + if (size > 0) luaL_addstring(buffer, EQCRLF); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally converts a string to quoted-printable +* A, B = qp(C, D, marker) +* Marker is the text to be used to replace CRLF sequences found in A. +* A is the encoded version of the largest prefix of C .. D that +* can be encoded without doubts. +* B has the remaining bytes of C .. D, *without* encoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_qp(lua_State *L) +{ + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 3); + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + asize = qppad(atom, asize, &buffer); + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Accumulate characters until we are sure about how to deal with them. +* Once we are sure, output the to the buffer, in the correct form. +\*-------------------------------------------------------------------------*/ +static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + int d; + input[size++] = c; + /* deal with all characters we can deal */ + switch (input[0]) { + /* if we have an escape character */ + case '=': + if (size < 3) return size; + /* eliminate soft line break */ + if (input[1] == '\r' && input[2] == '\n') return 0; + /* decode quoted representation */ + c = qpunbase[input[1]]; d = qpunbase[input[2]]; + /* if it is an invalid, do not decode */ + if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3); + else luaL_addchar(buffer, (char) ((c << 4) + d)); + return 0; + case '\r': + if (size < 2) return size; + if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2); + return 0; + default: + if (input[0] == '\t' || (input[0] > 31 && input[0] < 127)) + luaL_addchar(buffer, input[0]); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally decodes a string in quoted-printable +* A, B = qp(C, D) +* A is the decoded version of the largest prefix of C .. D that +* can be decoded without doubts. +* B has the remaining bytes of C .. D, *without* decoding. +\*-------------------------------------------------------------------------*/ +static int mime_global_unqp(lua_State *L) +{ + size_t asize = 0, isize = 0; + UC atom[3]; + const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* make sure we don't confuse buffer stuff with arguments */ + lua_settop(L, 2); + /* process first part of input */ + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; + } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Incrementally breaks a quoted-printed string into lines +* A, n = qpwrp(l, B, length) +* A is a copy of B, broken into lines of at most 'length' bytes. +* 'l' is how many bytes are left for the first line of B. +* 'n' is the number of bytes left in the last line of A. +* There are two complications: lines can't be broken in the middle +* of an encoded =XX, and there might be line breaks already +\*-------------------------------------------------------------------------*/ +static int mime_global_qpwrp(lua_State *L) +{ + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); + const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + if (left < length) lua_pushstring(L, EQCRLF); + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { + case '\r': + break; + case '\n': + left = length; + luaL_addstring(&buffer, CRLF); + break; + case '=': + if (left <= 3) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + default: + if (left <= 1) { + left = length; + luaL_addstring(&buffer, EQCRLF); + } + luaL_addchar(&buffer, *input); + left--; + break; + } + input++; + } + luaL_pushresult(&buffer); + lua_pushnumber(L, left); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Here is what we do: \n, and \r are considered candidates for line +* break. We issue *one* new line marker if any of them is seen alone, or +* followed by a different one. That is, \n\n and \r\r will issue two +* end of line markers each, but \r\n, \n\r etc will only issue *one* +* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as +* probably other more obscure conventions. +* +* c is the current character being processed +* last is the previous character +\*-------------------------------------------------------------------------*/ +#define eolcandidate(c) (c == '\r' || c == '\n') +static int eolprocess(int c, int last, const char *marker, + luaL_Buffer *buffer) +{ + if (eolcandidate(c)) { + if (eolcandidate(last)) { + if (c == last) luaL_addstring(buffer, marker); + return 0; + } else { + luaL_addstring(buffer, marker); + return c; + } + } else { + luaL_addchar(buffer, (char) c); + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Converts a string to uniform EOL convention. +* A, n = eol(o, B, marker) +* A is the converted version of the largest prefix of B that can be +* converted unambiguously. 'o' is the context returned by the previous +* call. 'n' is the new context. +\*-------------------------------------------------------------------------*/ +static int mime_global_eol(lua_State *L) +{ + int ctx = (int) luaL_checkinteger(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; + luaL_buffinit(L, &buffer); + /* end of input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 0); + return 2; + } + /* process all input */ + while (input < last) + ctx = eolprocess(*input++, ctx, marker, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, ctx); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Takes one byte and stuff it if needed. +\*-------------------------------------------------------------------------*/ +static size_t dot(int c, size_t state, luaL_Buffer *buffer) +{ + luaL_addchar(buffer, (char) c); + switch (c) { + case '\r': + return 1; + case '\n': + return (state == 1)? 2: 0; + case '.': + if (state == 2) + luaL_addchar(buffer, '.'); + /* Falls through. */ + default: + return 0; + } +} + +/*-------------------------------------------------------------------------*\ +* Incrementally applies smtp stuffing to a string +* A, n = dot(l, D) +\*-------------------------------------------------------------------------*/ +static int mime_global_dot(lua_State *L) +{ + size_t isize = 0, state = (size_t) luaL_checknumber(L, 1); + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ + if (!input) { + lua_pushnil(L); + lua_pushnumber(L, 2); + return 2; + } + /* process all input */ + luaL_buffinit(L, &buffer); + while (input < last) + state = dot(*input++, state, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, (lua_Number) state); + return 2; +} + diff --git a/vendor/luasocket/src/mime.h b/vendor/luasocket/src/mime.h new file mode 100644 index 00000000..4d938f46 --- /dev/null +++ b/vendor/luasocket/src/mime.h @@ -0,0 +1,22 @@ +#ifndef MIME_H +#define MIME_H +/*=========================================================================*\ +* Core MIME support +* LuaSocket toolkit +* +* This module provides functions to implement transfer content encodings +* and formatting conforming to RFC 2045. It is used by mime.lua, which +* provide a higher level interface to this functionality. +\*=========================================================================*/ +#include "luasocket.h" + +/*-------------------------------------------------------------------------*\ +* Current MIME library version +\*-------------------------------------------------------------------------*/ +#define MIME_VERSION "MIME 1.0.3" +#define MIME_COPYRIGHT "Copyright (C) 2004-2013 Diego Nehab" +#define MIME_AUTHORS "Diego Nehab" + +LUASOCKET_API int luaopen_mime_core(lua_State *L); + +#endif /* MIME_H */ diff --git a/vendor/luasocket/src/mime.lua b/vendor/luasocket/src/mime.lua new file mode 100644 index 00000000..93539de6 --- /dev/null +++ b/vendor/luasocket/src/mime.lua @@ -0,0 +1,81 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local ltn12 = require("ltn12") +local mime = require("mime.core") +local _M = mime + +-- encode, decode and wrap algorithm tables +local encodet, decodet, wrapt = {},{},{} + +_M.encodet = encodet +_M.decodet = decodet +_M.wrapt = wrapt + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(_M.b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(_M.qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(_M.unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(_M.unqp, "") +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(_M.wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(_M.qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +_M.encode = choose(encodet) +_M.decode = choose(decodet) +_M.wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function _M.normalize(marker) + return ltn12.filter.cycle(_M.eol, 0, marker) +end + +-- high level stuffing filter +function _M.stuff() + return ltn12.filter.cycle(_M.dot, 2) +end + +return _M diff --git a/vendor/luasocket/src/options.c b/vendor/luasocket/src/options.c new file mode 100644 index 00000000..3280c51d --- /dev/null +++ b/vendor/luasocket/src/options.c @@ -0,0 +1,480 @@ +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" +#include "auxiliar.h" +#include "options.h" +#include "inet.h" +#include + +/*=========================================================================*\ +* Internal functions prototypes +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name); +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_setint(lua_State *L, p_socket ps, int level, int name); +static int opt_getint(lua_State *L, p_socket ps, int level, int name); +static int opt_set(lua_State *L, p_socket ps, int level, int name, + void *val, int len); +static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Calls appropriate option handler +\*-------------------------------------------------------------------------*/ +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[57]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +/*------------------------------------------------------*/ +/* enables reuse of local address */ +int opt_set_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +int opt_get_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +/*------------------------------------------------------*/ +/* enables reuse of local port */ +int opt_set_reuseport(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); +} + +int opt_get_reuseport(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); +} + +/*------------------------------------------------------*/ +/* disables the Nagle algorithm */ +int opt_set_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +int opt_get_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +/*------------------------------------------------------*/ +#ifdef TCP_KEEPIDLE + +int opt_get_tcp_keepidle(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE); +} + +int opt_set_tcp_keepidle(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE); +} + +#endif + +/*------------------------------------------------------*/ +#ifdef TCP_KEEPCNT + +int opt_get_tcp_keepcnt(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPCNT); +} + +int opt_set_tcp_keepcnt(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPCNT); +} + +#endif + +/*------------------------------------------------------*/ +#ifdef TCP_KEEPINTVL + +int opt_get_tcp_keepintvl(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL); +} + +int opt_set_tcp_keepintvl(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL); +} + +#endif + +/*------------------------------------------------------*/ +int opt_set_keepalive(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_get_keepalive(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +/*------------------------------------------------------*/ +int opt_set_dontroute(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +int opt_get_dontroute(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +/*------------------------------------------------------*/ +int opt_set_broadcast(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +int opt_get_broadcast(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +/*------------------------------------------------------*/ +int opt_set_recv_buf_size(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, SOL_SOCKET, SO_RCVBUF); +} + +int opt_get_recv_buf_size(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, SOL_SOCKET, SO_RCVBUF); +} + +/*------------------------------------------------------*/ +int opt_get_send_buf_size(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, SOL_SOCKET, SO_SNDBUF); +} + +int opt_set_send_buf_size(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, SOL_SOCKET, SO_SNDBUF); +} + +// /*------------------------------------------------------*/ + +#ifdef TCP_FASTOPEN +int opt_set_tcp_fastopen(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN); +} +#endif + +#ifdef TCP_FASTOPEN_CONNECT +int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN_CONNECT); +} +#endif + +/*------------------------------------------------------*/ + +#ifdef TCP_DEFER_ACCEPT +int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_TCP, TCP_DEFER_ACCEPT); +} +#endif + +/*------------------------------------------------------*/ +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +} + +/*------------------------------------------------------*/ +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) +{ + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); +} + +/*------------------------------------------------------*/ +int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +/*------------------------------------------------------*/ +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +} + +/*------------------------------------------------------*/ +int opt_set_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name, table */ + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "on"); + lua_gettable(L, 3); + if (!lua_isboolean(L, -1)) + luaL_argerror(L, 3, "boolean 'on' field expected"); + li.l_onoff = (u_short) lua_toboolean(L, -1); + lua_pushstring(L, "timeout"); + lua_gettable(L, 3); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "number 'timeout' field expected"); + li.l_linger = (u_short) lua_tonumber(L, -1); + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li)); +} + +int opt_get_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name */ + int len = sizeof(li); + int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len); + if (err) + return err; + lua_newtable(L); + lua_pushboolean(L, li.l_onoff); + lua_setfield(L, -2, "on"); + lua_pushinteger(L, li.l_linger); + lua_setfield(L, -2, "timeout"); + return 1; +} + +/*------------------------------------------------------*/ +int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) +{ + return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); +} + +/*------------------------------------------------------*/ +int opt_set_ip_multicast_if(lua_State *L, p_socket ps) +{ + const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ + struct in_addr val; + val.s_addr = htonl(INADDR_ANY); + if (strcmp(address, "*") && !inet_aton(address, &val)) + luaL_argerror(L, 3, "ip expected"); + return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &val, sizeof(val)); +} + +int opt_get_ip_multicast_if(lua_State *L, p_socket ps) +{ + struct in_addr val; + socklen_t len = sizeof(val); + if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, inet_ntoa(val)); + return 1; +} + +/*------------------------------------------------------*/ +int opt_set_ip_add_membership(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); +} + +int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); +} + +/*------------------------------------------------------*/ +int opt_set_ip6_add_membership(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +} + +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) +{ + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP); +} + +/*------------------------------------------------------*/ +int opt_get_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} + +int opt_set_ip6_v6only(lua_State *L, p_socket ps) +{ + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); +} + +/*------------------------------------------------------*/ +int opt_get_error(lua_State *L, p_socket ps) +{ + int val = 0; + socklen_t len = sizeof(val); + if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + lua_pushstring(L, socket_strerror(val)); + return 1; +} + +/*=========================================================================*\ +* Auxiliar functions +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ip_mreq val; /* obj, name, table */ + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + val.imr_interface.s_addr = htonl(INADDR_ANY); + if (strcmp(lua_tostring(L, -1), "*") && + !inet_aton(lua_tostring(L, -1), &val.imr_interface)) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) +{ + struct ipv6_mreq val; /* obj, opt-name, table */ + memset(&val, 0, sizeof(val)); + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + /* By default we listen to interface on default route + * (sigh). However, interface= can override it. We should + * support either number, or name for it. Waiting for + * windows port of if_nametoindex */ + if (!lua_isnil(L, -1)) { + if (lua_isnumber(L, -1)) { + val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1); + } else + luaL_argerror(L, -1, "number 'interface' field expected"); + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static +int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +{ + socklen_t socklen = *len; + if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + *len = socklen; + return 0; +} + +static +int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) +{ + if (setsockopt(*ps, level, name, (char *) val, len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushboolean(L, val); + return 1; +} + +static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static int opt_getint(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushnumber(L, val); + return 1; +} + +static int opt_setint(lua_State *L, p_socket ps, int level, int name) +{ + int val = (int) lua_tonumber(L, 3); /* obj, name, int */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} diff --git a/vendor/luasocket/src/options.h b/vendor/luasocket/src/options.h new file mode 100644 index 00000000..456eeb5f --- /dev/null +++ b/vendor/luasocket/src/options.h @@ -0,0 +1,113 @@ +#ifndef OPTIONS_H +#define OPTIONS_H +/*=========================================================================*\ +* Common option interface +* LuaSocket toolkit +* +* This module provides a common interface to socket options, used mainly by +* modules UDP and TCP. +\*=========================================================================*/ + +#include "luasocket.h" +#include "socket.h" + +/* option registry */ +typedef struct t_opt { + const char *name; + int (*func)(lua_State *L, p_socket ps); +} t_opt; +typedef t_opt *p_opt; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps); + +int opt_set_reuseaddr(lua_State *L, p_socket ps); +int opt_get_reuseaddr(lua_State *L, p_socket ps); + +int opt_set_reuseport(lua_State *L, p_socket ps); +int opt_get_reuseport(lua_State *L, p_socket ps); + +int opt_set_tcp_nodelay(lua_State *L, p_socket ps); +int opt_get_tcp_nodelay(lua_State *L, p_socket ps); + +#ifdef TCP_KEEPIDLE +int opt_set_tcp_keepidle(lua_State *L, p_socket ps); +int opt_get_tcp_keepidle(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_KEEPCNT +int opt_set_tcp_keepcnt(lua_State *L, p_socket ps); +int opt_get_tcp_keepcnt(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_KEEPINTVL +int opt_set_tcp_keepintvl(lua_State *L, p_socket ps); +int opt_get_tcp_keepintvl(lua_State *L, p_socket ps); +#endif + +#ifdef TCP_DEFER_ACCEPT +int opt_set_tcp_defer_accept(lua_State *L, p_socket ps); +#endif + +int opt_set_keepalive(lua_State *L, p_socket ps); +int opt_get_keepalive(lua_State *L, p_socket ps); + +int opt_set_dontroute(lua_State *L, p_socket ps); +int opt_get_dontroute(lua_State *L, p_socket ps); + +int opt_set_broadcast(lua_State *L, p_socket ps); +int opt_get_broadcast(lua_State *L, p_socket ps); + +int opt_set_recv_buf_size(lua_State *L, p_socket ps); +int opt_get_recv_buf_size(lua_State *L, p_socket ps); + +int opt_set_send_buf_size(lua_State *L, p_socket ps); +int opt_get_send_buf_size(lua_State *L, p_socket ps); + +#ifdef TCP_FASTOPEN +int opt_set_tcp_fastopen(lua_State *L, p_socket ps); +#endif +#ifdef TCP_FASTOPEN_CONNECT +int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps); +#endif + +int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); + +int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); + +int opt_set_ip_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); + +int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps); +int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); + +int opt_set_linger(lua_State *L, p_socket ps); +int opt_get_linger(lua_State *L, p_socket ps); + +int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps); + +int opt_set_ip_multicast_if(lua_State *L, p_socket ps); +int opt_get_ip_multicast_if(lua_State *L, p_socket ps); + +int opt_set_ip_add_membership(lua_State *L, p_socket ps); +int opt_set_ip_drop_membersip(lua_State *L, p_socket ps); + +int opt_set_ip6_add_membership(lua_State *L, p_socket ps); +int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps); + +int opt_set_ip6_v6only(lua_State *L, p_socket ps); +int opt_get_ip6_v6only(lua_State *L, p_socket ps); + +int opt_get_error(lua_State *L, p_socket ps); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif diff --git a/vendor/luasocket/src/pierror.h b/vendor/luasocket/src/pierror.h new file mode 100644 index 00000000..cb773ab7 --- /dev/null +++ b/vendor/luasocket/src/pierror.h @@ -0,0 +1,28 @@ +#ifndef PIERROR_H +#define PIERROR_H +/*=========================================================================*\ +* Error messages +* Defines platform independent error messages +\*=========================================================================*/ + +#define PIE_HOST_NOT_FOUND "host not found" +#define PIE_ADDRINUSE "address already in use" +#define PIE_ISCONN "already connected" +#define PIE_ACCESS "permission denied" +#define PIE_CONNREFUSED "connection refused" +#define PIE_CONNABORTED "closed" +#define PIE_CONNRESET "closed" +#define PIE_TIMEDOUT "timeout" +#define PIE_AGAIN "temporary failure in name resolution" +#define PIE_BADFLAGS "invalid value for ai_flags" +#define PIE_BADHINTS "invalid value for hints" +#define PIE_FAIL "non-recoverable failure in name resolution" +#define PIE_FAMILY "ai_family not supported" +#define PIE_MEMORY "memory allocation failure" +#define PIE_NONAME "host or service not provided, or not known" +#define PIE_OVERFLOW "argument buffer overflow" +#define PIE_PROTOCOL "resolved protocol is unknown" +#define PIE_SERVICE "service not supported for socket type" +#define PIE_SOCKTYPE "ai_socktype not supported" + +#endif diff --git a/vendor/luasocket/src/select.c b/vendor/luasocket/src/select.c new file mode 100644 index 00000000..bb47c459 --- /dev/null +++ b/vendor/luasocket/src/select.c @@ -0,0 +1,214 @@ +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "socket.h" +#include "timeout.h" +#include "select.h" + +#include + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static t_socket getfd(lua_State *L); +static int dirty(lua_State *L); +static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd); +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start); +static void make_assoc(lua_State *L, int tab); +static int global_select(lua_State *L); + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"select", global_select}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int select_open(lua_State *L) { + lua_pushstring(L, "_SETSIZE"); + lua_pushinteger(L, FD_SETSIZE); + lua_rawset(L, -3); + lua_pushstring(L, "_SOCKETINVALID"); + lua_pushinteger(L, SOCKET_INVALID); + lua_rawset(L, -3); + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Global Lua functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Waits for a set of sockets until a condition is met or timeout. +\*-------------------------------------------------------------------------*/ +static int global_select(lua_State *L) { + int rtab, wtab, itab, ret, ndirty; + t_socket max_fd = SOCKET_INVALID; + fd_set rset, wset; + t_timeout tm; + double t = luaL_optnumber(L, 3, -1); + FD_ZERO(&rset); FD_ZERO(&wset); + lua_settop(L, 3); + lua_newtable(L); itab = lua_gettop(L); + lua_newtable(L); rtab = lua_gettop(L); + lua_newtable(L); wtab = lua_gettop(L); + collect_fd(L, 1, itab, &rset, &max_fd); + collect_fd(L, 2, itab, &wset, &max_fd); + ndirty = check_dirty(L, 1, rtab, &rset); + t = ndirty > 0? 0.0: t; + timeout_init(&tm, t, -1); + timeout_markstart(&tm); + ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm); + if (ret > 0 || ndirty > 0) { + return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); + return_fd(L, &wset, max_fd+1, itab, wtab, 0); + make_assoc(L, rtab); + make_assoc(L, wtab); + return 2; + } else if (ret == 0) { + lua_pushstring(L, "timeout"); + return 3; + } else { + luaL_error(L, "select failed"); + return 3; + } +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +static t_socket getfd(lua_State *L) { + t_socket fd = SOCKET_INVALID; + lua_pushstring(L, "getfd"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (lua_isnumber(L, -1)) { + double numfd = lua_tonumber(L, -1); + fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID; + } + } + lua_pop(L, 1); + return fd; +} + +static int dirty(lua_State *L) { + int is = 0; + lua_pushstring(L, "dirty"); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + is = lua_toboolean(L, -1); + } + lua_pop(L, 1); + return is; +} + +static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd) { + int i = 1, n = 0; + /* nil is the same as an empty table */ + if (lua_isnil(L, tab)) return; + /* otherwise we need it to be a table */ + luaL_checktype(L, tab, LUA_TTABLE); + for ( ;; ) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + /* getfd figures out if this is a socket */ + fd = getfd(L); + if (fd != SOCKET_INVALID) { + /* make sure we don't overflow the fd_set */ +#ifdef _WIN32 + if (n >= FD_SETSIZE) + luaL_argerror(L, tab, "too many sockets"); +#else + if (fd >= FD_SETSIZE) + luaL_argerror(L, tab, "descriptor too large for set size"); +#endif + FD_SET(fd, set); + n++; + /* keep track of the largest descriptor so far */ + if (*max_fd == SOCKET_INVALID || *max_fd < fd) + *max_fd = fd; + /* make sure we can map back from descriptor to the object */ + lua_pushnumber(L, (lua_Number) fd); + lua_pushvalue(L, -2); + lua_settable(L, itab); + } + lua_pop(L, 1); + i = i + 1; + } +} + +static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { + int ndirty = 0, i = 1; + if (lua_isnil(L, tab)) + return 0; + for ( ;; ) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + break; + } + fd = getfd(L); + if (fd != SOCKET_INVALID && dirty(L)) { + lua_pushnumber(L, ++ndirty); + lua_pushvalue(L, -2); + lua_settable(L, dtab); + FD_CLR(fd, set); + } + lua_pop(L, 1); + i = i + 1; + } + return ndirty; +} + +static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start) { + t_socket fd; + for (fd = 0; fd < max_fd; fd++) { + if (FD_ISSET(fd, set)) { + lua_pushnumber(L, ++start); + lua_pushnumber(L, (lua_Number) fd); + lua_gettable(L, itab); + lua_settable(L, tab); + } + } +} + +static void make_assoc(lua_State *L, int tab) { + int i = 1, atab; + lua_newtable(L); atab = lua_gettop(L); + for ( ;; ) { + lua_pushnumber(L, i); + lua_gettable(L, tab); + if (!lua_isnil(L, -1)) { + lua_pushnumber(L, i); + lua_pushvalue(L, -2); + lua_settable(L, atab); + lua_pushnumber(L, i); + lua_settable(L, atab); + } else { + lua_pop(L, 1); + break; + } + i = i+1; + } +} diff --git a/vendor/luasocket/src/select.h b/vendor/luasocket/src/select.h new file mode 100644 index 00000000..5d45fe75 --- /dev/null +++ b/vendor/luasocket/src/select.h @@ -0,0 +1,23 @@ +#ifndef SELECT_H +#define SELECT_H +/*=========================================================================*\ +* Select implementation +* LuaSocket toolkit +* +* Each object that can be passed to the select function has to export +* method getfd() which returns the descriptor to be passed to the +* underlying select function. Another method, dirty(), should return +* true if there is data ready for reading (required for buffered input). +\*=========================================================================*/ + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int select_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* SELECT_H */ diff --git a/vendor/luasocket/src/serial.c b/vendor/luasocket/src/serial.c new file mode 100644 index 00000000..21485d3e --- /dev/null +++ b/vendor/luasocket/src/serial.c @@ -0,0 +1,171 @@ +/*=========================================================================*\ +* Serial stream +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" + +#include +#include + +/* +Reuses userdata definition from unix.h, since it is useful for all +stream-like objects. + +If we stored the serial path for use in error messages or userdata +printing, we might need our own userdata definition. + +Group usage is semi-inherited from unix.c, but unnecessary since we +have only one object type. +*/ + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); + +/* serial object methods */ +static luaL_Reg serial_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_serial(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "serial{client}", serial_methods); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); + lua_pushcfunction(L, global_create); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ + + +/*-------------------------------------------------------------------------*\ +* Creates a serial object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); + + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + + /* open serial device */ + t_socket sock = open(path, O_NOCTTY|O_RDWR); + + /*printf("open %s on %d\n", path, sock);*/ + + if (sock < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } + /* set its type as client object */ + auxiliar_setclass(L, "serial{client}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; +} diff --git a/vendor/luasocket/src/smtp.lua b/vendor/luasocket/src/smtp.lua new file mode 100644 index 00000000..b113d006 --- /dev/null +++ b/vendor/luasocket/src/smtp.lua @@ -0,0 +1,256 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require("socket") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +local headers = require("socket.headers") +local mime = require("mime") + +socket.smtp = {} +local _M = socket.smtp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +_M.TIMEOUT = 60 +-- default server used to send e-mails +_M.SERVER = "localhost" +-- default port +_M.PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +_M.DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +_M.ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or _M.DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(user) .. "\r\n")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(password) .. "\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT, + _M.TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +end + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it's faster +local function send_headers(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f,v in base.pairs(tosend) do + h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from string + coroutine.yield(mesgt.body) +end + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then send_multipart(mesgt) + elseif base.type(mesgt.body) == "function" then send_source(mesgt) + else send_string(mesgt) end +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function _M.message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +_M.send = socket.protect(function(mailt) + local s = _M.open(mailt.server, mailt.port, mailt.create) + local ext = s:greet(mailt.domain) + s:auth(mailt.user, mailt.password, ext) + s:send(mailt) + s:quit() + return s:close() +end) + +return _M \ No newline at end of file diff --git a/vendor/luasocket/src/socket.h b/vendor/luasocket/src/socket.h new file mode 100755 index 00000000..2555bab6 --- /dev/null +++ b/vendor/luasocket/src/socket.h @@ -0,0 +1,75 @@ +#ifndef SOCKET_H +#define SOCKET_H +/*=========================================================================*\ +* Socket compatibilization module +* LuaSocket toolkit +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Platform specific compatibilization +\*=========================================================================*/ +#ifdef _WIN32 +#include "wsocket.h" +#define LUA_GAI_STRERROR gai_strerrorA +#else +#include "usocket.h" +#define LUA_GAI_STRERROR gai_strerror +#endif + +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + +/* convenient shorthand */ +typedef struct sockaddr SA; + +/*=========================================================================*\ +* Functions bellow implement a comfortable platform independent +* interface to sockets +\*=========================================================================*/ + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int socket_waitfd(p_socket ps, int sw, p_timeout tm); +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_timeout tm); +int socket_create(p_socket ps, int domain, int type, int protocol); +int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); +int socket_listen(p_socket ps, int backlog); +void socket_shutdown(p_socket ps, int how); +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *addr_len, p_timeout tm); +int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +void socket_setblocking(p_socket ps); +void socket_setnonblocking(p_socket ps); +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); +int socket_gethostbyname(const char *addr, struct hostent **hp); +const char *socket_hoststrerror(int err); +const char *socket_strerror(int err); +const char *socket_ioerror(p_socket ps, int err); +const char *socket_gaistrerror(int err); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* SOCKET_H */ diff --git a/vendor/luasocket/src/socket.lua b/vendor/luasocket/src/socket.lua new file mode 100644 index 00000000..d1c0b164 --- /dev/null +++ b/vendor/luasocket/src/socket.lua @@ -0,0 +1,149 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") + +local _M = socket + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function _M.connect4(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet") +end + +function _M.connect6(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet6") +end + +function _M.bind(host, port, backlog) + if host == "*" then host = "0.0.0.0" end + local addrinfo, err = socket.dns.getaddrinfo(host); + if not addrinfo then return nil, err end + local sock, res + err = "no info on address" + for i, alt in base.ipairs(addrinfo) do + if alt.family == "inet" then + sock, err = socket.tcp4() + else + sock, err = socket.tcp6() + end + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + res, err = sock:bind(alt.addr, port) + if not res then + sock:close() + else + res, err = sock:listen(backlog) + if not res then + sock:close() + else + return sock + end + end + end + return nil, err +end + +_M.try = _M.newtry() + +function _M.choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +local sourcet, sinkt = {}, {} +_M.sourcet = sourcet +_M.sinkt = sinkt + +_M.BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +_M.sink = _M.choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +_M.source = _M.choose(sourcet) + +return _M diff --git a/vendor/luasocket/src/tcp.c b/vendor/luasocket/src/tcp.c new file mode 100644 index 00000000..e84db845 --- /dev/null +++ b/vendor/luasocket/src/tcp.c @@ -0,0 +1,480 @@ +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "tcp.h" + +#include + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int global_create4(lua_State *L); +static int global_create6(lua_State *L); +static int global_connect(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_getfamily(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_getoption(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* tcp object methods */ +static luaL_Reg tcp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfamily", meth_getfamily}, + {"getfd", meth_getfd}, + {"getoption", meth_getoption}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optget[] = { + {"keepalive", opt_get_keepalive}, + {"reuseaddr", opt_get_reuseaddr}, + {"reuseport", opt_get_reuseport}, + {"tcp-nodelay", opt_get_tcp_nodelay}, +#ifdef TCP_KEEPIDLE + {"tcp-keepidle", opt_get_tcp_keepidle}, +#endif +#ifdef TCP_KEEPCNT + {"tcp-keepcnt", opt_get_tcp_keepcnt}, +#endif +#ifdef TCP_KEEPINTVL + {"tcp-keepintvl", opt_get_tcp_keepintvl}, +#endif + {"linger", opt_get_linger}, + {"error", opt_get_error}, + {"recv-buffer-size", opt_get_recv_buf_size}, + {"send-buffer-size", opt_get_send_buf_size}, + {NULL, NULL} +}; + +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"reuseport", opt_set_reuseport}, + {"tcp-nodelay", opt_set_tcp_nodelay}, +#ifdef TCP_KEEPIDLE + {"tcp-keepidle", opt_set_tcp_keepidle}, +#endif +#ifdef TCP_KEEPCNT + {"tcp-keepcnt", opt_set_tcp_keepcnt}, +#endif +#ifdef TCP_KEEPINTVL + {"tcp-keepintvl", opt_set_tcp_keepintvl}, +#endif + {"ipv6-v6only", opt_set_ip6_v6only}, + {"linger", opt_set_linger}, + {"recv-buffer-size", opt_set_recv_buf_size}, + {"send-buffer-size", opt_set_send_buf_size}, +#ifdef TCP_DEFER_ACCEPT + {"tcp-defer-accept", opt_set_tcp_defer_accept}, +#endif +#ifdef TCP_FASTOPEN + {"tcp-fastopen", opt_set_tcp_fastopen}, +#endif +#ifdef TCP_FASTOPEN_CONNECT + {"tcp-fastopen-connect", opt_set_tcp_fastopen_connect}, +#endif + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"tcp", global_create}, + {"tcp4", global_create4}, + {"tcp6", global_create6}, + {"connect", global_connect}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int tcp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "tcp{master}", tcp_methods); + auxiliar_newclass(L, "tcp{client}", tcp_methods); + auxiliar_newclass(L, "tcp{server}", tcp_methods); + /* create class groups */ + auxiliar_add2group(L, "tcp{master}", "tcp{any}"); + auxiliar_add2group(L, "tcp{client}", "tcp{any}"); + auxiliar_add2group(L, "tcp{server}", "tcp{any}"); + /* define library functions */ + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_send(L, &tcp->buf); +} + +static int meth_receive(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_receive(L, &tcp->buf); +} + +static int meth_getstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_getstats(L, &tcp->buf); +} + +static int meth_setstats(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + return buffer_meth_setstats(L, &tcp->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_getoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_getoption(L, optget, &tcp->sock); +} + +static int meth_setoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_setoption(L, optset, &tcp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushnumber(L, (int) tcp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + tcp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + lua_pushboolean(L, !buffer_isempty(&tcp->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) +{ + p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm); + /* if successful, push client socket */ + if (err == NULL) { + p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + auxiliar_setclass(L, "tcp{client}", -1); + /* initialize structure fields */ + memset(clnt, 0, sizeof(t_tcp)); + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + clnt->family = server->family; + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static int meth_bind(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + const char *err; + struct addrinfo bindhints; + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = tcp->family; + bindhints.ai_flags = AI_PASSIVE; + err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master tcp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_connect(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + struct addrinfo connecthints; + const char *err; + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_STREAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = tcp->family; + timeout_markstart(&tcp->tm); + err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, + &tcp->tm, &connecthints); + /* have to set the class even if it failed due to non-blocking connects */ + auxiliar_setclass(L, "tcp{client}", 1); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + socket_destroy(&tcp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Returns family as string +\*-------------------------------------------------------------------------*/ +static int meth_getfamily(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + if (tcp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; + } else if (tcp->family == AF_INET) { + lua_pushliteral(L, "inet4"); + return 1; + } else { + lua_pushliteral(L, "inet4"); + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&tcp->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "tcp{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&tcp->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getpeername(L, &tcp->sock, tcp->family); +} + +static int meth_getsockname(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return inet_meth_getsockname(L, &tcp->sock, tcp->family); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return timeout_meth_settimeout(L, &tcp->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return timeout_meth_gettimeout(L, &tcp->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master tcp object +\*-------------------------------------------------------------------------*/ +static int tcp_create(lua_State *L, int family) { + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + memset(tcp, 0, sizeof(t_tcp)); + /* set its type as master object */ + auxiliar_setclass(L, "tcp{master}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + tcp->sock = SOCKET_INVALID; + tcp->family = family; + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + socket_setnonblocking(&tcp->sock); + } + return 1; +} + +static int global_create(lua_State *L) { + return tcp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); +} + +static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); +} + +static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); + const char *remoteserv = luaL_checkstring(L, 2); + const char *localaddr = luaL_optstring(L, 3, NULL); + const char *localserv = luaL_optstring(L, 4, "0"); + int family = inet_optfamily(L, 5, "unspec"); + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + struct addrinfo bindhints, connecthints; + const char *err = NULL; + /* initialize tcp structure */ + memset(tcp, 0, sizeof(t_tcp)); + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + tcp->sock = SOCKET_INVALID; + tcp->family = AF_UNSPEC; + /* allow user to pick local address and port */ + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = family; + bindhints.ai_flags = AI_PASSIVE; + if (localaddr) { + err = inet_trybind(&tcp->sock, &tcp->family, localaddr, + localserv, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + } + /* try to connect to remote address and port */ + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_STREAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = tcp->family; + err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, + &tcp->tm, &connecthints); + if (err) { + socket_destroy(&tcp->sock); + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + auxiliar_setclass(L, "tcp{client}", -1); + return 1; +} diff --git a/vendor/luasocket/src/tcp.h b/vendor/luasocket/src/tcp.h new file mode 100644 index 00000000..9b282efe --- /dev/null +++ b/vendor/luasocket/src/tcp.h @@ -0,0 +1,43 @@ +#ifndef TCP_H +#define TCP_H +/*=========================================================================*\ +* TCP object +* LuaSocket toolkit +* +* The tcp.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created tcp object, that has not been bound or connected. Server +* objects are tcp objects bound to some local address. Client objects are +* tcp objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "luasocket.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_tcp_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; + int family; +} t_tcp; + +typedef t_tcp *p_tcp; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int tcp_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* TCP_H */ diff --git a/vendor/luasocket/src/timeout.c b/vendor/luasocket/src/timeout.c new file mode 100644 index 00000000..2bdc0698 --- /dev/null +++ b/vendor/luasocket/src/timeout.c @@ -0,0 +1,226 @@ +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "timeout.h" + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int timeout_lua_gettime(lua_State *L); +static int timeout_lua_sleep(lua_State *L); + +static luaL_Reg func[] = { + { "gettime", timeout_lua_gettime }, + { "sleep", timeout_lua_sleep }, + { NULL, NULL } +}; + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initialize structure +\*-------------------------------------------------------------------------*/ +void timeout_init(p_timeout tm, double block, double total) { + tm->block = block; + tm->total = total; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was successful +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_get(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + return tm->block; + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Returns time since start of operation +* Input +* tm: timeout control structure +* Returns +* start field of structure +\*-------------------------------------------------------------------------*/ +double timeout_getstart(p_timeout tm) { + return tm->start; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was a failure +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_getretry(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +p_timeout timeout_markstart(p_timeout tm) { + tm->start = timeout_gettime(); + return tm; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in s, relative to January 1, 1970 (UTC) +* Returns +* time in s. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +double timeout_gettime(void) { + FILETIME ft; + double t; + GetSystemTimeAsFileTime(&ft); + /* Windows file time (time since January 1, 1601 (UTC)) */ + t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); + /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ + return (t - 11644473600.0); +} +#else +double timeout_gettime(void) { + struct timeval v; + gettimeofday(&v, (struct timezone *) NULL); + /* Unix Epoch time (time since January 1, 1970 (UTC)) */ + return v.tv_sec + v.tv_usec/1.0e6; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int timeout_open(lua_State *L) { + luaL_setfuncs(L, func, 0); + return 0; +} + +/*-------------------------------------------------------------------------*\ +* Sets timeout values for IO operations +* Lua Input: base, time [, mode] +* time: time out value in seconds +* mode: "b" for block timeout, "t" for total timeout. (default: b) +\*-------------------------------------------------------------------------*/ +int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + double t = luaL_optnumber(L, 2, -1); + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': + tm->block = t; + break; + case 'r': case 't': + tm->total = t; + break; + default: + luaL_argcheck(L, 0, 3, "invalid timeout mode"); + break; + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Gets timeout values for IO operations +* Lua Output: block, total +\*-------------------------------------------------------------------------*/ +int timeout_meth_gettimeout(lua_State *L, p_timeout tm) { + lua_pushnumber(L, tm->block); + lua_pushnumber(L, tm->total); + return 2; +} + +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +static int timeout_lua_gettime(lua_State *L) +{ + lua_pushnumber(L, timeout_gettime()); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + if (n < 0.0) n = 0.0; + if (n < DBL_MAX/1000.0) n *= 1000.0; + if (n > INT_MAX) n = INT_MAX; + Sleep((int)n); + return 0; +} +#else +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + struct timespec t, r; + if (n < 0.0) n = 0.0; + if (n > INT_MAX) n = INT_MAX; + t.tv_sec = (int) n; + n -= t.tv_sec; + t.tv_nsec = (int) (n * 1000000000); + if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; + while (nanosleep(&t, &r) != 0) { + t.tv_sec = r.tv_sec; + t.tv_nsec = r.tv_nsec; + } + return 0; +} +#endif diff --git a/vendor/luasocket/src/timeout.h b/vendor/luasocket/src/timeout.h new file mode 100644 index 00000000..9e5250d3 --- /dev/null +++ b/vendor/luasocket/src/timeout.h @@ -0,0 +1,40 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H +/*=========================================================================*\ +* Timeout management functions +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +/* timeout control structure */ +typedef struct t_timeout_ { + double block; /* maximum time for blocking calls */ + double total; /* total number of miliseconds for operation */ + double start; /* time of start of operation */ +} t_timeout; +typedef t_timeout *p_timeout; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +void timeout_init(p_timeout tm, double block, double total); +double timeout_get(p_timeout tm); +double timeout_getstart(p_timeout tm); +double timeout_getretry(p_timeout tm); +p_timeout timeout_markstart(p_timeout tm); + +double timeout_gettime(void); + +int timeout_open(lua_State *L); + +int timeout_meth_settimeout(lua_State *L, p_timeout tm); +int timeout_meth_gettimeout(lua_State *L, p_timeout tm); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#define timeout_iszero(tm) ((tm)->block == 0.0) + +#endif /* TIMEOUT_H */ diff --git a/vendor/luasocket/src/tp.lua b/vendor/luasocket/src/tp.lua new file mode 100644 index 00000000..b8ebc56d --- /dev/null +++ b/vendor/luasocket/src/tp.lua @@ -0,0 +1,134 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") + +socket.tp = {} +local _M = socket.tp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +_M.TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:getpeername() + return self.c:getpeername() +end + +function metat.__index:getsockname() + return self.c:getpeername() +end + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + cmd = string.upper(cmd) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = self.c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function _M.connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or _M.TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end + +return _M diff --git a/vendor/luasocket/src/udp.c b/vendor/luasocket/src/udp.c new file mode 100755 index 00000000..712ad50f --- /dev/null +++ b/vendor/luasocket/src/udp.c @@ -0,0 +1,488 @@ +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "inet.h" +#include "options.h" +#include "udp.h" + +#include +#include + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int global_create4(lua_State *L); +static int global_create6(lua_State *L); +static int meth_send(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_getfamily(lua_State *L); +static int meth_getsockname(lua_State *L); +static int meth_getpeername(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_setsockname(lua_State *L); +static int meth_setpeername(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_getoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); + +/* udp object methods */ +static luaL_Reg udp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfamily", meth_getfamily}, + {"getfd", meth_getfd}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"getoption", meth_getoption}, + {"setpeername", meth_setpeername}, + {"setsockname", meth_setsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket options for setoption */ +static t_opt optset[] = { + {"dontroute", opt_set_dontroute}, + {"broadcast", opt_set_broadcast}, + {"reuseaddr", opt_set_reuseaddr}, + {"reuseport", opt_set_reuseport}, + {"ip-multicast-if", opt_set_ip_multicast_if}, + {"ip-multicast-ttl", opt_set_ip_multicast_ttl}, + {"ip-multicast-loop", opt_set_ip_multicast_loop}, + {"ip-add-membership", opt_set_ip_add_membership}, + {"ip-drop-membership", opt_set_ip_drop_membersip}, + {"ipv6-unicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_set_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_set_ip6_multicast_loop}, + {"ipv6-add-membership", opt_set_ip6_add_membership}, + {"ipv6-drop-membership", opt_set_ip6_drop_membersip}, + {"ipv6-v6only", opt_set_ip6_v6only}, + {"recv-buffer-size", opt_set_recv_buf_size}, + {"send-buffer-size", opt_set_send_buf_size}, + {NULL, NULL} +}; + +/* socket options for getoption */ +static t_opt optget[] = { + {"dontroute", opt_get_dontroute}, + {"broadcast", opt_get_broadcast}, + {"reuseaddr", opt_get_reuseaddr}, + {"reuseport", opt_get_reuseport}, + {"ip-multicast-if", opt_get_ip_multicast_if}, + {"ip-multicast-loop", opt_get_ip_multicast_loop}, + {"error", opt_get_error}, + {"ipv6-unicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-hops", opt_get_ip6_unicast_hops}, + {"ipv6-multicast-loop", opt_get_ip6_multicast_loop}, + {"ipv6-v6only", opt_get_ip6_v6only}, + {"recv-buffer-size", opt_get_recv_buf_size}, + {"send-buffer-size", opt_get_send_buf_size}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"udp", global_create}, + {"udp4", global_create4}, + {"udp6", global_create6}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int udp_open(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "udp{connected}", udp_methods); + auxiliar_newclass(L, "udp{unconnected}", udp_methods); + /* create class groups */ + auxiliar_add2group(L, "udp{connected}", "udp{any}"); + auxiliar_add2group(L, "udp{unconnected}", "udp{any}"); + auxiliar_add2group(L, "udp{connected}", "select{able}"); + auxiliar_add2group(L, "udp{unconnected}", "select{able}"); + /* define library functions */ + luaL_setfuncs(L, func, 0); + /* export default UDP size */ + lua_pushliteral(L, "_DATAGRAMSIZE"); + lua_pushinteger(L, UDP_DATAGRAMSIZE); + lua_rawset(L, -3); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *udp_strerror(int err) { + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +/*-------------------------------------------------------------------------*\ +* Send data through connected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + p_timeout tm = &udp->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&udp->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected udp socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *ip = luaL_checkstring(L, 3); + const char *port = luaL_checkstring(L, 4); + p_timeout tm = &udp->tm; + int err; + struct addrinfo aihint; + struct addrinfo *ai; + memset(&aihint, 0, sizeof(aihint)); + aihint.ai_family = udp->family; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_flags = AI_NUMERICHOST; +#ifdef AI_NUMERICSERV + aihint.ai_flags |= AI_NUMERICSERV; +#endif + err = getaddrinfo(ip, port, &aihint, &ai); + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); + return 2; + } + + /* create socket if on first sendto if AF_UNSPEC was set */ + if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) { + struct addrinfo *ap; + const char *errstr = NULL; + for (ap = ai; ap != NULL; ap = ap->ai_next) { + errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0); + if (errstr == NULL) { + socket_setnonblocking(&udp->sock); + udp->family = ap->ai_family; + break; + } + } + if (errstr != NULL) { + lua_pushnil(L); + lua_pushstring(L, errstr); + freeaddrinfo(ai); + return 2; + } + } + + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + (socklen_t) ai->ai_addrlen, tm); + freeaddrinfo(ai); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receive(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + char buf[UDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &udp->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&udp->sock, dgram, wanted, &got, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + char buf[UDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + int err; + p_timeout tm = &udp->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + lua_pushstring(L, addrstr); + lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); + if (wanted > sizeof(buf)) free(dgram); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* Returns family as string +\*-------------------------------------------------------------------------*/ +static int meth_getfamily(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + if (udp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; + } else { + lua_pushliteral(L, "inet4"); + return 1; + } +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + lua_pushnumber(L, (int) udp->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + udp->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + (void) udp; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call inet methods +\*-------------------------------------------------------------------------*/ +static int meth_getpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1); + return inet_meth_getpeername(L, &udp->sock, udp->family); +} + +static int meth_getsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return inet_meth_getsockname(L, &udp->sock, udp->family); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return opt_meth_setoption(L, optset, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_getoption(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return opt_meth_getoption(L, optget, &udp->sock); +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return timeout_meth_settimeout(L, &udp->tm); +} + +static int meth_gettimeout(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + return timeout_meth_gettimeout(L, &udp->tm); +} + +/*-------------------------------------------------------------------------*\ +* Turns a master udp object into a client object. +\*-------------------------------------------------------------------------*/ +static int meth_setpeername(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + p_timeout tm = &udp->tm; + const char *address = luaL_checkstring(L, 2); + int connecting = strcmp(address, "*"); + const char *port = connecting? luaL_checkstring(L, 3): "0"; + struct addrinfo connecthints; + const char *err; + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_DGRAM; + /* make sure we try to connect only to the same family */ + connecthints.ai_family = udp->family; + if (connecting) { + err = inet_tryconnect(&udp->sock, &udp->family, address, + port, tm, &connecthints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + auxiliar_setclass(L, "udp{connected}", 1); + } else { + /* we ignore possible errors because Mac OS X always + * returns EAFNOSUPPORT */ + inet_trydisconnect(&udp->sock, udp->family, tm); + auxiliar_setclass(L, "udp{unconnected}", 1); + } + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + socket_destroy(&udp->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master object into a server object +\*-------------------------------------------------------------------------*/ +static int meth_setsockname(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + const char *err; + struct addrinfo bindhints; + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_DGRAM; + bindhints.ai_family = udp->family; + bindhints.ai_flags = AI_PASSIVE; + err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master udp object +\*-------------------------------------------------------------------------*/ +static int udp_create(lua_State *L, int family) { + /* allocate udp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + auxiliar_setclass(L, "udp{unconnected}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + udp->sock = SOCKET_INVALID; + timeout_init(&udp->tm, -1, -1); + udp->family = family; + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + socket_setnonblocking(&udp->sock); + } + return 1; +} + +static int global_create(lua_State *L) { + return udp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); +} + +static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); +} diff --git a/vendor/luasocket/src/udp.h b/vendor/luasocket/src/udp.h new file mode 100644 index 00000000..07d5247f --- /dev/null +++ b/vendor/luasocket/src/udp.h @@ -0,0 +1,39 @@ +#ifndef UDP_H +#define UDP_H +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* The udp.h module provides LuaSocket with support for UDP protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. UDP objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ +#include "luasocket.h" + +#include "timeout.h" +#include "socket.h" + +#define UDP_DATAGRAMSIZE 8192 + +typedef struct t_udp_ { + t_socket sock; + t_timeout tm; + int family; +} t_udp; +typedef t_udp *p_udp; + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int udp_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* UDP_H */ diff --git a/vendor/luasocket/src/unix.c b/vendor/luasocket/src/unix.c new file mode 100644 index 00000000..268d8b21 --- /dev/null +++ b/vendor/luasocket/src/unix.c @@ -0,0 +1,69 @@ +/*=========================================================================*\ +* Unix domain socket +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "unixstream.h" +#include "unixdgram.h" + +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"stream", unixstream_open}, + {"dgram", unixdgram_open}, + {NULL, NULL} +}; + +static void add_alias(lua_State *L, int index, const char *name, const char *target) +{ + lua_getfield(L, index, target); + lua_setfield(L, index, name); +} + +static int compat_socket_unix_call(lua_State *L) +{ + /* Look up socket.unix.stream in the socket.unix table (which is the first + * argument). */ + lua_getfield(L, 1, "stream"); + + /* Replace the stack entry for the socket.unix table with the + * socket.unix.stream function. */ + lua_replace(L, 1); + + /* Call socket.unix.stream, passing along any arguments. */ + int n = lua_gettop(L); + lua_call(L, n-1, LUA_MULTRET); + + /* Pass along the return values from socket.unix.stream. */ + n = lua_gettop(L); + return n; +} + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +LUASOCKET_API int luaopen_socket_unix(lua_State *L) +{ + int i; + lua_newtable(L); + int socket_unix_table = lua_gettop(L); + + for (i = 0; mod[i].name; i++) + mod[i].func(L); + + /* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and + * "dgram" functions. */ + add_alias(L, socket_unix_table, "tcp", "stream"); + add_alias(L, socket_unix_table, "udp", "dgram"); + + /* Add a backwards compatibility function and a metatable setup to call it + * for the old socket.unix() interface. */ + lua_pushcfunction(L, compat_socket_unix_call); + lua_setfield(L, socket_unix_table, "__call"); + lua_pushvalue(L, socket_unix_table); + lua_setmetatable(L, socket_unix_table); + + return 1; +} diff --git a/vendor/luasocket/src/unix.h b/vendor/luasocket/src/unix.h new file mode 100644 index 00000000..c2035618 --- /dev/null +++ b/vendor/luasocket/src/unix.h @@ -0,0 +1,26 @@ +#ifndef UNIX_H +#define UNIX_H +/*=========================================================================*\ +* Unix domain object +* LuaSocket toolkit +* +* This module is just an example of how to extend LuaSocket with a new +* domain. +\*=========================================================================*/ +#include "luasocket.h" + +#include "buffer.h" +#include "timeout.h" +#include "socket.h" + +typedef struct t_unix_ { + t_socket sock; + t_io io; + t_buffer buf; + t_timeout tm; +} t_unix; +typedef t_unix *p_unix; + +LUASOCKET_API int luaopen_socket_unix(lua_State *L); + +#endif /* UNIX_H */ diff --git a/vendor/luasocket/src/unixdgram.c b/vendor/luasocket/src/unixdgram.c new file mode 100644 index 00000000..69093d73 --- /dev/null +++ b/vendor/luasocket/src/unixdgram.c @@ -0,0 +1,405 @@ +/*=========================================================================*\ +* Unix domain socket dgram submodule +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" + +#include +#include + +#include + +#define UNIXDGRAM_DATAGRAMSIZE 8192 + +/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */ +#ifndef SUN_LEN +#define SUN_LEN(ptr) \ + ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixdgram_tryconnect(p_unix un, const char *path); +static const char *unixdgram_trybind(p_unix un, const char *path); + +/* unixdgram object methods */ +static luaL_Reg unixdgram_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"reuseaddr", opt_set_reuseaddr}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"dgram", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixdgram_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods); + auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods); + /* create class groups */ + auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}"); + auxiliar_add2group(L, "unixdgram{connected}", "select{able}"); + auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *unixdgram_strerror(int err) +{ + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +static int meth_send(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1); + p_timeout tm = &un->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&un->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected unixdgram socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *path = luaL_checkstring(L, 3); + p_timeout tm = &un->tm; + int err; + struct sockaddr_un remote; + size_t len = strlen(path); + + if (len >= sizeof(remote.sun_path)) { + lua_pushnil(L); + lua_pushstring(L, "path too long"); + return 2; + } + + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +#else + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, + sizeof(remote.sun_family) + len, tm); +#endif + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&un->sock, dgram, wanted, &got, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a DGRAM socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + char buf[UNIXDGRAM_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + addr.sun_path[0] = '\0'; + err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixdgram_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + + lua_pushlstring(L, dgram, got); + /* the path may be empty, when client send without bind */ + lua_pushstring(L, addr.sun_path); + if (wanted > sizeof(buf)) free(dgram); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + (void) un; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; + size_t addrlen = SUN_LEN(&local); +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = addrlen + 1; +#endif + int err = socket_bind(&un->sock, (SA *) &local, addrlen); + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixdgram object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixdgram_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); + size_t addrlen = SUN_LEN(&remote); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = addrlen + 1; +#endif + int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm); + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixdgram_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn unconnected object into a connected object */ + auxiliar_setclass(L, "unixdgram{connected}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1); + return timeout_meth_gettimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixdgram object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) +{ + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixdgram object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixdgram{unconnected}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/vendor/luasocket/src/unixdgram.h b/vendor/luasocket/src/unixdgram.h new file mode 100644 index 00000000..a1a0166b --- /dev/null +++ b/vendor/luasocket/src/unixdgram.h @@ -0,0 +1,28 @@ +#ifndef UNIXDGRAM_H +#define UNIXDGRAM_H +/*=========================================================================*\ +* DGRAM object +* LuaSocket toolkit +* +* The dgram.h module provides LuaSocket with support for DGRAM protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. DGRAM objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ + +#include "unix.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int unixdgram_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* UNIXDGRAM_H */ diff --git a/vendor/luasocket/src/unixstream.c b/vendor/luasocket/src/unixstream.c new file mode 100644 index 00000000..02aced9c --- /dev/null +++ b/vendor/luasocket/src/unixstream.c @@ -0,0 +1,355 @@ +/*=========================================================================*\ +* Unix domain socket stream sub module +* LuaSocket toolkit +\*=========================================================================*/ +#include "luasocket.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unixstream.h" + +#include +#include + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixstream_tryconnect(p_unix un, const char *path); +static const char *unixstream_trybind(p_unix un, const char *path); + +/* unixstream object methods */ +static luaL_Reg unixstream_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"linger", opt_set_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"stream", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixstream_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixstream{master}", unixstream_methods); + auxiliar_newclass(L, "unixstream{client}", unixstream_methods); + auxiliar_newclass(L, "unixstream{server}", unixstream_methods); + + /* create class groups */ + auxiliar_add2group(L, "unixstream{master}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{client}", "unixstream{any}"); + auxiliar_add2group(L, "unixstream{server}", "unixstream{any}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + auxiliar_setclass(L, "unixstream{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixstream_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixstream object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixstream_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixstream_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + auxiliar_setclass(L, "unixstream{client}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&un->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "unixstream{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&stream->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixstream object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixstream object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixstream{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/vendor/luasocket/src/unixstream.h b/vendor/luasocket/src/unixstream.h new file mode 100644 index 00000000..7916affa --- /dev/null +++ b/vendor/luasocket/src/unixstream.h @@ -0,0 +1,29 @@ +#ifndef UNIXSTREAM_H +#define UNIXSTREAM_H +/*=========================================================================*\ +* UNIX STREAM object +* LuaSocket toolkit +* +* The unixstream.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created unixstream object, that has not been bound or connected. Server +* objects are unixstream objects bound to some local address. Client objects are +* unixstream objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "unix.h" + +#ifndef _WIN32 +#pragma GCC visibility push(hidden) +#endif + +int unixstream_open(lua_State *L); + +#ifndef _WIN32 +#pragma GCC visibility pop +#endif + +#endif /* UNIXSTREAM_H */ diff --git a/vendor/luasocket/src/url.lua b/vendor/luasocket/src/url.lua new file mode 100644 index 00000000..8e0dc5ce --- /dev/null +++ b/vendor/luasocket/src/url.lua @@ -0,0 +1,331 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +local socket = require("socket") + +socket.url = {} +local _M = socket.url + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_M._VERSION = "URL 1.0.3" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function _M.escape(s) + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end)) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed within a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02X", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Unencodes a escaped hexadecimal string into its binary representation +-- Input +-- s: escaped hexadecimal string to be unencoded +-- Returns +-- unescaped binary representation of escaped hexadecimal binary +----------------------------------------------------------------------------- +function _M.unescape(s) + return (string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end)) +end + +----------------------------------------------------------------------------- +-- Removes '..' and '.' components appropriately from a path. +-- Input +-- path +-- Returns +-- dot-normalized path +local function remove_dot_components(path) + local marker = string.char(1) + repeat + local was = path + path = path:gsub('//', '/'..marker..'/', 1) + until path == was + repeat + local was = path + path = path:gsub('/%./', '/', 1) + until path == was + repeat + local was = path + path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1) + until path == was + path = path:gsub('[^/]+/%.%./*$', '') + path = path:gsub('/%.%.$', '/') + path = path:gsub('/%.$', '/') + path = path:gsub('^/%.%./', '/') + path = path:gsub(marker, '') + return path +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then + return remove_dot_components(relative_path) end + base_path = base_path:gsub("[^/]*$", "") + if not base_path:find'/$' then base_path = base_path .. '/' end + local path = base_path .. relative_path + path = remove_dot_components(path) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- ::= :///;?# +-- ::= @: +-- ::= [:] +-- :: = {/} +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/} is considered part of +----------------------------------------------------------------------------- +function _M.parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get query string + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:%]]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then + -- IPv6? + parsed.host = string.match(authority, "^%[(.+)%]$") or authority + end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function _M.build(parsed) + --local ppath = _M.parse_path(parsed.path or "") + --local url = _M.build_path(ppath) + local url = parsed.path or "" + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if string.find(authority, ":") then -- IPv6? + authority = "[" .. authority .. "]" + end + if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function _M.absolute(base_url, relative_url) + local base_parsed + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = _M.build(base_parsed) + else + base_parsed = _M.parse(base_url) + end + local result + local relative_parsed = _M.parse(relative_url) + if not base_parsed then + result = relative_url + elseif not relative_parsed then + result = base_url + elseif relative_parsed.scheme then + result = relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + result = _M.build(relative_parsed) + end + return remove_dot_components(result) +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function _M.parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, #parsed do + parsed[i] = _M.unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function _M.build_path(parsed, unsafe) + local path = "" + local n = #parsed + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end + +return _M diff --git a/vendor/luasocket/src/usocket.c b/vendor/luasocket/src/usocket.c new file mode 100644 index 00000000..69635daa --- /dev/null +++ b/vendor/luasocket/src/usocket.c @@ -0,0 +1,454 @@ +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +* +* The code is now interrupt-safe. +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include "luasocket.h" + +#include "socket.h" +#include "pierror.h" + +#include +#include + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#ifndef SOCKET_SELECT +#include + +#define WAITFD_R POLLIN +#define WAITFD_W POLLOUT +#define WAITFD_C (POLLIN|POLLOUT) +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + struct pollfd pfd; + pfd.fd = *ps; + pfd.events = sw; + pfd.revents = 0; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + int t = (int)(timeout_getretry(tm)*1e3); + ret = poll(&pfd, 1, t >= 0? t: -1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; + return IO_DONE; +} +#else + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp; + double t; + if (*ps >= FD_SETSIZE) return EINVAL; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + /* must set bits within loop, because select may have modifed them */ + rp = wp = NULL; + if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + t = timeout_getretry(tm); + tp = NULL; + if (t >= 0.0) { + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(*ps+1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED; + return IO_DONE; +} +#endif + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + /* installs a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + close(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Select with timeout control +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { + struct timeval tv; + double t = timeout_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + /* timeout = 0 means no wait */ + ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + if (listen(*ps, backlog)) err = errno; + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + shutdown(*ps, how); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE; + else return errno; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + err = errno; + if (err == EINTR) continue; + if (err != EAGAIN && err != ECONNABORTED) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (err == EPIPE) return IO_CLOSED; + if (err == EPROTOTYPE) continue; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recvfrom(*ps, data, count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +* Make sure important error messages are standard +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return hstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case EADDRINUSE: return PIE_ADDRINUSE; + case EISCONN: return PIE_ISCONN; + case EACCES: return PIE_ACCESS; + case ECONNREFUSED: return PIE_CONNREFUSED; + case ECONNABORTED: return PIE_CONNABORTED; + case ECONNRESET: return PIE_CONNRESET; + case ETIMEDOUT: return PIE_TIMEDOUT; + default: { + return strerror(err); + } + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return PIE_OVERFLOW; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; + case EAI_SYSTEM: return strerror(errno); + default: return LUA_GAI_STRERROR(err); + } +} diff --git a/vendor/luasocket/src/usocket.h b/vendor/luasocket/src/usocket.h new file mode 100644 index 00000000..45f2f99f --- /dev/null +++ b/vendor/luasocket/src/usocket.h @@ -0,0 +1,59 @@ +#ifndef USOCKET_H +#define USOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Unix +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include +/* close function */ +#include +/* fnctnl function and associated constants */ +#include +/* struct sockaddr */ +#include +/* socket function */ +#include +/* struct timeval */ +#include +/* gethostbyname and gethostbyaddr functions */ +#include +/* sigpipe handling */ +#include +/* IP stuff*/ +#include +#include +/* TCP options (nagle algorithm disable) */ +#include +#include + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +/* Some platforms use IPV6_JOIN_GROUP instead if + * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ + +/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ + +typedef int t_socket; +typedef t_socket *p_socket; +typedef struct sockaddr_storage t_sockaddr_storage; + +#define SOCKET_INVALID (-1) + +#endif /* USOCKET_H */ diff --git a/vendor/luasocket/src/wsocket.c b/vendor/luasocket/src/wsocket.c new file mode 100755 index 00000000..6cb1e415 --- /dev/null +++ b/vendor/luasocket/src/wsocket.c @@ -0,0 +1,434 @@ +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +\*=========================================================================*/ +#include "luasocket.h" + +#include + +#include "socket.h" +#include "pierror.h" + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int elen = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return PIE_ADDRINUSE; + case WSAECONNREFUSED : return PIE_CONNREFUSED; + case WSAEISCONN: return PIE_ISCONN; + case WSAEACCES: return PIE_ACCESS; + case WSAECONNABORTED: return PIE_CONNABORTED; + case WSAECONNRESET: return PIE_CONNRESET; + case WSAETIMEDOUT: return PIE_TIMEDOUT; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */ + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */ + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */ + case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */ + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */ + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */ + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */ + case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */ + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */ + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */ + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; +#endif + case EAI_FAIL: return PIE_FAIL; + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return PIE_OVERFLOW; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; +#endif + case EAI_SERVICE: return PIE_SERVICE; + case EAI_SOCKTYPE: return PIE_SOCKTYPE; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: return strerror(errno); +#endif + default: return LUA_GAI_STRERROR(err); + } +} diff --git a/vendor/luasocket/src/wsocket.h b/vendor/luasocket/src/wsocket.h new file mode 100644 index 00000000..39866402 --- /dev/null +++ b/vendor/luasocket/src/wsocket.h @@ -0,0 +1,33 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* Socket compatibilization module for Win32 +* LuaSocket toolkit +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include +#include + +typedef int socklen_t; +typedef SOCKADDR_STORAGE t_sockaddr_storage; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +#define SOCKET_INVALID (INVALID_SOCKET) + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV (0) +#endif + +#endif /* WSOCKET_H */ diff --git a/vendor/md5/.gitignore b/vendor/md5/.gitignore new file mode 100644 index 00000000..9776091d --- /dev/null +++ b/vendor/md5/.gitignore @@ -0,0 +1,4 @@ +*.o +*.obj +*.so +*.dll diff --git a/vendor/md5/README.md b/vendor/md5/README.md new file mode 100644 index 00000000..d51492e0 --- /dev/null +++ b/vendor/md5/README.md @@ -0,0 +1,47 @@ +# MD5 - Cryptographic Library for Lua + +http://keplerproject.github.io/md5/ + +MD5 offers basic cryptographic facilities for Lua 5.1: a hash (digest) +function, a pair crypt/decrypt based on MD5 and CFB, and a pair crypt/decrypt based +on DES with 56-bit keys. + +MD5 current version is 1.2. + +Please check the documentation at /doc/us/ for more information. + +## Installation + +To install using [LuaRocks](https://github.com/keplerproject/luarocks) run: + +``` +luarocks install md5 +``` + +To install on Linux/OSX/BSD, please edit the config file and then call + +``` +make +make install +``` + +The last step may require root privileges. + +## History + +Version 1.2 [06/Sep/2013] + +* Code adapted to compile for Lua 5.0, 5.1 and 5.2 + +Version 1.1.2 [12/May/2008] + +* Fixed bug in 64-bit systems +* Fixed the Windows makefile to accept longer directory names + (patch by Alessandro Hecht and Ignacio BurgueƱo). + + +## License + +MD5 is free software and uses the same license as Lua (MIT). + +The DES 56 C library was implemented by Stuart Levy and uses a MIT license too (check the source). diff --git a/vendor/md5/rockspec/md5-1.2-1.rockspec b/vendor/md5/rockspec/md5-1.2-1.rockspec new file mode 100644 index 00000000..04f61ce4 --- /dev/null +++ b/vendor/md5/rockspec/md5-1.2-1.rockspec @@ -0,0 +1,35 @@ +package = "MD5" +version = "1.2-1" +source = { + url = "https://github.com/keplerproject/md5/archive/v1.2.tar.gz", + md5 = "c166f8a983401802a86655a8c733441e", + dir = "md5-1.2", +} +description = { + summary = "Basic cryptographic library", + detailed = [[ + MD5 offers basic cryptographic facilities for Lua 5.X: + a hash (digest) function, a pair crypt/decrypt based on MD5 and CFB, + and a pair crypt/decrypt based on DES with 56-bit keys. + ]], + license = "MIT/X11", + homepage = "http://www.keplerproject.org/md5/", +} +dependencies = { + "lua >= 5.0" +} +build = { + type = "builtin", + modules = { + md5 = "src/md5.lua", + ["md5.core"] = { + sources = { "src/compat-5.2.c", "src/md5.c", "src/md5lib.c", }, + incdirs = { "src/", }, + }, + des56 = { + sources = { "src/compat-5.2.c", "src/des56.c", "src/ldes56.c", }, + incdirs = { "src/", }, + }, + }, + copy_directories = { "doc", "tests", }, +} diff --git a/vendor/md5/src/compat-5.2.c b/vendor/md5/src/compat-5.2.c new file mode 100644 index 00000000..ce57660b --- /dev/null +++ b/vendor/md5/src/compat-5.2.c @@ -0,0 +1,21 @@ +#include "lua.h" +#include "lauxlib.h" +#include "compat-5.2.h" + +#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 +/* +** Adapted from Lua 5.2.0 +*/ +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup + 1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ + } + lua_pop(L, nup); /* remove upvalues */ +} +#endif diff --git a/vendor/md5/src/compat-5.2.h b/vendor/md5/src/compat-5.2.h new file mode 100644 index 00000000..c80fd616 --- /dev/null +++ b/vendor/md5/src/compat-5.2.h @@ -0,0 +1,15 @@ +#if !defined LUA_VERSION_NUM +/* Lua 5.0 */ +#define luaL_Reg luaL_reg + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) +#endif + +#if LUA_VERSION_NUM==501 +/* Lua 5.1 */ +#define lua_rawlen lua_objlen +#endif + +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); diff --git a/vendor/md5/src/des56.c b/vendor/md5/src/des56.c new file mode 100755 index 00000000..1fb377b9 --- /dev/null +++ b/vendor/md5/src/des56.c @@ -0,0 +1,548 @@ + +/* + * Fast implementation of the DES, as described in the Federal Register, + * Vol. 40, No. 52, p. 12134, March 17, 1975. + * + * Stuart Levy, Minnesota Supercomputer Center, April 1988. + * Currently (2007) slevy@ncsa.uiuc.edu + * NCSA, University of Illinois Urbana-Champaign + * + * Calling sequence: + * + * typedef unsigned long keysched[32]; + * + * fsetkey(key, keysched) / * Converts a DES key to a "key schedule" * / + * unsigned char key[8]; + * keysched *ks; + * + * fencrypt(block, decrypt, keysched) / * En/decrypts one 64-bit block * / + * unsigned char block[8]; / * data, en/decrypted in place * / + * int decrypt; / * 0=>encrypt, 1=>decrypt * / + * keysched *ks; / * key schedule, as set by fsetkey * / + * + * Key and data block representation: + * The 56-bit key (bits 1..64 including "parity" bits 8, 16, 24, ..., 64) + * and the 64-bit data block (bits 1..64) + * are each stored in arrays of 8 bytes. + * Following the NBS numbering, the MSB has the bit number 1, so + * key[0] = 128*bit1 + 64*bit2 + ... + 1*bit8, ... through + * key[7] = 128*bit57 + 64*bit58 + ... + 1*bit64. + * In the key, "parity" bits are not checked; their values are ignored. + * +*/ + +/* +=============================================================================== +License + +des56.c is licensed under the terms of the MIT license reproduced below. +This means that des56.c is free software and can be used for both academic +and commercial purposes at absolutely no cost. +=============================================================================== +Copyright (C) 1988 Stuart Levy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + + +#include "des56.h" + + +/* + * Key schedule generation. + * We begin by pointlessly permuting the 56 useful key bits into + * two groups of 28 bits called C and D. + * bK_C and bK_D are indexed by C and D bit numbers, respectively, + * and give the key bit number (1..64) which should initialize that C/D bit. + * This is the "permuted choice 1" table. + */ + +static tiny bK_C[28] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, +}; +static tiny bK_D[28] = { + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4, +}; + +/* + * For speed, we invert these, building tables to map groups of + * key bits into the corresponding C and D bits. + * We represent C and D each as 28 contiguous bits right-justified in a + * word, padded on the left with zeros. + * If key byte `i' is said to contain bits Ki,0 (MSB) Ki,1 ... Ki,7 (LSB) + * then + * wC_K4[i][Ki,0 Ki,1 Ki,2 Ki,3] gives the C bits for Ki,0..3, + * wD_K4[i][Ki,0 Ki,1 Ki,2 Ki,3] the corresponding D bits, + * wC_K3[i][Ki,4 Ki,5 Ki,6] the C bits for Ki,4..6, + * and wD_K3[i][Ki,4 Ki,5 Ki,6] the D bits for Ki,4..6. + * Ki,7 is ignored since it is the nominal parity bit. + * We could just use a single table for [i][Ki,0 .. Ki,6] but that + * would take a lot of storage for such a rarely-used function. + */ + +static word32 wC_K4[8][16], wC_K3[8][8]; +static word32 wD_K4[8][16], wD_K3[8][8]; + +/* + * Successive Ci and Di for the sixteen steps in the key schedule are + * created by independent 28-bit left circular shifts on C and D. + * The shift count varies with the step number. + */ +static tiny preshift[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +/* + * Each step in the key schedule is generated by selecting 48 bits + * (8 groups of 6 bits) from the appropriately shifted Ci and Di. + * bCD_KS, indexed by the key schedule bit number, gives the bit number + * in CD (CD1 = MSB of C, CD28 = LSB of C, CD29 = MSB of D, CD56 = LSB of D) + * which determines that bit of the key schedule. + * Note that only C bits (1..28) appear in the first (upper) 24 bits of + * the key schedule, and D bits (29..56) in the second (lower) 24 bits. + * This is the "permuted-choice-2" table. + */ + +static tiny bCD_KS[48] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32, +}; + +/* + * We invert bCD_KS into a pair of tables which map groups of 4 + * C or D bits into corresponding key schedule bits. + * We represent each step of the key schedule as 8 groups of 8 bits, + * with the 6 real bits right-justified in each 8-bit group. + * hKS_C4[i][C4i+1 .. C4i+4] gives the bits in the high order (first four) + * key schedule "bytes" which correspond to C bits 4i+1 .. 4i+4. + * lKS_D4[i][D4i+1 .. D4i+4] gives the appropriate bits in the latter (last 4) + * key schedule bytes, from the corresponding D bits. + */ + +static word32 hKS_C4[7][16]; +static word32 lKS_D4[7][16]; + +/* + * Encryption/decryption. + * Before beginning, and after ending, we perform another useless permutation + * on the bits in the data block. + * + * The initial permutation and its inverse, final permutation + * are too simple to need a table for. If we break the input I1 .. I64 into + * 8-bit chunks I0,0 I0,1 ... I0,7 I1,0 I1,1 ... I7,7 + * then the initial permutation sets LR as follows: + * L = I7,1 I6,1 I5,1 ... I0,1 I7,3 I6,3 ... I0,3 I7,5 ... I0,5 I7,7 ... I0,7 + * and + * R = I7,0 I6,0 I5,0 ... I0,0 I7,2 I6,2 ... I0,2 I7,4 ... I0,4 I7,6 ... I0,6 + * + * If we number the bits in the final LR similarly, + * L = L0,0 L0,1 ... L3,7 R = R0,0 R0,1 ... R3,7 + * then the output is + * O = R0,7 L0,7 R1,7 L1,7 ... R3,7 L3,7 R0,6 L0,6 ... L3,6 R0,5 ... R3,0 L3,0 + * + * To speed I => LR shuffling we use an array of 32-bit values indexed by + * 8-bit input bytes. + * wL_I8[ 0 I0,1 0 I0,3 0 I0,5 0 I0,7 ] = the corresponding L bits. + * Other R and L bits are derived from wL_I8 by shifting. + * + * To speed LR => O shuffling, an array of 32-bit values indexed by 4-bit lumps: + * wO_L4[ L0,4 L0,5 L0,6 L0,7 ] = the corresponding high-order 32 O bits. + */ + +static word32 wL_I8[0x55 + 1]; +static word32 wO_L4[16]; + +/* + * Core of encryption/decryption. + * In each key schedule stage, we: + * take 8 overlapping groups of 6 bits each from R + * (the NBS tabulates the bit selections in the E table, + * but it's so simple we just use shifting to get the right bits) + * XOR each group with the corresponding bits from the key schedule + * Use the resulting 6 bits as an index into the appropriate S table + * (there are 8 such tables, one per group of 6 bits) + * Each S entry yields 4 bits. + * The 8 groups of 4 bits are catenated into a 32-bit value. + * Those 32 bits are permuted according to the P table. + * Finally the permuted 32-bit value is XORed with L and becomes + * the R value for the next stage, while the previous R becomes the new L. + * + * Here, we merge the P permutation with the S tables by making the + * S entries be 32-bit masks, already suitably permuted. + * Also, the bits in each six-bit group must be permuted before use as + * an index into the NBS-tabulated S tables. + * We rearrange entries in wPS so that natural bit order can be used. + */ + +static word32 wPS[8][64]; + +static tiny P[32] = { + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25, +}; + +static tiny S[8][64] = { + { + 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, + }, + + { + 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, + }, + + { + 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, + }, + + { + 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, + }, + + { + 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, + }, + + { + 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, + }, + + { + 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, + }, + + { + 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, + }, +}; + +static void buildtables( void ) +{ + register int i, j; + register word32 v; + word32 wC_K[64], wD_K[64]; + word32 hKS_C[28], lKS_D[28]; + int Smap[64]; + word32 wP[32]; + +#if USG +# define ZERO(array) memset((char *)(array), '\0', sizeof(array)) +#else +# if BSD +# define ZERO(array) bzero((char *)(array), sizeof(array)) +# else +# define ZERO(array) { register word32 *p = (word32 *)(array); \ + i = sizeof(array) / sizeof(*p); \ + do { *p++ = 0; } while(--i > 0); \ + } +# endif +#endif + + + /* Invert permuted-choice-1 (key => C,D) */ + + ZERO(wC_K); + ZERO(wD_K); + v = 1; + for(j = 28; --j >= 0; ) { + wC_K[ bK_C[j] - 1 ] = wD_K[ bK_D[j] - 1 ] = v; + v += v; /* (i.e. v <<= 1) */ + } + + for(i = 0; i < 64; i++) { + int t = 8 >> (i & 3); + for(j = 0; j < 16; j++) { + if(j & t) { + wC_K4[i >> 3][j] |= wC_K[i]; + wD_K4[i >> 3][j] |= wD_K[i]; + if(j < 8) { + wC_K3[i >> 3][j] |= wC_K[i + 3]; + wD_K3[i >> 3][j] |= wD_K[i + 3]; + } + } + } + /* Generate the sequence 0,1,2,3, 8,9,10,11, ..., 56,57,58,59. */ + if(t == 1) i += 4; + } + + /* Invert permuted-choice-2 */ + + ZERO(hKS_C); + ZERO(lKS_D); + v = 1; + for(i = 24; (i -= 6) >= 0; ) { + j = i+5; + do { + hKS_C[ bCD_KS[j] - 1 ] = lKS_D[ bCD_KS[j+24] - 28 - 1 ] = v; + v += v; /* Like v <<= 1 but may be faster */ + } while(--j >= i); + v <<= 2; /* Keep byte aligned */ + } + + for(i = 0; i < 28; i++) { + v = 8 >> (i & 3); + for(j = 0; j < 16; j++) { + if(j & v) { + hKS_C4[i >> 2][j] |= hKS_C[i]; + lKS_D4[i >> 2][j] |= lKS_D[i]; + } + } + } + + /* Initial permutation */ + + for(i = 0; i <= 0x55; i++) { + v = 0; + if(i & 64) v = (word32) 1 << 24; + if(i & 16) v |= (word32) 1 << 16; + if(i & 4) v |= (word32) 1 << 8; + if(i & 1) v |= 1; + wL_I8[i] = v; + } + + /* Final permutation */ + + for(i = 0; i < 16; i++) { + v = 0; + if(i & 1) v = (word32) 1 << 24; + if(i & 2) v |= (word32) 1 << 16; + if(i & 4) v |= (word32) 1 << 8; + if(i & 8) v |= (word32) 1; + wO_L4[i] = v; + } + + /* Funny bit rearrangement on second index into S tables */ + + for(i = 0; i < 64; i++) { + Smap[i] = (i & 0x20) | (i & 1) << 4 | (i & 0x1e) >> 1; + } + + /* Invert permutation P into mask indexed by R bit number */ + + v = 1; + for(i = 32; --i >= 0; ) { + wP[ P[i] - 1 ] = v; + v += v; + } + + /* Build bit-mask versions of S tables, indexed in natural bit order */ + + for(i = 0; i < 8; i++) { + for(j = 0; j < 64; j++) { + int k, t; + + t = S[i][ Smap[j] ]; + for(k = 0; k < 4; k++) { + if(t & 8) + wPS[i][j] |= wP[4*i + k]; + t += t; + } + } + } +} + + +void fsetkey(char key[8], keysched *ks) +{ + register int i; + register word32 C, D; + static int built = 0; + + if(!built) { + buildtables(); + built = 1; + } + + C = D = 0; + for(i = 0; i < 8; i++) { + register int v; + + v = key[i] >> 1; /* Discard "parity" bit */ + C |= wC_K4[i][(v>>3) & 15] | wC_K3[i][v & 7]; + D |= wD_K4[i][(v>>3) & 15] | wD_K3[i][v & 7]; + } + + /* + * C and D now hold the suitably right-justified + * 28 permuted key bits each. + */ + for(i = 0; i < 16; i++) { +#ifdef CRAY +#define choice2(x, v) x[6][v&15] | x[5][(v>>4)&15] | x[4][(v>>8)&15] | \ + x[3][(v>>12)&15] | x[2][(v>>16)&15] | x[1][(v>>20)&15] | \ + x[0][(v>>24)&15] +#else + register word32 *ap; + +# define choice2(x, v) ( \ + ap = &(x)[0][0], \ + ap[16*6 + (v&15)] | \ + ap[16*5 + ((v>>4)&15)] | ap[16*4 + ((v>>8)&15)] | \ + ap[16*3 + ((v>>12)&15)] | ap[16*2 + ((v>>16)&15)] | \ + ap[16*1 + ((v>>20)&15)] | ap[16*0 + ((v>>24)&15)] ) +#endif + + + /* 28-bit left circular shift */ + C <<= preshift[i]; + C = ((C >> 28) & 3) | (C & (((word32)1<<28) - 1)); + ks->KS[i].h = choice2(hKS_C4, C); + + D <<= preshift[i]; + D = ((D >> 28) & 3) | (D & (((word32)1<<28) - 1)); + ks->KS[i].l = choice2(lKS_D4, D); + } +} + +void +fencrypt(char block[8], int decrypt, keysched *ks) +{ + int i; + register word32 L, R; + register struct keystage *ksp; + register word32 *ap; + + /* Initial permutation */ + + L = R = 0; + i = 7; + ap = wL_I8; + do { + register int v; + + v = block[i]; /* Could optimize according to ENDIAN */ + L = ap[v & 0x55] | (L << 1); + R = ap[(v >> 1) & 0x55] | (R << 1); + } while(--i >= 0); + + if(decrypt) { + ksp = &ks->KS[15]; + } else { + ksp = &ks->KS[0]; + } + +#ifdef CRAY +# define PS(i,j) wPS[i][j] +#else +# define PS(i,j) ap[64*(i) + (j)] + ap = &wPS[0][0]; +#endif + + i = 16; + do { + register word32 k, tR; + + tR = (R >> 15) | (R << 17); + + k = ksp->h; + L ^= PS(0, ((tR >> 12) ^ (k >> 24)) & 63) + | PS(1, ((tR >> 8) ^ (k >> 16)) & 63) + | PS(2, ((tR >> 4) ^ (k >> 8)) & 63) + | PS(3, (tR ^ k) & 63); + + k = ksp->l; + L ^= PS(4, ((R >> 11) ^ (k >> 24)) & 63) + | PS(5, ((R >> 7) ^ (k >> 16)) & 63) + | PS(6, ((R >> 3) ^ (k >> 8)) & 63) + | PS(7, ((tR >> 16) ^ k) & 63); + + tR = L; + L = R; + R = tR; + + + if(decrypt) + ksp--; + else + ksp++; + } while(--i > 0); + { + register word32 t; + +#ifdef CRAY +# define FP(k) (wO_L4[ (L >> (k)) & 15 ] << 1 | wO_L4[ (R >> (k)) & 15 ]) +#else +# define FP(k) (ap[ (L >> (k)) & 15 ] << 1 | ap[ (R >> (k)) & 15 ]) + + ap = wO_L4; +#endif + + t = FP(0) | (FP(8) | (FP(16) | (FP(24) << 2)) << 2) << 2; + R = FP(4) | (FP(12) | (FP(20) | (FP(28) << 2)) << 2) << 2; + L = t; + } + { + register word32 t; + register char *bp; + + bp = &block[7]; + t = R; + *bp = t & 255; + *--bp = (t >>= 8) & 255; + *--bp = (t >>= 8) & 255; + *--bp = (t >> 8) & 255; + t = L; + *--bp = t & 255; + *--bp = (t >>= 8) & 255; + *--bp = (t >>= 8) & 255; + *--bp = (t >> 8) & 255; + } +} + diff --git a/vendor/md5/src/des56.def b/vendor/md5/src/des56.def new file mode 100644 index 00000000..c0493a73 --- /dev/null +++ b/vendor/md5/src/des56.def @@ -0,0 +1,5 @@ +LIBRARY des56.dll +DESCRIPTION "DES56" +VERSION 1.3 +EXPORTS +luaopen_des56 diff --git a/vendor/md5/src/des56.h b/vendor/md5/src/des56.h new file mode 100755 index 00000000..88e5f137 --- /dev/null +++ b/vendor/md5/src/des56.h @@ -0,0 +1,77 @@ +#ifndef DES56_H +#define DES56_H 1 +/* + * Fast implementation of the DES, as described in the Federal Register, + * Vol. 40, No. 52, p. 12134, March 17, 1975. + * + * Stuart Levy, Minnesota Supercomputer Center, April 1988. + * Currently (2007) slevy@ncsa.uiuc.edu + * NCSA, University of Illinois Urbana-Champaign + * + * Calling sequence: + * + * typedef unsigned long keysched[32]; + * + * fsetkey(key, keysched) / * Converts a DES key to a "key schedule" * / + * unsigned char key[8]; + * keysched *ks; + * + * fencrypt(block, decrypt, keysched) / * En/decrypts one 64-bit block * / + * unsigned char block[8]; / * data, en/decrypted in place * / + * int decrypt; / * 0=>encrypt, 1=>decrypt * / + * keysched *ks; / * key schedule, as set by fsetkey * / + * + * Key and data block representation: + * The 56-bit key (bits 1..64 including "parity" bits 8, 16, 24, ..., 64) + * and the 64-bit data block (bits 1..64) + * are each stored in arrays of 8 bytes. + * Following the NBS numbering, the MSB has the bit number 1, so + * key[0] = 128*bit1 + 64*bit2 + ... + 1*bit8, ... through + * key[7] = 128*bit57 + 64*bit58 + ... + 1*bit64. + * In the key, "parity" bits are not checked; their values are ignored. + * +*/ + +/* +=============================================================================== +License + +des56.c is licensed under the terms of the MIT license reproduced below. +This means that des56.c is free software and can be used for both academic +and commercial purposes at absolutely no cost. +=============================================================================== +Copyright (C) 1988 Stuart Levy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +typedef unsigned long word32; +typedef unsigned char tiny; + +typedef struct keysched { + struct keystage { + word32 h, l; + } KS[16]; +} keysched; + +extern void fsetkey(char key[8], keysched *ks); + +extern void fencrypt(char block[8], int decrypt, keysched *ks); + +#endif /*DES56_H*/ diff --git a/vendor/md5/src/ldes56.c b/vendor/md5/src/ldes56.c new file mode 100644 index 00000000..8c29ec1d --- /dev/null +++ b/vendor/md5/src/ldes56.c @@ -0,0 +1,152 @@ +#include +#include + +#include "des56.h" + +#include "lua.h" +#include "lauxlib.h" + +#include "compat-5.2.h" +#include "ldes56.h" + +static int des56_decrypt( lua_State *L ) +{ + char* decypheredText; + keysched KS; + int rel_index, abs_index; + size_t cypherlen; + const char *cypheredText = + luaL_checklstring( L, 1, &cypherlen ); + const char *key = luaL_optstring( L, 2, NULL ); + int padinfo; + + padinfo = cypheredText[cypherlen-1]; + cypherlen--; + + /* Aloca array */ + decypheredText = + (char *) malloc( (cypherlen+1) * sizeof(char)); + if(decypheredText == NULL) { + lua_pushstring(L, "Error decrypting file. Not enough memory."); + lua_error(L); + } + + /* Inicia decifragem */ + if (key && strlen(key) >= 8) + { + char k[8]; + int i; + + for (i=0; i<8; i++) + k[i] = (unsigned char)key[i]; + fsetkey(k, &KS); + } else { + lua_pushstring(L, "Error decrypting file. Invalid key."); + lua_error(L); + } + + rel_index = 0; + abs_index = 0; + + while (abs_index < (int) cypherlen) + { + decypheredText[abs_index] = cypheredText[abs_index]; + abs_index++; + rel_index++; + if( rel_index == 8 ) + { + rel_index = 0; + fencrypt(&(decypheredText[abs_index - 8]), 1, &KS); + } + } + decypheredText[abs_index] = 0; + + lua_pushlstring(L, decypheredText, (abs_index-padinfo)); + free( decypheredText ); + return 1; +} + +static int des56_crypt( lua_State *L ) +{ + char *cypheredText; + keysched KS; + int rel_index, pad, abs_index; + size_t plainlen; + const char *plainText = luaL_checklstring( L, 1, &plainlen ); + const char *key = luaL_optstring( L, 2, NULL ); + + cypheredText = (char *) malloc( (plainlen+8) * sizeof(char)); + if(cypheredText == NULL) { + lua_pushstring(L, "Error encrypting file. Not enough memory."); + lua_error(L); + } + + if (key && strlen(key) >= 8) + { + char k[8]; + int i; + + for (i=0; i<8; i++) + k[i] = (unsigned char)key[i]; + fsetkey(k, &KS); + } else { + lua_pushstring(L, "Error encrypting file. Invalid key."); + lua_error(L); + } + + rel_index = 0; + abs_index = 0; + while (abs_index < (int) plainlen) { + cypheredText[abs_index] = plainText[abs_index]; + abs_index++; + rel_index++; + if( rel_index == 8 ) { + rel_index = 0; + fencrypt(&(cypheredText[abs_index - 8]), 0, &KS); + } + } + + pad = 0; + if(rel_index != 0) { /* Pads remaining bytes with zeroes */ + while(rel_index < 8) + { + pad++; + cypheredText[abs_index++] = 0; + rel_index++; + } + fencrypt(&(cypheredText[abs_index - 8]), 0, &KS); + } + cypheredText[abs_index] = pad; + + lua_pushlstring( L, cypheredText, abs_index+1 ); + free( cypheredText ); + return 1; +} + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2007-2019 PUC-Rio"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "DES 56 cryptographic facilities for Lua"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "DES56 1.3"); + lua_settable (L, -3); +} + +static const struct luaL_Reg des56lib[] = { + {"crypt", des56_crypt}, + {"decrypt", des56_decrypt}, + {NULL, NULL}, +}; + +int luaopen_des56 (lua_State *L) { + lua_newtable(L); + luaL_setfuncs(L, des56lib, 0); + set_info (L); + return 1; +} diff --git a/vendor/md5/src/ldes56.h b/vendor/md5/src/ldes56.h new file mode 100755 index 00000000..8c2e86a8 --- /dev/null +++ b/vendor/md5/src/ldes56.h @@ -0,0 +1 @@ +int luaopen_des56 (lua_State *L); diff --git a/vendor/md5/src/md5.c b/vendor/md5/src/md5.c new file mode 100755 index 00000000..f35c8e14 --- /dev/null +++ b/vendor/md5/src/md5.c @@ -0,0 +1,262 @@ +/** +* $Id: md5.c,v 1.2 2008/03/24 20:59:12 mascarenhas Exp $ +* Hash function MD5 +* @author Marcela Ozorio Suarez, Roberto I. +*/ + + +#include +#include +#include +#include + +#include "md5.h" + + +#define WORD 32 +#define MASK 0xFFFFFFFF + + +/** +* md5 hash function. +* @param message: aribtary string. +* @param len: message length. +* @param output: buffer to receive the hash value. Its size must be +* (at least) HASHSIZE. +*/ +void md5 (const char *message, size_t len, char output[HASHSIZE]); + +/** +* init a new md5 calculate context +* @param m a uninitialized md5_t type context +*/ +void md5_init (md5_t *m); + +/** +* update message to md5 context +* @param m a initialized md5_t type context +* @param message aribtary string +* @param len message length +* @return true if update completed, means len is not the multiples of 64. +* false if you can continue update. +*/ +int md5_update (md5_t *m, const char *message, size_t len); + +/** + * finish md5 calculate. + * @param m a md5_type context which previous md5_update on it return true. + * @param output buffer to receive the hash value. its size must be + * (at least) HASHSIZE. + */ +void md5_finish (md5_t *m, char output[HASHSIZE]); + +/* +** Realiza a rotacao no sentido horario dos bits da variavel 'D' do tipo WORD32. +** Os bits sao deslocados de 'num' posicoes +*/ +#define rotate(D, num) (D<>(WORD-num)) + +/*Macros que definem operacoes relizadas pelo algoritmo md5 */ +#define F(x, y, z) (((x) & (y)) | ((~(x)) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~(z)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~(z)))) + + +/*vetor de numeros utilizados pelo algoritmo md5 para embaralhar bits */ +static const WORD32 T[64]={ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +}; + + +static void word32tobytes (const WORD32 *input, char *output) { + int j = 0; + while (j<4*4) { + WORD32 v = *input++; + output[j++] = (char)(v & 0xff); v >>= 8; + output[j++] = (char)(v & 0xff); v >>= 8; + output[j++] = (char)(v & 0xff); v >>= 8; + output[j++] = (char)(v & 0xff); + } +} + + +static void inic_digest(WORD32 *d) { + d[0] = 0x67452301; + d[1] = 0xEFCDAB89; + d[2] = 0x98BADCFE; + d[3] = 0x10325476; +} + + +/*funcao que implemeta os quatro passos principais do algoritmo MD5 */ +static void digest(const WORD32 *m, WORD32 *d) { + int j; + /*MD5 PASSO1 */ + for (j=0; j<4*4; j+=4) { + d[0] = d[0]+ F(d[1], d[2], d[3])+ m[j] + T[j]; d[0]=rotate(d[0], 7); + d[0]+=d[1]; + d[3] = d[3]+ F(d[0], d[1], d[2])+ m[(j)+1] + T[j+1]; d[3]=rotate(d[3], 12); + d[3]+=d[0]; + d[2] = d[2]+ F(d[3], d[0], d[1])+ m[(j)+2] + T[j+2]; d[2]=rotate(d[2], 17); + d[2]+=d[3]; + d[1] = d[1]+ F(d[2], d[3], d[0])+ m[(j)+3] + T[j+3]; d[1]=rotate(d[1], 22); + d[1]+=d[2]; + } + /*MD5 PASSO2 */ + for (j=0; j<4*4; j+=4) { + d[0] = d[0]+ G(d[1], d[2], d[3])+ m[(5*j+1)&0x0f] + T[(j-1)+17]; + d[0] = rotate(d[0],5); + d[0]+=d[1]; + d[3] = d[3]+ G(d[0], d[1], d[2])+ m[((5*(j+1)+1)&0x0f)] + T[(j+0)+17]; + d[3] = rotate(d[3], 9); + d[3]+=d[0]; + d[2] = d[2]+ G(d[3], d[0], d[1])+ m[((5*(j+2)+1)&0x0f)] + T[(j+1)+17]; + d[2] = rotate(d[2], 14); + d[2]+=d[3]; + d[1] = d[1]+ G(d[2], d[3], d[0])+ m[((5*(j+3)+1)&0x0f)] + T[(j+2)+17]; + d[1] = rotate(d[1], 20); + d[1]+=d[2]; + } + /*MD5 PASSO3 */ + for (j=0; j<4*4; j+=4) { + d[0] = d[0]+ H(d[1], d[2], d[3])+ m[(3*j+5)&0x0f] + T[(j-1)+33]; + d[0] = rotate(d[0], 4); + d[0]+=d[1]; + d[3] = d[3]+ H(d[0], d[1], d[2])+ m[(3*(j+1)+5)&0x0f] + T[(j+0)+33]; + d[3] = rotate(d[3], 11); + d[3]+=d[0]; + d[2] = d[2]+ H(d[3], d[0], d[1])+ m[(3*(j+2)+5)&0x0f] + T[(j+1)+33]; + d[2] = rotate(d[2], 16); + d[2]+=d[3]; + d[1] = d[1]+ H(d[2], d[3], d[0])+ m[(3*(j+3)+5)&0x0f] + T[(j+2)+33]; + d[1] = rotate(d[1], 23); + d[1]+=d[2]; + } + /*MD5 PASSO4 */ + for (j=0; j<4*4; j+=4) { + d[0] = d[0]+ I(d[1], d[2], d[3])+ m[(7*j)&0x0f] + T[(j-1)+49]; + d[0] = rotate(d[0], 6); + d[0]+=d[1]; + d[3] = d[3]+ I(d[0], d[1], d[2])+ m[(7*(j+1))&0x0f] + T[(j+0)+49]; + d[3] = rotate(d[3], 10); + d[3]+=d[0]; + d[2] = d[2]+ I(d[3], d[0], d[1])+ m[(7*(j+2))&0x0f] + T[(j+1)+49]; + d[2] = rotate(d[2], 15); + d[2]+=d[3]; + d[1] = d[1]+ I(d[2], d[3], d[0])+ m[(7*(j+3))&0x0f] + T[(j+2)+49]; + d[1] = rotate(d[1], 21); + d[1]+=d[2]; + } +} + + +static void bytestoword32 (WORD32 *x, const char *pt) { + int i; + for (i=0; i<16; i++) { + int j=i*4; + x[i] = (((WORD32)(unsigned char)pt[j+3] << 8 | + (WORD32)(unsigned char)pt[j+2]) << 8 | + (WORD32)(unsigned char)pt[j+1]) << 8 | + (WORD32)(unsigned char)pt[j]; + } + +} + + +static void put_length(WORD32 *x, long len) { + /* in bits! */ + x[14] = (WORD32)((len<<3) & MASK); + x[15] = (WORD32)(len>>(32-3) & 0x7); +} + + +/* +** returned status: +* 0 - normal message (full 64 bytes) +* 1 - enough room for 0x80, but not for message length (two 4-byte words) +* 2 - enough room for 0x80 plus message length (at least 9 bytes free) +*/ +static int converte (WORD32 *x, const char *pt, int num, int old_status) { + int new_status = 0; + char buff[64]; + if (num<64) { + memcpy(buff, pt, num); /* to avoid changing original string */ + memset(buff+num, 0, 64-num); + if (old_status == 0) + buff[num] = '\200'; + new_status = 1; + pt = buff; + } + bytestoword32(x, pt); + if (num <= (64 - 9)) + new_status = 2; + return new_status; +} + + +void md5 (const char *message, size_t len, char output[HASHSIZE]) { + WORD32 d[4]; + int status = 0; + long i = 0; + inic_digest(d); + while (status != 2) { + WORD32 d_old[4]; + WORD32 wbuff[16]; + int numbytes = (len-i >= 64) ? 64 : len-i; + /*salva os valores do vetor digest*/ + d_old[0]=d[0]; d_old[1]=d[1]; d_old[2]=d[2]; d_old[3]=d[3]; + status = converte(wbuff, message+i, numbytes, status); + if (status == 2) put_length(wbuff, len); + digest(wbuff, d); + d[0]+=d_old[0]; d[1]+=d_old[1]; d[2]+=d_old[2]; d[3]+=d_old[3]; + i += numbytes; + } + word32tobytes(d, output); +} + +void md5_init(md5_t *m) { + inic_digest(m->d); + m->len = 0; +} + +int md5_update(md5_t *m, const char *message, size_t len) { + WORD32 *d = m->d; + size_t addlen = m->len; + int status = 0, i = 0; + while (status != 2) { + WORD32 d_old[4]; + WORD32 wbuff[16]; + int numbytes = (len-i >= 64) ? 64 : len-i; + if (status != 1 && numbytes == 0 && len != 0) + break; + /*salva os valores do vetor digest*/ + d_old[0]=d[0]; d_old[1]=d[1]; d_old[2]=d[2]; d_old[3]=d[3]; + status = converte(wbuff, message+i, numbytes, status); + if (status == 2) put_length(wbuff, addlen+len); + digest(wbuff, d); + d[0]+=d_old[0]; d[1]+=d_old[1]; d[2]+=d_old[2]; d[3]+=d_old[3]; + i += numbytes; + } + m->len += len; + return status == 2; +} + +void md5_finish (md5_t *m, char output[HASHSIZE]) { + word32tobytes(m->d, output); +} diff --git a/vendor/md5/src/md5.def b/vendor/md5/src/md5.def new file mode 100644 index 00000000..8ab09e76 --- /dev/null +++ b/vendor/md5/src/md5.def @@ -0,0 +1,5 @@ +LIBRARY core.dll +DESCRIPTION "LuaMD5" +VERSION 1.3 +EXPORTS +luaopen_md5_core diff --git a/vendor/md5/src/md5.h b/vendor/md5/src/md5.h new file mode 100755 index 00000000..522ede81 --- /dev/null +++ b/vendor/md5/src/md5.h @@ -0,0 +1,40 @@ +/** +* $Id: md5.h,v 1.2 2006/03/03 15:04:49 tomas Exp $ +* Cryptographic module for Lua. +* @author Roberto Ierusalimschy +*/ + + +#ifndef md5_h +#define md5_h + +#include +#include + + +#define HASHSIZE 16 + +#if __STDC_VERSION__ >= 199901L +#include +typedef uint32_t WORD32; +#else +/* static assert that int equal or greater than 32bit. */ +typedef char static_assert_sizeof_int + [sizeof(unsigned int) >= 4 ? 1 : -1]; +typedef unsigned int WORD32; +#endif + +typedef struct md5_t { + WORD32 d[4]; + size_t len; +} md5_t; + +void md5_init (md5_t *m); +int md5_update (md5_t *m, const char *message, size_t len); +void md5_finish (md5_t *m, char output[HASHSIZE]); +void md5 (const char *message, size_t len, char output[HASHSIZE]); + +LUALIB_API int luaopen_md5_core (lua_State *L); + + +#endif diff --git a/vendor/md5/src/md5.lua b/vendor/md5/src/md5.lua new file mode 100644 index 00000000..4eeda2dd --- /dev/null +++ b/vendor/md5/src/md5.lua @@ -0,0 +1,26 @@ +---------------------------------------------------------------------------- +-- $Id: md5.lua,v 1.4 2006/08/21 19:24:21 carregal Exp $ +---------------------------------------------------------------------------- + +local core +local string = string or require"string" +if string.find(_VERSION, "Lua 5.0") then + local cpath = os.getenv"LUA_CPATH" or "/usr/local/lib/lua/5.0/" + core = loadlib(cpath.."md5/core.so", "luaopen_md5_core")() +else + core = require"md5.core" +end + + +---------------------------------------------------------------------------- +-- @param k String with original message. +-- @return String with the md5 hash value converted to hexadecimal digits + +function core.sumhexa (k) + k = core.sum(k) + return (string.gsub(k, ".", function (c) + return string.format("%02x", string.byte(c)) + end)) +end + +return core diff --git a/vendor/md5/src/md5lib.c b/vendor/md5/src/md5lib.c new file mode 100644 index 00000000..19abc85f --- /dev/null +++ b/vendor/md5/src/md5lib.c @@ -0,0 +1,200 @@ +/** +* $Id: md5lib.c,v 1.10 2008/05/12 20:51:27 carregal Exp $ +* Cryptographic and Hash functions for Lua +* @author Roberto Ierusalimschy +*/ + + +#include +#include +#include + +#include +#include + +#include "md5.h" +#include "compat-5.2.h" + + +/** +* Hash function. Returns a hash for a given string. +* @param message: arbitrary binary string. +* @return A 128-bit hash string. +*/ +static int lmd5 (lua_State *L) { + char buff[16]; + size_t l; + const char *message = luaL_checklstring(L, 1, &l); + md5(message, l, buff); + lua_pushlstring(L, buff, 16L); + return 1; +} + + +/** +* X-Or. Does a bit-a-bit exclusive-or of two strings. +* @param s1: arbitrary binary string. +* @param s2: arbitrary binary string with same length as s1. +* @return a binary string with same length as s1 and s2, +* where each bit is the exclusive-or of the corresponding bits in s1-s2. +*/ +static int ex_or (lua_State *L) { + size_t l1, l2; + const char *s1 = luaL_checklstring(L, 1, &l1); + const char *s2 = luaL_checklstring(L, 2, &l2); + luaL_Buffer b; + luaL_argcheck( L, l1 == l2, 2, "lengths must be equal" ); + luaL_buffinit(L, &b); + while (l1--) luaL_addchar(&b, (*s1++)^(*s2++)); + luaL_pushresult(&b); + return 1; +} + + +static void checkseed (lua_State *L) { + if (lua_isnone(L, 3)) { /* no seed? */ + time_t tm = time(NULL); /* for `random' seed */ + lua_pushlstring(L, (char *)&tm, sizeof(tm)); + } +} + + +#define MAXKEY 256 +#define BLOCKSIZE 16 + + + +static int initblock (lua_State *L, const char *seed, int lseed, char *block) { + size_t lkey; + const char *key = luaL_checklstring(L, 2, &lkey); + if (lkey > MAXKEY) + luaL_error(L, "key too long (> %d)", MAXKEY); + memset(block, 0, BLOCKSIZE); + memcpy(block, seed, lseed); + memcpy(block+BLOCKSIZE, key, lkey); + return (int)lkey+BLOCKSIZE; +} + + +static void codestream (lua_State *L, const char *msg, size_t lmsg, + char *block, int lblock) { + luaL_Buffer b; + luaL_buffinit(L, &b); + while (lmsg > 0) { + char code[BLOCKSIZE]; + int i; + md5(block, lblock, code); + for (i=0; i 0; i++, lmsg--) + code[i] ^= *msg++; + luaL_addlstring(&b, code, i); + memcpy(block, code, i); /* update seed */ + } + luaL_pushresult(&b); +} + + +static void decodestream (lua_State *L, const char *cypher, size_t lcypher, + char *block, int lblock) { + luaL_Buffer b; + luaL_buffinit(L, &b); + while (lcypher > 0) { + char code[BLOCKSIZE]; + int i; + md5(block, lblock, code); /* update seed */ + for (i=0; i 0; i++, lcypher--) + code[i] ^= *cypher++; + luaL_addlstring(&b, code, i); + memcpy(block, cypher-i, i); + } + luaL_pushresult(&b); +} + + +/** +* Encrypts a string. Uses the hash function md5 in CFB (Cipher-feedback +* mode). +* @param message: arbitrary binary string to be encrypted. +* @param key: arbitrary binary string to be used as a key. +* @param [seed]: optional arbitrary binary string to be used as a seed. +* if no seed is provided, the function uses the result of +* time() as a seed. +* @return The cyphertext (as a binary string). +*/ +static int crypt (lua_State *L) { + size_t lmsg; + const char *msg = luaL_checklstring(L, 1, &lmsg); + size_t lseed; + const char *seed; + int lblock; + char block[BLOCKSIZE+MAXKEY]; + checkseed(L); + seed = luaL_checklstring(L, 3, &lseed); + if (lseed > BLOCKSIZE) + luaL_error(L, "seed too long (> %d)", BLOCKSIZE); + /* put seed and seed length at the beginning of result */ + block[0] = (char)lseed; + memcpy(block+1, seed, lseed); + lua_pushlstring(L, block, lseed+1); /* to concat with result */ + lblock = initblock(L, seed, lseed, block); + codestream(L, msg, lmsg, block, lblock); + lua_concat(L, 2); + return 1; +} + + +/** +* Decrypts a string. For any message, key, and seed, we have that +* decrypt(crypt(msg, key, seed), key) == msg. +* @param cyphertext: message to be decrypted (this must be the result of + a previous call to crypt. +* @param key: arbitrary binary string to be used as a key. +* @return The plaintext. +*/ +static int decrypt (lua_State *L) { + size_t lcyphertext; + const char *cyphertext = luaL_checklstring(L, 1, &lcyphertext); + size_t lseed = cyphertext[0]; + const char *seed = cyphertext+1; + int lblock; + char block[BLOCKSIZE+MAXKEY]; + luaL_argcheck(L, lcyphertext >= lseed+1 && lseed <= BLOCKSIZE, 1, + "invalid cyphered string"); + cyphertext += lseed+1; + lcyphertext -= lseed+1; + lblock = initblock(L, seed, lseed, block); + decodestream(L, cyphertext, lcyphertext, block, lblock); + return 1; +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2019 PUC-Rio"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "Checksum facilities for Lua"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "MD5 1.3"); + lua_settable (L, -3); +} + + +static struct luaL_Reg md5lib[] = { + {"sum", lmd5}, + {"exor", ex_or}, + {"crypt", crypt}, + {"decrypt", decrypt}, + {NULL, NULL} +}; + + +int luaopen_md5_core (lua_State *L) { + lua_newtable(L); + luaL_setfuncs(L, md5lib, 0); + set_info (L); + return 1; +} -- cgit v1.2.3-55-g6feb