From d842bf4183e31b88df2b2ed2dc18c3e14cfb0fea Mon Sep 17 00:00:00 2001 From: TurtleP Date: Sun, 11 Aug 2024 13:55:59 -0400 Subject: [PATCH] luasocket + add callback check for keyboards --- CMakeLists.txt | 49 +- include/common/Reference.hpp | 82 + include/common/luax.hpp | 2 + include/modules/keyboard/Keyboard.tcc | 52 +- libraries/luasocket/libluasocket/auxiliar.c | 154 ++ libraries/luasocket/libluasocket/auxiliar.h | 54 + libraries/luasocket/libluasocket/buffer.c | 273 ++ libraries/luasocket/libluasocket/buffer.h | 52 + libraries/luasocket/libluasocket/compat.c | 39 + libraries/luasocket/libluasocket/compat.h | 22 + libraries/luasocket/libluasocket/except.c | 129 + libraries/luasocket/libluasocket/except.h | 46 + libraries/luasocket/libluasocket/ftp.lua | 332 +++ libraries/luasocket/libluasocket/headers.lua | 107 + libraries/luasocket/libluasocket/http.lua | 427 +++ libraries/luasocket/libluasocket/inet.c | 537 ++++ libraries/luasocket/libluasocket/inet.h | 56 + libraries/luasocket/libluasocket/io.c | 28 + libraries/luasocket/libluasocket/io.h | 70 + libraries/luasocket/libluasocket/ltn12.lua | 321 +++ libraries/luasocket/libluasocket/luasocket.c | 104 + libraries/luasocket/libluasocket/luasocket.h | 36 + libraries/luasocket/libluasocket/makefile | 461 ++++ libraries/luasocket/libluasocket/mbox.lua | 96 + libraries/luasocket/libluasocket/mime.c | 852 ++++++ libraries/luasocket/libluasocket/mime.h | 22 + libraries/luasocket/libluasocket/mime.lua | 84 + libraries/luasocket/libluasocket/options.c | 480 ++++ libraries/luasocket/libluasocket/options.h | 113 + libraries/luasocket/libluasocket/pierror.h | 28 + libraries/luasocket/libluasocket/select.c | 214 ++ libraries/luasocket/libluasocket/select.h | 23 + libraries/luasocket/libluasocket/serial.c | 171 ++ libraries/luasocket/libluasocket/smtp.lua | 259 ++ libraries/luasocket/libluasocket/socket.h | 75 + libraries/luasocket/libluasocket/socket.lua | 152 ++ libraries/luasocket/libluasocket/tcp.c | 480 ++++ libraries/luasocket/libluasocket/tcp.h | 43 + libraries/luasocket/libluasocket/timeout.c | 226 ++ libraries/luasocket/libluasocket/timeout.h | 40 + libraries/luasocket/libluasocket/tp.lua | 137 + libraries/luasocket/libluasocket/udp.c | 488 ++++ libraries/luasocket/libluasocket/udp.h | 39 + libraries/luasocket/libluasocket/unix.c | 69 + libraries/luasocket/libluasocket/unix.h | 26 + libraries/luasocket/libluasocket/unixdgram.c | 405 +++ libraries/luasocket/libluasocket/unixdgram.h | 28 + libraries/luasocket/libluasocket/unixstream.c | 355 +++ libraries/luasocket/libluasocket/unixstream.h | 29 + libraries/luasocket/libluasocket/url.lua | 333 +++ libraries/luasocket/libluasocket/usocket.c | 454 ++++ libraries/luasocket/libluasocket/usocket.h | 59 + libraries/luasocket/libluasocket/wsocket.c | 434 +++ libraries/luasocket/libluasocket/wsocket.h | 33 + libraries/luasocket/luasocket.cpp | 130 + libraries/luasocket/luasocket.hpp | 41 + platform/cafe/libraries/luasocket.patch | 2109 +++++++++++++++ .../include/modules/font/BCFNTRasterizer.hpp | 4 + platform/ctr/include/modules/font/Font.hpp | 13 + .../ctr/include/modules/keyboard/Keyboard.hpp | 6 + platform/ctr/libraries/luasocket.patch | 2397 +++++++++++++++++ .../source/modules/font/BCFNTRasterizer.cpp | 38 +- platform/ctr/source/modules/font/Font.cpp | 7 +- .../ctr/source/modules/keyboard/Keyboard.cpp | 12 + platform/hac/libraries/luasocket.patch | 2133 +++++++++++++++ source/common/Reference.cpp | 78 + source/common/luax.cpp | 13 + source/modules/font/wrap_Font.cpp | 44 + source/modules/graphics/Graphics.cpp | 4 +- source/modules/graphics/wrap_Graphics.cpp | 4 +- source/modules/graphics/wrap_TextBatch.cpp | 2 +- source/modules/keyboard/wrap_Keyboard.cpp | 58 +- source/modules/love/love.cpp | 7 +- source/modules/love/scripts/callbacks.lua | 2 +- 74 files changed, 16753 insertions(+), 29 deletions(-) create mode 100644 include/common/Reference.hpp create mode 100644 libraries/luasocket/libluasocket/auxiliar.c create mode 100644 libraries/luasocket/libluasocket/auxiliar.h create mode 100644 libraries/luasocket/libluasocket/buffer.c create mode 100644 libraries/luasocket/libluasocket/buffer.h create mode 100644 libraries/luasocket/libluasocket/compat.c create mode 100644 libraries/luasocket/libluasocket/compat.h create mode 100644 libraries/luasocket/libluasocket/except.c create mode 100644 libraries/luasocket/libluasocket/except.h create mode 100644 libraries/luasocket/libluasocket/ftp.lua create mode 100644 libraries/luasocket/libluasocket/headers.lua create mode 100644 libraries/luasocket/libluasocket/http.lua create mode 100644 libraries/luasocket/libluasocket/inet.c create mode 100644 libraries/luasocket/libluasocket/inet.h create mode 100644 libraries/luasocket/libluasocket/io.c create mode 100644 libraries/luasocket/libluasocket/io.h create mode 100644 libraries/luasocket/libluasocket/ltn12.lua create mode 100644 libraries/luasocket/libluasocket/luasocket.c create mode 100644 libraries/luasocket/libluasocket/luasocket.h create mode 100644 libraries/luasocket/libluasocket/makefile create mode 100644 libraries/luasocket/libluasocket/mbox.lua create mode 100644 libraries/luasocket/libluasocket/mime.c create mode 100644 libraries/luasocket/libluasocket/mime.h create mode 100644 libraries/luasocket/libluasocket/mime.lua create mode 100644 libraries/luasocket/libluasocket/options.c create mode 100644 libraries/luasocket/libluasocket/options.h create mode 100644 libraries/luasocket/libluasocket/pierror.h create mode 100644 libraries/luasocket/libluasocket/select.c create mode 100644 libraries/luasocket/libluasocket/select.h create mode 100644 libraries/luasocket/libluasocket/serial.c create mode 100644 libraries/luasocket/libluasocket/smtp.lua create mode 100644 libraries/luasocket/libluasocket/socket.h create mode 100644 libraries/luasocket/libluasocket/socket.lua create mode 100644 libraries/luasocket/libluasocket/tcp.c create mode 100644 libraries/luasocket/libluasocket/tcp.h create mode 100644 libraries/luasocket/libluasocket/timeout.c create mode 100644 libraries/luasocket/libluasocket/timeout.h create mode 100644 libraries/luasocket/libluasocket/tp.lua create mode 100644 libraries/luasocket/libluasocket/udp.c create mode 100644 libraries/luasocket/libluasocket/udp.h create mode 100644 libraries/luasocket/libluasocket/unix.c create mode 100644 libraries/luasocket/libluasocket/unix.h create mode 100644 libraries/luasocket/libluasocket/unixdgram.c create mode 100644 libraries/luasocket/libluasocket/unixdgram.h create mode 100644 libraries/luasocket/libluasocket/unixstream.c create mode 100644 libraries/luasocket/libluasocket/unixstream.h create mode 100644 libraries/luasocket/libluasocket/url.lua create mode 100644 libraries/luasocket/libluasocket/usocket.c create mode 100644 libraries/luasocket/libluasocket/usocket.h create mode 100644 libraries/luasocket/libluasocket/wsocket.c create mode 100644 libraries/luasocket/libluasocket/wsocket.h create mode 100644 libraries/luasocket/luasocket.cpp create mode 100644 libraries/luasocket/luasocket.hpp create mode 100644 platform/cafe/libraries/luasocket.patch create mode 100644 platform/ctr/libraries/luasocket.patch create mode 100644 platform/hac/libraries/luasocket.patch create mode 100644 source/common/Reference.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eafd6ce3..ace4e9b34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE set(APP_TITLE "LÖVE Potion") set(APP_AUTHOR "LÖVEBrew Team") +file(COPY libraries/luasocket DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(APP_TITLE "LÖVE Potion (${COMMIT_HASH})") target_compile_definitions(${PROJECT_NAME} PRIVATE __DEBUG__=1) @@ -79,6 +81,8 @@ if(NINTENDO_3DS) target_sources(${PROJECT_NAME} PRIVATE source/modules/image/magpie/T3XHandler.cpp ) + + execute_process(COMMAND patch -d ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket -N -i ${PROJECT_SOURCE_DIR}/platform/ctr/libraries/luasocket.patch) endif() if (NINTENDO_SWITCH) @@ -212,6 +216,47 @@ add_library(lua53 ) target_link_libraries(lua53 PRIVATE PkgConfig::lua51) +add_library(luasocket + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/luasocket.hpp + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/luasocket.cpp + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/auxiliar.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/auxiliar.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/buffer.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/buffer.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/compat.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/compat.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/except.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/except.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/inet.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/inet.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/io.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/io.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/ltn12.lua + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/luasocket.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/luasocket.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/mime.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/mime.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/options.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/options.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/pierror.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/select.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/select.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/smtp.lua + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/socket.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/tcp.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/tcp.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/timeout.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/timeout.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/udp.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/udp.h + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/usocket.c + ${CMAKE_CURRENT_BINARY_DIR}/luasocket/libluasocket/usocket.h +) +target_link_libraries(luasocket PRIVATE PkgConfig::lua51) +target_include_directories(luasocket PRIVATE + libraries/lua53 +) + FetchContent_Declare(lua-https GIT_REPOSITORY https://github.com/love2d/lua-https GIT_TAG main @@ -323,7 +368,7 @@ endif() # link everything else target_link_libraries(${PROJECT_NAME} PRIVATE - ${APP_LIBS} z luabit lua53 ogg modplug lua-https + ${APP_LIBS} z luabit lua53 ogg modplug lua-https luasocket ) @@ -333,6 +378,7 @@ include_directories( libraries/dr libraries/lua53 libraries/noise1234 + libraries/luasocket libraries/physfs libraries/wuff libraries/utf8 @@ -351,6 +397,7 @@ source/common/Message.cpp source/common/Module.cpp source/common/Object.cpp source/common/pixelformat.cpp +source/common/Reference.cpp source/common/Stream.cpp source/common/types.cpp source/common/Variant.cpp diff --git a/include/common/Reference.hpp b/include/common/Reference.hpp new file mode 100644 index 000000000..7d6267cf9 --- /dev/null +++ b/include/common/Reference.hpp @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2006-2024 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_REFERENCE_H +#define LOVE_REFERENCE_H + +struct lua_State; + +namespace love +{ + + /** + * This class wraps the reference functionality built into + * Lua, which allows C++ code to refer to Lua variables. + **/ + class Reference + { + public: + /** + * Creates the reference object, but does not create + * the actual reference. + **/ + Reference(); + + /** + * Creates the object and a reference to the value + * on the top of the stack. + **/ + Reference(lua_State* L); + + /** + * Deletes the reference, if any. + **/ + virtual ~Reference(); + + /** + * Creates a reference to the value on the + * top of the stack. + **/ + void ref(lua_State* L); + + /** + * Unrefs the reference, if any. + **/ + void unref(); + + /** + * Pushes the referred value onto the stack of the specified Lua coroutine. + * NOTE: The coroutine *must* belong to the same Lua state that was used for + * Reference::ref. + **/ + void push(lua_State* L); + + private: + // A pinned coroutine (probably the main thread) belonging to the Lua state + // in which the reference resides. + lua_State* pinnedL; + + // Index to the Lua reference. + int idx; + }; + +} // namespace love + +#endif // LOVE_REFERENCE_H diff --git a/include/common/luax.hpp b/include/common/luax.hpp index c8b38ba60..bd3b6ad7f 100644 --- a/include/common/luax.hpp +++ b/include/common/luax.hpp @@ -299,6 +299,8 @@ namespace love lua_Number luax_checknumberclamped01(lua_State* L, int index); + Reference* luax_refif(lua_State* L, int type); + // #endregion // #region Registry diff --git a/include/modules/keyboard/Keyboard.tcc b/include/modules/keyboard/Keyboard.tcc index 5494b3db5..5c80e55fb 100644 --- a/include/modules/keyboard/Keyboard.tcc +++ b/include/modules/keyboard/Keyboard.tcc @@ -5,12 +5,39 @@ #include "common/Result.hpp" #include +#include namespace love { class KeyboardBase : public Module { public: + enum KeyboardResult + { + RESULT_OK, //< Input accepted + RESULT_CANCEL, //< Input cancelled + RESULT_CONTINUE, //< Input validation callback should continue + RESULT_MAX_ENUM + }; + + struct KeyboardValidationInfo; + +#if defined(__3DS__) + using ValidationError = const char**; +#elif defined(__SWITCH__) + using ValidationError = char*; +#endif + + typedef KeyboardResult (*KeyboardValidationCallback)(const KeyboardValidationInfo* info, + const char* text, ValidationError error); + + struct KeyboardValidationInfo + { + KeyboardValidationCallback callback = nullptr; + void* data = nullptr; + void* luaState = nullptr; + }; + enum KeyboardType { TYPE_NORMAL, @@ -20,10 +47,11 @@ namespace love struct KeyboardOptions { - uint8_t type; - bool password; - std::string_view hint; - uint32_t maxLength; + uint8_t type; // KeyboardType + bool password; // Whether the input should be hidden + std::string_view hint; // Hint text + uint32_t maxLength; // Maximum length of the input + KeyboardValidationInfo callback; // Callback function }; enum KeyboardOption @@ -32,6 +60,7 @@ namespace love OPTION_PASSCODE, OPTION_HINT, OPTION_MAX_LENGTH, + OPTION_CALLBACK, OPTION_MAX_ENUM }; @@ -46,11 +75,7 @@ namespace love static constexpr uint32_t MULTIPLIER = 3; #endif - KeyboardBase() : - Module(M_KEYBOARD, "love.keyboard"), - keyRepeat(false), - showing(false), - text(nullptr) + KeyboardBase() : Module(M_KEYBOARD, "love.keyboard"), keyRepeat(false), showing(false), text(nullptr) {} virtual ~KeyboardBase() @@ -86,7 +111,8 @@ namespace love { "type", OPTION_TYPE }, { "password", OPTION_PASSCODE }, { "hint", OPTION_HINT }, - { "maxLength", OPTION_MAX_LENGTH } + { "maxLength", OPTION_MAX_LENGTH }, + { "callback", OPTION_CALLBACK } ); STRINGMAP_DECLARE(KeyboardTypes, KeyboardType, @@ -94,6 +120,12 @@ namespace love { "qwerty", TYPE_QWERTY }, { "numpad", TYPE_NUMPAD } ); + + STRINGMAP_DECLARE(KeyboardResults, KeyboardResult, + { "ok", RESULT_OK }, + { "cancel", RESULT_CANCEL }, + { "continue", RESULT_CONTINUE } + ); // clang-format on protected: diff --git a/libraries/luasocket/libluasocket/auxiliar.c b/libraries/luasocket/libluasocket/auxiliar.c new file mode 100644 index 000000000..93a66a09f --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/auxiliar.h b/libraries/luasocket/libluasocket/auxiliar.h new file mode 100644 index 000000000..e8c3ead82 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/buffer.c b/libraries/luasocket/libluasocket/buffer.c new file mode 100644 index 000000000..7148be34f --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/buffer.h b/libraries/luasocket/libluasocket/buffer.h new file mode 100644 index 000000000..a0901fcc8 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/compat.c b/libraries/luasocket/libluasocket/compat.c new file mode 100644 index 000000000..34ffdaf71 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/compat.h b/libraries/luasocket/libluasocket/compat.h new file mode 100644 index 000000000..fa2d7d7c6 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/except.c b/libraries/luasocket/libluasocket/except.c new file mode 100644 index 000000000..9c3317f26 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/except.h b/libraries/luasocket/libluasocket/except.h new file mode 100644 index 000000000..71c31fd4d --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/ftp.lua b/libraries/luasocket/libluasocket/ftp.lua new file mode 100644 index 000000000..a2f6563b8 --- /dev/null +++ b/libraries/luasocket/libluasocket/ftp.lua @@ -0,0 +1,332 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/headers.lua b/libraries/luasocket/libluasocket/headers.lua new file mode 100644 index 000000000..05818703f --- /dev/null +++ b/libraries/luasocket/libluasocket/headers.lua @@ -0,0 +1,107 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/http.lua b/libraries/luasocket/libluasocket/http.lua new file mode 100644 index 000000000..1d7672924 --- /dev/null +++ b/libraries/luasocket/libluasocket/http.lua @@ -0,0 +1,427 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/inet.c b/libraries/luasocket/libluasocket/inet.c new file mode 100644 index 000000000..138c9abe8 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/inet.h b/libraries/luasocket/libluasocket/inet.h new file mode 100644 index 000000000..5618b61b3 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/io.c b/libraries/luasocket/libluasocket/io.c new file mode 100644 index 000000000..5ad4b3afc --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/io.h b/libraries/luasocket/libluasocket/io.h new file mode 100644 index 000000000..b8a54df6e --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/ltn12.lua b/libraries/luasocket/libluasocket/ltn12.lua new file mode 100644 index 000000000..e210b568a --- /dev/null +++ b/libraries/luasocket/libluasocket/ltn12.lua @@ -0,0 +1,321 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/luasocket.c b/libraries/luasocket/libluasocket/luasocket.c new file mode 100644 index 000000000..0fd99f703 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/luasocket.h b/libraries/luasocket/libluasocket/luasocket.h new file mode 100644 index 000000000..1017fbaab --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/makefile b/libraries/luasocket/libluasocket/makefile new file mode 100644 index 000000000..06f4d1927 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/mbox.lua b/libraries/luasocket/libluasocket/mbox.lua new file mode 100644 index 000000000..f343d7168 --- /dev/null +++ b/libraries/luasocket/libluasocket/mbox.lua @@ -0,0 +1,96 @@ +R"luastring"--( +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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/mime.c b/libraries/luasocket/libluasocket/mime.c new file mode 100644 index 000000000..05602f566 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/mime.h b/libraries/luasocket/libluasocket/mime.h new file mode 100644 index 000000000..4d938f46e --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/mime.lua b/libraries/luasocket/libluasocket/mime.lua new file mode 100644 index 000000000..4831ebc44 --- /dev/null +++ b/libraries/luasocket/libluasocket/mime.lua @@ -0,0 +1,84 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/options.c b/libraries/luasocket/libluasocket/options.c new file mode 100644 index 000000000..3280c51d7 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/options.h b/libraries/luasocket/libluasocket/options.h new file mode 100644 index 000000000..456eeb5f4 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/pierror.h b/libraries/luasocket/libluasocket/pierror.h new file mode 100644 index 000000000..cb773ab7f --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/select.c b/libraries/luasocket/libluasocket/select.c new file mode 100644 index 000000000..bb47c4592 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/select.h b/libraries/luasocket/libluasocket/select.h new file mode 100644 index 000000000..5d45fe753 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/serial.c b/libraries/luasocket/libluasocket/serial.c new file mode 100644 index 000000000..21485d3e2 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/smtp.lua b/libraries/luasocket/libluasocket/smtp.lua new file mode 100644 index 000000000..8a8bf4de1 --- /dev/null +++ b/libraries/luasocket/libluasocket/smtp.lua @@ -0,0 +1,259 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/socket.h b/libraries/luasocket/libluasocket/socket.h new file mode 100644 index 000000000..2555bab64 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/socket.lua b/libraries/luasocket/libluasocket/socket.lua new file mode 100644 index 000000000..f70f3e1e4 --- /dev/null +++ b/libraries/luasocket/libluasocket/socket.lua @@ -0,0 +1,152 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/tcp.c b/libraries/luasocket/libluasocket/tcp.c new file mode 100644 index 000000000..e84db8454 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/tcp.h b/libraries/luasocket/libluasocket/tcp.h new file mode 100644 index 000000000..9b282efeb --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/timeout.c b/libraries/luasocket/libluasocket/timeout.c new file mode 100644 index 000000000..2bdc0698c --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/timeout.h b/libraries/luasocket/libluasocket/timeout.h new file mode 100644 index 000000000..9e5250d33 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/tp.lua b/libraries/luasocket/libluasocket/tp.lua new file mode 100644 index 000000000..03a5344d7 --- /dev/null +++ b/libraries/luasocket/libluasocket/tp.lua @@ -0,0 +1,137 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/udp.c b/libraries/luasocket/libluasocket/udp.c new file mode 100644 index 000000000..712ad50fe --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/udp.h b/libraries/luasocket/libluasocket/udp.h new file mode 100644 index 000000000..07d5247fc --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unix.c b/libraries/luasocket/libluasocket/unix.c new file mode 100644 index 000000000..268d8b212 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unix.h b/libraries/luasocket/libluasocket/unix.h new file mode 100644 index 000000000..c20356189 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unixdgram.c b/libraries/luasocket/libluasocket/unixdgram.c new file mode 100644 index 000000000..69093d734 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unixdgram.h b/libraries/luasocket/libluasocket/unixdgram.h new file mode 100644 index 000000000..a1a0166bd --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unixstream.c b/libraries/luasocket/libluasocket/unixstream.c new file mode 100644 index 000000000..02aced9c8 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/unixstream.h b/libraries/luasocket/libluasocket/unixstream.h new file mode 100644 index 000000000..7916affa7 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/url.lua b/libraries/luasocket/libluasocket/url.lua new file mode 100644 index 000000000..3bf1c17e4 --- /dev/null +++ b/libraries/luasocket/libluasocket/url.lua @@ -0,0 +1,333 @@ +R"luastring"--( +----------------------------------------------------------------------------- +-- 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 +-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. +--)luastring"--" diff --git a/libraries/luasocket/libluasocket/usocket.c b/libraries/luasocket/libluasocket/usocket.c new file mode 100644 index 000000000..69635daa6 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/usocket.h b/libraries/luasocket/libluasocket/usocket.h new file mode 100644 index 000000000..45f2f99f7 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/wsocket.c b/libraries/luasocket/libluasocket/wsocket.c new file mode 100644 index 000000000..6cb1e415c --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/libluasocket/wsocket.h b/libraries/luasocket/libluasocket/wsocket.h new file mode 100644 index 000000000..398664026 --- /dev/null +++ b/libraries/luasocket/libluasocket/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/libraries/luasocket/luasocket.cpp b/libraries/luasocket/luasocket.cpp new file mode 100644 index 000000000..9c973a1fb --- /dev/null +++ b/libraries/luasocket/luasocket.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2006-2022 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "luasocket.hpp" + +// LuaSocket +extern "C" +{ +#include "libluasocket/luasocket.h" +#include "libluasocket/mime.h" +} + +// Lua files +static constexpr char ftp_lua[] = { +#include "libluasocket/ftp.lua" +}; + +static constexpr char headers_lua[] = { +#include "libluasocket/headers.lua" +}; + +static constexpr char http_lua[] = { +#include "libluasocket/http.lua" +}; + +static constexpr char ltn12_lua[] = { +#include "libluasocket/ltn12.lua" +}; + +static constexpr char mbox_lua[] = { +#include "libluasocket/mbox.lua" +}; + +static constexpr char mime_lua[] = { +#include "libluasocket/mime.lua" +}; + +static constexpr char smtp_lua[] = { +#include "libluasocket/smtp.lua" +}; + +static constexpr char socket_lua[] = { +#include "libluasocket/socket.lua" +}; + +static constexpr char tp_lua[] = { +#include "libluasocket/tp.lua" +}; + +static constexpr char url_lua[] = { +#include "libluasocket/url.lua" +}; + +static void preload(lua_State* L, const char* name, lua_CFunction func) +{ + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushcfunction(L, func); + lua_setfield(L, -2, name); + lua_pop(L, 2); +} + +static void preload(lua_State* L, const char* name, const char* chunkname, const void* lua, + size_t size) +{ + if (luaL_loadbuffer(L, (const char*)lua, size, name) != 0) + { + luaL_loadstring(L, "local name, msg = ... return function() error(name..\": \"..msg) end"); + lua_pushstring(L, name); + lua_pushvalue(L, -3); + // Before: + // -1: error message + // -2: module name + // -3: loadstring function + // -4: error message + lua_call(L, 2, 1); + // After: + // -1: function + // -2: error message + lua_remove(L, -2); + } + + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + lua_pushvalue(L, -3); + lua_setfield(L, -2, name); + lua_pop(L, 3); +} + +namespace love::luasocket +{ + int preload(lua_State* L) + { + // Preload code from LuaSocket. + preload(L, "socket.core", luaopen_socket_core); + preload(L, "mime.core", luaopen_mime_core); + + preload(L, "socket", "=[socket \"socket.lua\"]", socket_lua, sizeof(socket_lua)); + preload(L, "socket.ftp", "=[socket \"ftp.lua\"]", ftp_lua, sizeof(ftp_lua)); + preload(L, "socket.http", "=[socket \"http.lua\"]", http_lua, sizeof(http_lua)); + preload(L, "ltn12", "=[socket \"ltn12.lua\"]", ltn12_lua, sizeof(ltn12_lua)); + preload(L, "mime", "=[socket \"mime.lua\"]", mime_lua, sizeof(mime_lua)); + preload(L, "socket.smtp", "=[socket \"smtp.lua\"]", smtp_lua, sizeof(smtp_lua)); + preload(L, "socket.tp", "=[socket \"tp.lua\"]", tp_lua, sizeof(tp_lua)); + preload(L, "socket.url", "=[socket \"url.lua\"]", url_lua, sizeof(url_lua)); + preload(L, "socket.headers", "=[socket \"headers.lua\"]", headers_lua, sizeof(headers_lua)); + preload(L, "mbox", "=[socket \"mbox.lua\"]", mbox_lua, sizeof(mbox_lua)); + + // No need to register garbage collector function. + + return 0; + } +} // namespace love::luasocket diff --git a/libraries/luasocket/luasocket.hpp b/libraries/luasocket/luasocket.hpp new file mode 100644 index 000000000..4ef5879ca --- /dev/null +++ b/libraries/luasocket/luasocket.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2006-2022 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_LUASOCKET_LUASOCKET_H +#define LOVE_LUASOCKET_LUASOCKET_H + +// LOVE + +extern "C" +{ +#include +#include +#include +} + +namespace love +{ + namespace luasocket + { + int preload(lua_State* L); + } // namespace luasocket +} // namespace love + +#endif // LOVE_LUASOCKET_LUASOCKET_H diff --git a/platform/cafe/libraries/luasocket.patch b/platform/cafe/libraries/luasocket.patch new file mode 100644 index 000000000..8d2a9fb65 --- /dev/null +++ b/platform/cafe/libraries/luasocket.patch @@ -0,0 +1,2109 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..35b8131 100755 +--- a/inet.c ++++ b/inet.c +@@ -139,9 +139,13 @@ static int inet_global_toip(lua_State *L) + + int inet_optfamily(lua_State* L, int narg, const char* def) + { ++ #if !defined(__WIIU__) + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; +- ++ #else ++ static const char* optname[] = { "unspec", "inet", NULL }; ++ static int optvalue[] = { AF_UNSPEC, AF_INET, 0 }; ++ #endif + return optvalue[luaL_checkoption(L, narg, def, optname)]; + } + +@@ -187,11 +191,13 @@ static int inet_global_getaddrinfo(lua_State *L) + lua_pushliteral(L, "inet"); + lua_settable(L, -3); + break; ++ #if !defined(__WIIU__) + case AF_INET6: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "inet6"); + lua_settable(L, -3); + break; ++ #endif + case AF_UNSPEC: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unspec"); +@@ -241,16 +247,26 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); ++ #if !defined(__WIIU__) + char name[INET6_ADDRSTRLEN]; ++ #else ++ char name[INET_ADDRSTRLEN]; ++ #endif + 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; + } ++ #if !defined(__WIIU__) + err = getnameinfo((struct sockaddr *) &peer, peer_len, + name, INET6_ADDRSTRLEN, + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err = getnameinfo((struct sockaddr *) &peer, peer_len, ++ name, INET_ADDRSTRLEN, ++ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -260,7 +276,9 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) + lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; ++ #if !defined(__WIIU__) + case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ #endif + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } +@@ -275,15 +293,24 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + int err; + struct sockaddr_storage peer; + socklen_t peer_len = sizeof(peer); ++ #if !defined(__WIIU__) + char name[INET6_ADDRSTRLEN]; ++ #else ++ char name[INET_ADDRSTRLEN]; ++ #endif + 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; + } ++ #if !defined(__WIIU__) + err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err=getnameinfo((struct sockaddr *)&peer, peer_len, ++ name, INET_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -293,7 +320,9 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + lua_pushstring(L, port); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; ++ #if !defined(__WIIU__) + case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ #endif + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; + } +@@ -348,10 +377,12 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) + \*-------------------------------------------------------------------------*/ + 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 !defined(__WIIU__) + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } ++ #endif + return err; + } + +@@ -369,6 +400,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } ++ #if !defined(__WIIU__) + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; +@@ -378,6 +410,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } ++ #endif + } + return NULL; + } +@@ -436,7 +469,9 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client, + socklen_t len; + t_sockaddr_storage addr; + switch (family) { ++ #if !defined(__WIIU__) + case AF_INET6: len = sizeof(struct sockaddr_in6); break; ++ #endif + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# 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/options.c b/options.c +index 3280c51..ce8fcb4 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -147,6 +163,7 @@ int opt_get_keepalive(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_dontroute(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +@@ -156,6 +173,10 @@ int opt_get_dontroute(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); + } ++#else ++int opt_set_dontroute(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_dontroute(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_broadcast(lua_State *L, p_socket ps) +@@ -216,6 +237,7 @@ int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +@@ -236,7 +258,6 @@ 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) + { +@@ -247,8 +268,19 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); + } ++#else ++int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +@@ -258,7 +290,10 @@ int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + } +- ++#else ++int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + /*------------------------------------------------------*/ + int opt_set_linger(lua_State *L, p_socket ps) + { +@@ -293,6 +328,7 @@ int opt_get_linger(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__WIIU__) + int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL); +@@ -355,6 +391,21 @@ int opt_set_ip6_v6only(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); + } ++#else ++int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_set_ip_multicast_if(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_if(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_set_ip6_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } ++ ++int opt_get_ip6_v6only(lua_State *L, p_socket ps) { return get_opt_error(L); } ++int opt_set_ip6_v6only(lua_State *L, p_socket ps) { return set_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_get_error(lua_State *L, p_socket ps) +@@ -373,6 +424,7 @@ int opt_get_error(lua_State *L, p_socket ps) + /*=========================================================================*\ + * Auxiliar functions + \*=========================================================================*/ ++#if !defined(__WIIU__) + static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ip_mreq val; /* obj, name, table */ +@@ -419,6 +471,10 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* 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/tcp.c b/tcp.c +index e84db84..d718a85 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -312,6 +312,7 @@ static int meth_close(lua_State *L) + static int meth_getfamily(lua_State *L) + { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); ++ #if !defined(__WIIU__) + if (tcp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; +@@ -322,6 +323,15 @@ static int meth_getfamily(lua_State *L) + lua_pushliteral(L, "inet4"); + return 1; + } ++ #else ++ if (tcp->family == AF_INET) { ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ } else { ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ } ++ #endif + } + + /*-------------------------------------------------------------------------*\ +@@ -427,9 +437,18 @@ static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); + } + ++#if !defined(__WIIU__) + static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif + + static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); +diff --git a/udp.c b/udp.c +index 712ad50..7262c8e 100755 +--- a/udp.c ++++ b/udp.c +@@ -267,7 +267,11 @@ static int meth_receivefrom(lua_State *L) { + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); ++ #if !defined(__WIIU__) + char addrstr[INET6_ADDRSTRLEN]; ++ #else ++ char addrstr[INET_ADDRSTRLEN]; ++ #endif + char portstr[6]; + int err; + p_timeout tm = &udp->tm; +@@ -286,8 +290,13 @@ static int meth_receivefrom(lua_State *L) { + if (wanted > sizeof(buf)) free(dgram); + return 2; + } ++ #if !defined(__WIIU__) + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #else ++ err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, ++ INET_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); ++ #endif + if (err) { + lua_pushnil(L); + lua_pushstring(L, LUA_GAI_STRERROR(err)); +@@ -306,6 +315,7 @@ static int meth_receivefrom(lua_State *L) { + \*-------------------------------------------------------------------------*/ + static int meth_getfamily(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); ++ #if !defined(__WIIU__) + if (udp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; +@@ -313,6 +323,10 @@ static int meth_getfamily(lua_State *L) { + lua_pushliteral(L, "inet4"); + return 1; + } ++ #else ++ lua_pushliteral(L, "inet4"); ++ return 1; ++ #endif + } + + /*-------------------------------------------------------------------------*\ +@@ -483,6 +497,15 @@ static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); + } + ++#if !defined(__WIIU__) + static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif +diff --git a/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* 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/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#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/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#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/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* 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/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#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/usocket.c b/usocket.c +index 69635da..ecf6d2a 100644 +--- a/usocket.c ++++ b/usocket.c +@@ -18,7 +18,7 @@ + * Wait for readable/writable/connected socket with timeout + \*-------------------------------------------------------------------------*/ + #ifndef SOCKET_SELECT +-#include ++#include + + #define WAITFD_R POLLIN + #define WAITFD_W POLLOUT +@@ -236,7 +236,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { +- long put = (long) sendto(*ps, data, count, 0, addr, len); ++ long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; +@@ -403,7 +403,15 @@ const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; ++ #if !defined(__WIIU__) + default: return hstrerror(err); ++ #else ++ case TRY_AGAIN: return "Host name lookup failure"; ++ case NO_RECOVERY: return "Unknown server error"; ++ case NO_ADDRESS: ++ return "No address associated with name"; ++ ++ #endif + } + } + +diff --git a/usocket.h b/usocket.h +index 45f2f99..12c25ca 100644 +--- a/usocket.h ++++ b/usocket.h +@@ -29,7 +29,6 @@ + #include + /* TCP options (nagle algorithm disable) */ + #include +-#include + + #ifndef SO_REUSEPORT + #define SO_REUSEPORT SO_REUSEADDR +diff --git a/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* 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/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#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/platform/ctr/include/modules/font/BCFNTRasterizer.hpp b/platform/ctr/include/modules/font/BCFNTRasterizer.hpp index cae2e125c..ff4032ba5 100644 --- a/platform/ctr/include/modules/font/BCFNTRasterizer.hpp +++ b/platform/ctr/include/modules/font/BCFNTRasterizer.hpp @@ -49,7 +49,11 @@ namespace love ptrdiff_t getHandle() const override; private: + CFNT_s* getUserdata(Data* data) const; + int glyphCount; float scale; + + CFNT_s* userdata; }; } // namespace love diff --git a/platform/ctr/include/modules/font/Font.hpp b/platform/ctr/include/modules/font/Font.hpp index 3003e1dc2..0c9d1624f 100644 --- a/platform/ctr/include/modules/font/Font.hpp +++ b/platform/ctr/include/modules/font/Font.hpp @@ -4,6 +4,8 @@ #include <3ds.h> +using SystemFontType = CFG_Region; + namespace love { class FontModule : public FontModuleBase @@ -11,6 +13,8 @@ namespace love public: FontModule(); + static SystemFont* loadSystemFontByType(CFG_Region region); + static CFNT_s* loadSystemFont(CFG_Region region, size_t& size); virtual Rasterizer* newRasterizer(FileData* data) const override; @@ -20,6 +24,15 @@ namespace love using FontModuleBase::newTrueTypeRasterizer; + // clang-format off + STRINGMAP_DECLARE(SystemFonts, CFG_Region, + { "standard", CFG_REGION_USA }, + { "chinese", CFG_REGION_CHN }, + { "korean", CFG_REGION_KOR }, + { "taiwanese", CFG_REGION_TWN } + ); + // clang-format on + private: static constexpr auto FONT_ARCHIVE_TITLE = 0x0004009B00014002ULL; diff --git a/platform/ctr/include/modules/keyboard/Keyboard.hpp b/platform/ctr/include/modules/keyboard/Keyboard.hpp index d1f3b91a6..6b4095561 100644 --- a/platform/ctr/include/modules/keyboard/Keyboard.hpp +++ b/platform/ctr/include/modules/keyboard/Keyboard.hpp @@ -24,6 +24,12 @@ namespace love { TYPE_QWERTY, SWKBD_TYPE_QWERTY }, { TYPE_NUMPAD, SWKBD_TYPE_NUMPAD } ); + + ENUMMAP_DECLARE(CtrKeyboardResults, KeyboardResult, SwkbdCallbackResult, + { RESULT_OK, SWKBD_CALLBACK_OK }, + { RESULT_CANCEL, SWKBD_CALLBACK_CLOSE }, + { RESULT_CONTINUE, SWKBD_CALLBACK_CONTINUE } + ); // clang-format on private: diff --git a/platform/ctr/libraries/luasocket.patch b/platform/ctr/libraries/luasocket.patch new file mode 100644 index 000000000..01bbcb478 --- /dev/null +++ b/platform/ctr/libraries/luasocket.patch @@ -0,0 +1,2397 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..c262cf9 100755 +--- a/inet.c ++++ b/inet.c +@@ -139,8 +139,13 @@ static int inet_global_toip(lua_State *L) + + int inet_optfamily(lua_State* L, int narg, const char* def) + { ++ #if !defined(__3DS__) + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; ++ #else ++ static const char* optname[] = { "unspec", "inet", NULL }; ++ static int optvalue[] = { AF_UNSPEC, AF_INET, 0 }; ++ #endif + + return optvalue[luaL_checkoption(L, narg, def, optname)]; + } +@@ -348,10 +353,12 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) + \*-------------------------------------------------------------------------*/ + 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 !defined(__3DS__) + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } ++ #endif + return err; + } + +@@ -369,6 +376,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } ++ #if !defined(__3DS__) + case AF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; +@@ -378,6 +386,7 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } ++ #endif + } + return NULL; + } +@@ -436,7 +445,9 @@ const char *inet_tryaccept(p_socket server, int family, p_socket client, + socklen_t len; + t_sockaddr_storage addr; + switch (family) { ++ #if !defined(__3DS__) + case AF_INET6: len = sizeof(struct sockaddr_in6); break; ++ #endif + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } +diff --git a/inet.h b/inet.h +index 5618b61..f11d5ed 100644 +--- a/inet.h ++++ b/inet.h +@@ -22,6 +22,10 @@ + #define LUASOCKET_INET_ATON + #endif + ++#ifndef INET6_ADDRSTRLEN ++#define INET6_ADDRSTRLEN INET_ADDRSTRLEN ++#endif ++ + #ifndef _WIN32 + #pragma GCC visibility push(hidden) + #endif +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# 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/options.c b/options.c +index 3280c51..9a45e6f 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -136,6 +152,7 @@ int opt_set_tcp_keepintvl(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_keepalive(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +@@ -167,6 +184,16 @@ int opt_get_broadcast(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); + } ++#else ++int opt_set_keepalive(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_keepalive(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_dontroute(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_dontroute(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_broadcast(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_broadcast(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_recv_buf_size(lua_State *L, p_socket ps) +@@ -216,6 +243,7 @@ int opt_set_tcp_defer_accept(lua_State *L, p_socket ps) + #endif + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +@@ -236,6 +264,13 @@ int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) + { + return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + } ++#else ++int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++ ++int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_ip_multicast_loop(lua_State *L, p_socket ps) +@@ -249,6 +284,7 @@ int opt_get_ip_multicast_loop(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +@@ -258,6 +294,10 @@ int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) + { + return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + } ++#else ++int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_linger(lua_State *L, p_socket ps) +@@ -299,6 +339,7 @@ int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip_multicast_if(lua_State *L, p_socket ps) + { + const char *address = luaL_checkstring(L, 3); /* obj, name, ip */ +@@ -322,6 +363,10 @@ int opt_get_ip_multicast_if(lua_State *L, p_socket ps) + lua_pushstring(L, inet_ntoa(val)); + return 1; + } ++#else ++int opt_set_ip_multicast_if(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_get_ip_multicast_if(lua_State *L, p_socket ps) { return get_opt_error(L); } ++#endif + + /*------------------------------------------------------*/ + int opt_set_ip_add_membership(lua_State *L, p_socket ps) +@@ -335,6 +380,7 @@ int opt_set_ip_drop_membersip(lua_State *L, p_socket ps) + } + + /*------------------------------------------------------*/ ++#if !defined(__3DS__) + int opt_set_ip6_add_membership(lua_State *L, p_socket ps) + { + return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP); +@@ -355,7 +401,13 @@ int opt_set_ip6_v6only(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY); + } ++#else ++int opt_set_ip6_add_membership(lua_State *L, p_socket ps) { return set_opt_error(L); } ++int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps) { return set_opt_error(L); } + ++int opt_get_ip6_v6only(lua_State *L, p_socket ps) { return get_opt_error(L); } ++int opt_set_ip6_v6only(lua_State *L, p_socket ps) { return set_opt_error(L); } ++#endif + /*------------------------------------------------------*/ + int opt_get_error(lua_State *L, p_socket ps) + { +@@ -394,6 +446,7 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } + ++#if !defined(__3DS__) + static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ipv6_mreq val; /* obj, opt-name, table */ +@@ -419,6 +472,12 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) ++{ ++ return set_opt_error(L); ++} ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* 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/tcp.c b/tcp.c +index e84db84..9dfa27e 100644 +--- a/tcp.c ++++ b/tcp.c +@@ -427,9 +427,18 @@ static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); + } + ++#if !defined(__3DS__) + static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State* L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif + + static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); +diff --git a/udp.c b/udp.c +index 712ad50..a3c7a5f 100755 +--- a/udp.c ++++ b/udp.c +@@ -483,6 +483,15 @@ static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); + } + ++#if !defined(__3DS__) + static int global_create6(lua_State *L) { + return udp_create(L, AF_INET6); + } ++#else ++static int global_create6(lua_State *L) { ++ lua_pushnil(L); ++ lua_pushstring(L, "Setting local interface error: not supported"); ++ ++ return 2; ++} ++#endif +diff --git a/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* 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/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#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/unixdgram.c b/unixdgram.c +deleted file mode 100644 +index 69093d7..0000000 +--- a/unixdgram.c ++++ /dev/null +@@ -1,405 +0,0 @@ +-/*=========================================================================*\ +-* 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/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#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/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* 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/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#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/usocket.c b/usocket.c +index 69635da..3cd8cde 100644 +--- a/usocket.c ++++ b/usocket.c +@@ -18,7 +18,7 @@ + * Wait for readable/writable/connected socket with timeout + \*-------------------------------------------------------------------------*/ + #ifndef SOCKET_SELECT +-#include ++#include + + #define WAITFD_R POLLIN + #define WAITFD_W POLLOUT +@@ -431,12 +431,16 @@ const char *socket_ioerror(p_socket ps, int err) { + const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { ++#if !defined(__3DS__) + case EAI_AGAIN: return PIE_AGAIN; + case EAI_BADFLAGS: return PIE_BADFLAGS; ++#endif + #ifdef EAI_BADHINTS + case EAI_BADHINTS: return PIE_BADHINTS; + #endif ++#if !defined(__3DS__) + case EAI_FAIL: return PIE_FAIL; ++#endif + case EAI_FAMILY: return PIE_FAMILY; + case EAI_MEMORY: return PIE_MEMORY; + case EAI_NONAME: return PIE_NONAME; +@@ -446,9 +450,13 @@ const char *socket_gaistrerror(int err) { + #ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return PIE_PROTOCOL; + #endif ++#if !defined(__3DS__) + case EAI_SERVICE: return PIE_SERVICE; ++#endif + case EAI_SOCKTYPE: return PIE_SOCKTYPE; ++#if !defined(__3DS__) + case EAI_SYSTEM: return strerror(errno); ++#endif + default: return LUA_GAI_STRERROR(err); + } + } +diff --git a/usocket.h b/usocket.h +index 45f2f99..12c25ca 100644 +--- a/usocket.h ++++ b/usocket.h +@@ -29,7 +29,6 @@ + #include + /* TCP options (nagle algorithm disable) */ + #include +-#include + + #ifndef SO_REUSEPORT + #define SO_REUSEPORT SO_REUSEADDR +diff --git a/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* 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/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#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/platform/ctr/source/modules/font/BCFNTRasterizer.cpp b/platform/ctr/source/modules/font/BCFNTRasterizer.cpp index 0d8acccc1..6fe30faa6 100644 --- a/platform/ctr/source/modules/font/BCFNTRasterizer.cpp +++ b/platform/ctr/source/modules/font/BCFNTRasterizer.cpp @@ -74,6 +74,11 @@ namespace love return result; } + CFNT_s* BCFNTRasterizer::getUserdata(Data* data) const + { + return (this->userdata == nullptr) ? (CFNT_s*)data->getData() : this->userdata; + } + BCFNTRasterizer::BCFNTRasterizer(Data* data, int size) { this->dpiScale = 1.0f; @@ -82,11 +87,21 @@ namespace love if (this->size == 0) throw love::Exception("Invalid font size: {:d}", this->size); + if (linearGetSize(data->getData()) == 0) + { + this->userdata = (CFNT_s*)linearAlloc(data->getSize()); + + if (this->userdata == nullptr) + throw love::Exception(E_OUT_OF_MEMORY); + + std::memcpy(this->userdata, data->getData(), data->getSize()); + } + /* if we already have this data loaded, fixing this (again) is a bad time™ */ - if ((uintptr_t)fontGetInfo((CFNT_s*)data->getData())->tglp < (uintptr_t)data->getData()) - fontFixPointers((CFNT_s*)data->getData()); + if ((uintptr_t)fontGetInfo(this->getUserdata(data))->tglp < (uintptr_t)this->getUserdata(data)) + fontFixPointers(this->getUserdata(data)); - auto* fontInfo = fontGetInfo((CFNT_s*)data->getData()); + auto* fontInfo = fontGetInfo(this->getUserdata(data)); auto* sheetInfo = fontInfo->tglp; this->scale = std::floor(this->size * this->dpiScale + 0.5f) / sheetInfo->cellHeight; @@ -104,7 +119,10 @@ namespace love } BCFNTRasterizer::~BCFNTRasterizer() - {} + { + if (this->userdata) + linearFree(this->userdata); + } TextShaper* BCFNTRasterizer::newTextShaper() { @@ -114,7 +132,7 @@ namespace love bool BCFNTRasterizer::hasGlyph(uint32_t codepoint) const { const int index = this->getGlyphIndex(codepoint); - const auto* info = fontGetInfo((CFNT_s*)this->data->getData()); + const auto* info = fontGetInfo(this->getUserdata(this->data)); return index != info->alterCharIndex && codepoint != '\t'; } @@ -125,7 +143,7 @@ namespace love const auto flag = GLYPH_POS_CALC_VTXCOORD; fontGlyphPos_s result {}; - fontCalcGlyphPos(&result, (CFNT_s*)this->data->getData(), index, flag, this->scale, this->scale); + fontCalcGlyphPos(&result, this->getUserdata(this->data), index, flag, this->scale, this->scale); return result.xAdvance; } @@ -135,7 +153,7 @@ namespace love fontGlyphPos_s result {}; const auto flag = GLYPH_POS_CALC_VTXCOORD; - fontCalcGlyphPos(&result, (CFNT_s*)this->data->getData(), index, flag, this->scale, this->scale); + fontCalcGlyphPos(&result, this->getUserdata(this->data), index, flag, this->scale, this->scale); GlyphMetrics metrics {}; metrics.height = this->metrics.height; @@ -151,7 +169,7 @@ namespace love sheet.right = result.texcoord.right; sheet.bottom = result.texcoord.bottom; - const auto* info = fontGetGlyphInfo((CFNT_s*)this->data->getData()); + const auto* info = fontGetGlyphInfo(this->getUserdata(this->data)); PixelFormat format; if (!citro3d::getConstant((GPU_TEXCOLOR)info->sheetFmt, format)) @@ -162,7 +180,7 @@ namespace love int BCFNTRasterizer::getGlyphIndex(uint32_t codepoint) const { - return love::fontGlyphIndexFromCodePoint((CFNT_s*)this->data->getData(), codepoint); + return love::fontGlyphIndexFromCodePoint(this->getUserdata(this->data), codepoint); } int BCFNTRasterizer::getGlyphCount() const @@ -172,6 +190,6 @@ namespace love ptrdiff_t BCFNTRasterizer::getHandle() const { - return (ptrdiff_t)this->data->getData(); + return (ptrdiff_t)this->getUserdata(this->data); } } // namespace love diff --git a/platform/ctr/source/modules/font/Font.cpp b/platform/ctr/source/modules/font/Font.cpp index e439a571e..682be80b5 100644 --- a/platform/ctr/source/modules/font/Font.cpp +++ b/platform/ctr/source/modules/font/Font.cpp @@ -19,6 +19,11 @@ namespace love } // #endregion + SystemFont* FontModule::loadSystemFontByType(SystemFontType type) + { + return new SystemFont(type); + } + static CFNT_s* loadFromArchive(uint64_t title, const char* path, size_t& outSize) { std::unique_ptr data; @@ -103,7 +108,7 @@ namespace love FontModule::FontModule() : FontModuleBase("love.font.ctr") { - this->defaultFontData.set(new SystemFont(CFG_REGION_USA)); + this->defaultFontData.set(new SystemFont(CFG_REGION_USA), Acquire::NO_RETAIN); } Rasterizer* FontModule::newRasterizer(FileData* data) const diff --git a/platform/ctr/source/modules/keyboard/Keyboard.cpp b/platform/ctr/source/modules/keyboard/Keyboard.cpp index 25e3c929a..5c5b6f016 100644 --- a/platform/ctr/source/modules/keyboard/Keyboard.cpp +++ b/platform/ctr/source/modules/keyboard/Keyboard.cpp @@ -3,6 +3,17 @@ namespace love { + static SwkbdCallbackResult inputCallback(void* udata, const char** msg, const char* text, size_t length) + { + auto luaCallback = (KeyboardBase::KeyboardValidationInfo*)udata; + auto result = luaCallback->callback(luaCallback, text, msg); + + SwkbdCallbackResult out; + Keyboard::getConstant(result, out); + + return out; + } + Keyboard::Keyboard() : KeyboardBase(), state {} {} @@ -24,6 +35,7 @@ namespace love swkbdInit(&this->state, type, 2, length); swkbdSetInitialText(&this->state, this->text.get()); swkbdSetHintText(&this->state, options.hint.data()); + swkbdSetFilterCallback(&this->state, inputCallback, (void*)&options.callback); if (options.password) swkbdSetPasswordMode(&this->state, SWKBD_PASSWORD_HIDE_DELAY); diff --git a/platform/hac/libraries/luasocket.patch b/platform/hac/libraries/luasocket.patch new file mode 100644 index 000000000..6ba9fa36a --- /dev/null +++ b/platform/hac/libraries/luasocket.patch @@ -0,0 +1,2133 @@ +diff --git a/inet.c b/inet.c +index 138c9ab..1cb1cda 100755 +--- a/inet.c ++++ b/inet.c +@@ -371,7 +371,11 @@ const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + } + case AF_INET6: { + struct sockaddr_in6 sin6; ++ #if !defined(__SWITCH__) + struct in6_addr addrany = IN6ADDR_ANY_INIT; ++ #else ++ struct in6_addr addrany = in6addr_any; ++ #endif + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; +diff --git a/makefile b/makefile +deleted file mode 100755 +index 06f4d19..0000000 +--- a/makefile ++++ /dev/null +@@ -1,461 +0,0 @@ +-# 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/options.c b/options.c +index 3280c51..7c88d01 100644 +--- a/options.c ++++ b/options.c +@@ -22,6 +22,22 @@ static int opt_set(lua_State *L, p_socket ps, int level, int name, + static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + ++static int set_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "setsockopt failed: not supported"); ++ ++ return 2; ++} ++ ++static int get_opt_error(lua_State* L) ++{ ++ lua_pushnil(L); ++ lua_pushstring(L, "getsockopt failed: not supported"); ++ ++ return 2; ++} ++ + /*=========================================================================*\ + * Exported functions + \*=========================================================================*/ +@@ -393,7 +409,7 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } +- ++#if !defined(__SWITCH__) + static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + { + struct ipv6_mreq val; /* obj, opt-name, table */ +@@ -419,6 +435,9 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + } + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } ++#else ++static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) { return set_opt_error(L); } ++#endif + + static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +diff --git a/serial.c b/serial.c +deleted file mode 100644 +index 21485d3..0000000 +--- a/serial.c ++++ /dev/null +@@ -1,171 +0,0 @@ +-/*=========================================================================*\ +-* 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/unix.c b/unix.c +deleted file mode 100644 +index 268d8b2..0000000 +--- a/unix.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/*=========================================================================*\ +-* 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/unix.h b/unix.h +deleted file mode 100644 +index c203561..0000000 +--- a/unix.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-#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/unixdgram.c b/unixdgram.c +deleted file mode 100644 +index 69093d7..0000000 +--- a/unixdgram.c ++++ /dev/null +@@ -1,405 +0,0 @@ +-/*=========================================================================*\ +-* 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/unixdgram.h b/unixdgram.h +deleted file mode 100644 +index a1a0166..0000000 +--- a/unixdgram.h ++++ /dev/null +@@ -1,28 +0,0 @@ +-#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/unixstream.c b/unixstream.c +deleted file mode 100644 +index 02aced9..0000000 +--- a/unixstream.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/*=========================================================================*\ +-* 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/unixstream.h b/unixstream.h +deleted file mode 100644 +index 7916aff..0000000 +--- a/unixstream.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-#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/wsocket.c b/wsocket.c +deleted file mode 100755 +index 6cb1e41..0000000 +--- a/wsocket.c ++++ /dev/null +@@ -1,434 +0,0 @@ +-/*=========================================================================*\ +-* 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/wsocket.h b/wsocket.h +deleted file mode 100644 +index 3986640..0000000 +--- a/wsocket.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-#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/source/common/Reference.cpp b/source/common/Reference.cpp new file mode 100644 index 000000000..99e20facd --- /dev/null +++ b/source/common/Reference.cpp @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2006-2024 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "common/Reference.hpp" +#include "common/luax.hpp" + +namespace love +{ + + const char REFERENCE_TABLE_NAME[] = "love-references"; + + Reference::Reference() : pinnedL(nullptr), idx(LUA_REFNIL) + {} + + Reference::Reference(lua_State* L) : pinnedL(nullptr), idx(LUA_REFNIL) + { + ref(L); + } + + Reference::~Reference() + { + unref(); + } + + void Reference::ref(lua_State* L) + { + unref(); // Previously created reference needs to be cleared + pinnedL = luax_getpinnedthread(L); + luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); + lua_insert(L, -2); // Move reference table behind value. + idx = luaL_ref(L, -2); + lua_pop(L, 1); + } + + void Reference::unref() + { + if (idx != LUA_REFNIL) + { + // We use a pinned thread/coroutine for the Lua state because we know it + // hasn't been garbage collected and is valid, as long as the whole lua + // state is still open. + luax_insist(pinnedL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); + luaL_unref(pinnedL, -1, idx); + lua_pop(pinnedL, 1); + idx = LUA_REFNIL; + } + } + + void Reference::push(lua_State* L) + { + if (idx != LUA_REFNIL) + { + luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME); + lua_rawgeti(L, -1, idx); + lua_remove(L, -2); + } + else + lua_pushnil(L); + } + +} // namespace love diff --git a/source/common/luax.cpp b/source/common/luax.cpp index a575c751f..45a5a8fb0 100644 --- a/source/common/luax.cpp +++ b/source/common/luax.cpp @@ -3,6 +3,7 @@ #include "common/Module.hpp" #include "common/Object.hpp" +#include "common/Reference.hpp" #include #include @@ -989,5 +990,17 @@ namespace love return 1; } + Reference* luax_refif(lua_State* L, int type) + { + Reference* r = nullptr; + + // Create a reference only if the test succeeds. + if (lua_type(L, -1) == type) + r = new Reference(L); + else // Pop the value manually if it fails (done by Reference if it succeeds). + lua_pop(L, 1); + + return r; + } // #endregion } // namespace love diff --git a/source/modules/font/wrap_Font.cpp b/source/modules/font/wrap_Font.cpp index c15eb70e3..baec05bc7 100644 --- a/source/modules/font/wrap_Font.cpp +++ b/source/modules/font/wrap_Font.cpp @@ -75,6 +75,25 @@ static Rasterizer::Settings luax_checktruetypesettings(lua_State* L, int index) return settings; } +/* +** This is a simplified version(?) from 3.0 to make it suck less. +** We check the system font type *first*, but if it fails, we just get the file data via filename. +** Getting the data by filename will automatically propagate the error if the file doesn't exist. +** +** On Nintendo 3DS, we return a Data object wrapped through SystemFont because of linear memory. +*/ +static Data* luax_checkfilename(lua_State* L, int index) +{ + // CFG_REGION_USA, PlSharedFontType_Standard, OS_SHAREDDATATYPE_FONT_STANDARD + auto systemFontType = SystemFontType(0); + const char* name = luaL_checkstring(L, index); + + if (!FontModule::getConstant(name, systemFontType)) + return luax_getfiledata(L, index); + + return FontModule::loadSystemFontByType(systemFontType); +} + int Wrap_FontModule::newTrueTypeRasterizer(lua_State* L) { Rasterizer* rasterizer = nullptr; @@ -89,6 +108,31 @@ int Wrap_FontModule::newTrueTypeRasterizer(lua_State* L) luax_catchexcept(L, [&] { rasterizer = instance()->newTrueTypeRasterizer(size, settings); }); } + else + { + int size = luaL_optinteger(L, 2, 12); + + Rasterizer::Settings settings {}; + if (!lua_isnoneornil(L, 3)) + settings = luax_checktruetypesettings(L, 3); + + Data* data = nullptr; + + if (luax_istype(L, 1, Data::type)) + { + data = luax_checkdata(L, 1); + data->retain(); + } + else + data = luax_checkfilename(L, 1); + + // clang-format off + luax_catchexcept( + L, [&] { rasterizer = instance()->newTrueTypeRasterizer(data, size, settings); }, + [&](bool) { data->release(); } + ); + // clang-format on + } luax_pushtype(L, rasterizer); rasterizer->release(); diff --git a/source/modules/graphics/Graphics.cpp b/source/modules/graphics/Graphics.cpp index 3550fdee6..41c35fd79 100644 --- a/source/modules/graphics/Graphics.cpp +++ b/source/modules/graphics/Graphics.cpp @@ -991,8 +991,8 @@ namespace love for (int index = 0; index < points; ++index, phi += shift) { - coords[index].x = x + a * std::cos(phi); - coords[index].y = y + b * std::sin(phi); + coords[index].x = x + a * std::cosf(phi); + coords[index].y = y + b * std::sinf(phi); } coords[points] = coords[0]; diff --git a/source/modules/graphics/wrap_Graphics.cpp b/source/modules/graphics/wrap_Graphics.cpp index 501341e74..a57a68cbc 100644 --- a/source/modules/graphics/wrap_Graphics.cpp +++ b/source/modules/graphics/wrap_Graphics.cpp @@ -6,6 +6,7 @@ #include "modules/graphics/wrap_Font.hpp" #include "modules/graphics/wrap_Quad.hpp" +#include "modules/graphics/wrap_TextBatch.hpp" #include "modules/graphics/wrap_Texture.hpp" #include "modules/image/Image.hpp" @@ -1821,7 +1822,8 @@ static constexpr lua_CFunction types[] = open_drawable, love::open_texture, love::open_quad, - love::open_font + love::open_font, + love::open_textbatch }; // clang-format on diff --git a/source/modules/graphics/wrap_TextBatch.cpp b/source/modules/graphics/wrap_TextBatch.cpp index e80170a3c..cd7c3291e 100644 --- a/source/modules/graphics/wrap_TextBatch.cpp +++ b/source/modules/graphics/wrap_TextBatch.cpp @@ -190,7 +190,7 @@ static constexpr luaL_Reg functions[] = { "getFont", Wrap_TextBatch::getFont }, { "getWidth", Wrap_TextBatch::getWidth }, { "getHeight", Wrap_TextBatch::getHeight }, - { "getDimensions", Wrap_TextBatch::getDimensions }, + { "getDimensions", Wrap_TextBatch::getDimensions } }; // clang-format on diff --git a/source/modules/keyboard/wrap_Keyboard.cpp b/source/modules/keyboard/wrap_Keyboard.cpp index 21d8adbcf..d5f00d725 100644 --- a/source/modules/keyboard/wrap_Keyboard.cpp +++ b/source/modules/keyboard/wrap_Keyboard.cpp @@ -1,3 +1,5 @@ +#include "common/Reference.hpp" + #include "modules/keyboard/wrap_Keyboard.hpp" using namespace love; @@ -53,6 +55,43 @@ int Wrap_Keyboard::isModifierActive(lua_State* L) return 1; } +static Keyboard::KeyboardResult textInputValidationCallback(const Keyboard::KeyboardValidationInfo* info, + const char* text, Keyboard::ValidationError error) +{ + lua_State* L = (lua_State*)info->luaState; + auto* reference = (Reference*)info->data; + + if (reference == nullptr) + luaL_error(L, "Internal error in text input validation callback."); + + reference->push(L); + delete reference; + + luax_pushstring(L, text); + lua_call(L, 1, 2); + + const char* result = lua_tostring(L, -2); + + Keyboard::KeyboardResult out; + if (!Keyboard::getConstant(result, out)) + luax_enumerror(L, "keyboard result", Keyboard::KeyboardResults, result); + + const char* message = lua_tostring(L, -1); + + if (message != nullptr) + { +#if defined(__3DS__) + *error = message; +#elif defined(__SWITCH__) + std::memcpy(error, message, std::strlen(message)); +#endif + } + + lua_pop(L, 2); + + return out; +} + int Wrap_Keyboard::setTextInput(lua_State* L) { // clang-format off @@ -63,7 +102,8 @@ int Wrap_Keyboard::setTextInput(lua_State* L) type : Keyboard::TYPE_NORMAL, password : false, hint : "", - maxLength : Keyboard::DEFAULT_INPUT_LENGTH + maxLength : Keyboard::DEFAULT_INPUT_LENGTH, + callback : nullptr }; lua_getfield(L, 1, Keyboard::getConstant(Keyboard::OPTION_TYPE)); @@ -94,6 +134,22 @@ int Wrap_Keyboard::setTextInput(lua_State* L) options.maxLength = luaL_checkinteger(L, -1); lua_pop(L, 1); + lua_getfield(L, 1, Keyboard::getConstant(Keyboard::OPTION_CALLBACK)); + if (!lua_isnoneornil(L, -1)) + { + Keyboard::KeyboardValidationInfo info {}; + + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, -1); + + info.data = luax_refif(L, LUA_TFUNCTION); + lua_pop(L, 1); + info.callback = textInputValidationCallback; + + options.callback = info; + } + lua_pop(L, 1); + instance()->setTextInput(options); return 0; diff --git a/source/modules/love/love.cpp b/source/modules/love/love.cpp index f91830734..6becc83d3 100644 --- a/source/modules/love/love.cpp +++ b/source/modules/love/love.cpp @@ -1,6 +1,8 @@ #include "common/luax.hpp" #include "common/version.hpp" +#include + #include "modules/love/love.hpp" #include "modules/audio/wrap_Audio.hpp" @@ -240,6 +242,9 @@ int love_initialize(lua_State* L) lua_pushstring(L, __OS__); lua_setfield(L, -2, "_os"); + lua_pushstring(L, __CONSOLE__); + lua_setfield(L, -2, "_console"); + lua_pushcfunction(L, love_setDeprecationOutput); lua_setfield(L, -2, "setDeprecationOutput"); @@ -254,7 +259,7 @@ int love_initialize(lua_State* L) luax_addcompatibilityalias(L, "string", "gmatch", "gfind"); #endif - // love::luasocket::preload(L); + love::luasocket::preload(L); love::luax_preload(L, luaopen_luautf8, "utf8"); love::luax_preload(L, luaopen_https, "https"); diff --git a/source/modules/love/scripts/callbacks.lua b/source/modules/love/scripts/callbacks.lua index 5df1c82d3..3f61238d9 100644 --- a/source/modules/love/scripts/callbacks.lua +++ b/source/modules/love/scripts/callbacks.lua @@ -207,7 +207,7 @@ end -- local is_wii_u = love._os == "Cafe" local function get3DDepth(screen) - if love._console ~= "3ds" then + if love._console ~= "3DS" then return nil end