diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index 6c0943e46..b63102ff8 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -10,15 +10,12 @@ CXX_GUARD_START -#include #ifdef USE_DEBUGGERS #include #endif #include #include -mLOG_DECLARE_CATEGORY(SCRIPT); - struct mCore; struct mScriptTextBuffer; mSCRIPT_DECLARE_STRUCT(mCore); diff --git a/include/mgba/internal/script/socket.h b/include/mgba/internal/script/socket.h new file mode 100644 index 000000000..014a765bb --- /dev/null +++ b/include/mgba/internal/script/socket.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_SCRIPT_SOCKET_H +#define M_SCRIPT_SOCKET_H + +enum mSocketErrorCode { + mSCRIPT_SOCKERR_UNKNOWN_ERROR = -1, + mSCRIPT_SOCKERR_OK = 0, + mSCRIPT_SOCKERR_AGAIN, + mSCRIPT_SOCKERR_ADDRESS_IN_USE, + mSCRIPT_SOCKERR_CONNECTION_REFUSED, + mSCRIPT_SOCKERR_DENIED, + mSCRIPT_SOCKERR_FAILED, + mSCRIPT_SOCKERR_NETWORK_UNREACHABLE, + mSCRIPT_SOCKERR_NOT_FOUND, + mSCRIPT_SOCKERR_NO_DATA, + mSCRIPT_SOCKERR_OUT_OF_MEMORY, + mSCRIPT_SOCKERR_TIMEOUT, + mSCRIPT_SOCKERR_UNSUPPORTED, +}; + +#endif diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 753c356ba..ff4b34223 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -10,6 +10,7 @@ CXX_GUARD_START +#include #include #include #include @@ -18,6 +19,8 @@ CXX_GUARD_START #define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) } #define mSCRIPT_KV_SENTINEL { NULL, NULL } +mLOG_DECLARE_CATEGORY(SCRIPT); + struct mScriptFrame; struct mScriptFunction; struct mScriptEngineContext; @@ -83,6 +86,7 @@ struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); +void mScriptContextAttachSocket(struct mScriptContext* context); void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value); diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index a9a108e14..cd1e716d4 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -113,6 +113,7 @@ void ScriptingController::runCode(const QString& code) { void ScriptingController::init() { mScriptContextInit(&m_scriptContext); mScriptContextAttachStdlib(&m_scriptContext); + mScriptContextAttachSocket(&m_scriptContext); mScriptContextRegisterEngines(&m_scriptContext); mScriptContextAttachLogger(&m_scriptContext, &m_logger); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index e9ecc8c60..5cfb4ff41 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -1,6 +1,7 @@ include(ExportDirectory) set(SOURCE_FILES context.c + socket.c stdlib.c types.c) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index caca84d03..3d9886d81 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include +#include #include #include @@ -51,6 +53,156 @@ static int _luaLenList(lua_State* lua); static int _luaRequireShim(lua_State* lua); +static const char* _socketLuaSource = + "socket = {\n" + " ERRORS = {},\n" + " tcp = function() return socket._create(_socket.create(), socket._tcpMT) end,\n" + " bind = function(address, port)\n" + " local s = socket.tcp()\n" + " local ok, err = s:bind(address, port)\n" + " if ok then return s end\n" + " return ok, err\n" + " end,\n" + " connect = function(address, port)\n" + " local s = socket.tcp()\n" + " local ok, err = s:connect(address, port)\n" + " if ok then return s end\n" + " return ok, err\n" + " end,\n" + " _create = function(sock, mt) return setmetatable({\n" + " _s = sock,\n" + " _callbacks = {},\n" + " _nextCallback = 1,\n" + " }, mt) end,\n" + " _wrap = function(status)\n" + " if status == 0 then return 1 end\n" + " return nil, socket.ERRORS[status] or ('error#' .. status)\n" + " end,\n" + " _mt = {\n" + " __index = {\n" + " close = function(self)\n" + " if self._onframecb then\n" + " callbacks:remove(self._onframecb)\n" + " self._onframecb = nil\n" + " end\n" + " self._callbacks = {}\n" + " return self._s:close()\n" + " end,\n" + " add = function(self, event, callback)\n" + " if not self._callbacks[event] then self._callbacks[event] = {} end\n" + " local cbid = self._nextCallback\n" + " self._nextCallback = cbid + 1\n" + " self._callbacks[event][cbid] = callback\n" + " return id\n" + " end,\n" + " remove = function(self, cbid)\n" + " for _, group in pairs(self._callbacks) do\n" + " if group[cbid] then\n" + " group[cbid] = nil\n" + " end\n" + " end\n" + " end,\n" + " _dispatch = function(self, event, ...)\n" + " if not self._callbacks[event] then return end\n" + " for k, cb in pairs(self._callbacks[event]) do\n" + " if cb then\n" + " local ok, ret = pcall(cb, self, ...)\n" + " if not ok then console:error(ret) end\n" + " end\n" + " end\n" + " end,\n" + " },\n" + " },\n" + " _tcpMT = {\n" + " __index = {\n" + " _hook = function(self, status)\n" + " if status == 0 then\n" + " self._onframecb = callbacks:add('frame', function() self:poll() end)\n" + " end\n" + " return socket._wrap(status)\n" + " end,\n" + " bind = function(self, address, port)\n" + " return socket._wrap(self._s:open(address or '', port))\n" + " end,\n" + " connect = function(self, address, port)\n" + " local status = self._s:connect(address, port)\n" + " end,\n" + " listen = function(self, backlog)\n" + " local status = self._s:listen(backlog or 1)\n" + " return self:_hook(status)\n" + " end,\n" + " accept = function(self)\n" + " local client = self._s:accept()\n" + " if client.error ~= 0 then\n" + " client:close()\n" + " return socket._wrap(client.error)\n" + " end\n" + " local sock = socket._create(client, socket._tcpMT)\n" + " sock:_hook(0)\n" + " return sock\n" + " end,\n" + " send = function(self, data, i, j)\n" + " local result = self._s:send(string.sub(data, i or 1, j))\n" + " if result < 0 then return socket._wrap(self._s.error) end\n" + " if i then return result + i - 1 end\n" + " return result\n" + " end,\n" + // TODO: This does not match the API for LuaSocket's receive() implementation + " receive = function(self, maxBytes)\n" + " local result = self._s:recv(maxBytes)\n" + " if (not result or #result == 0) and self._s.error ~= 0 then\n" + " return socket._wrap(self._s.error)\n" + " elseif not result or #result == 0 then\n" + " return nil, 'disconnected'\n" + " end\n" + " return result or ''\n" + " end,\n" + " hasdata = function(self)\n" + " local status = self._s:select(0)\n" + " if status < 0 then\n" + " return socket._wrap(self._s.error)\n" + " end\n" + " return status > 0\n" + " end,\n" + " poll = function(self)\n" + " local status, err = self:hasdata()\n" + " if err then\n" + " self:_dispatch('error', err)\n" + " elseif status then\n" + " self:_dispatch('received')\n" + " end\n" + " end,\n" + " },\n" + " },\n" + " _errMT = {\n" + " __index = function (tbl, key)\n" + " return rawget(tbl, C.SOCKERR[key])\n" + " end,\n" + " },\n" + "}\n" + "setmetatable(socket._tcpMT.__index, socket._mt)\n" + "setmetatable(socket.ERRORS, socket._errMT)\n"; + +static const struct _mScriptSocketError { + enum mSocketErrorCode err; + const char* message; +} _mScriptSocketErrors[] = { + { mSCRIPT_SOCKERR_UNKNOWN_ERROR, "unknown error" }, + { mSCRIPT_SOCKERR_OK, NULL }, + { mSCRIPT_SOCKERR_AGAIN, "temporary failure" }, + { mSCRIPT_SOCKERR_ADDRESS_IN_USE, "address in use" }, + { mSCRIPT_SOCKERR_DENIED, "access denied" }, + { mSCRIPT_SOCKERR_UNSUPPORTED, "unsupported" }, + { mSCRIPT_SOCKERR_CONNECTION_REFUSED, "connection refused" }, + { mSCRIPT_SOCKERR_NETWORK_UNREACHABLE, "network unreachable" }, + { mSCRIPT_SOCKERR_TIMEOUT, "timeout" }, + { mSCRIPT_SOCKERR_FAILED, "failed" }, + { mSCRIPT_SOCKERR_NOT_FOUND, "not found" }, + { mSCRIPT_SOCKERR_NO_DATA, "no data" }, + { mSCRIPT_SOCKERR_OUT_OF_MEMORY, "out of memory" }, +}; +static const int _mScriptSocketNumErrors = sizeof(_mScriptSocketErrors) / sizeof(struct _mScriptSocketError); + #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber #endif @@ -177,6 +329,26 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS lua_getglobal(luaContext->lua, "require"); luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + int status = luaL_dostring(luaContext->lua, _socketLuaSource); + if (status) { + mLOG(SCRIPT, ERROR, "Error in dostring while initializing sockets: %s\n", lua_tostring(luaContext->lua, -1)); + lua_pop(luaContext->lua, 1); + } else { + int i; + lua_getglobal(luaContext->lua, "socket"); + lua_getfield(luaContext->lua, -1, "ERRORS"); + for (i = 0; i < _mScriptSocketNumErrors; i++) { + struct _mScriptSocketError* err = &_mScriptSocketErrors[i]; + if (err->message) { + lua_pushstring(luaContext->lua, err->message); + } else { + lua_pushnil(luaContext->lua); + } + lua_seti(luaContext->lua, -2, err->err); + } + lua_pop(luaContext->lua, 2); + } + return &luaContext->d; } diff --git a/src/script/socket.c b/src/script/socket.c new file mode 100644 index 000000000..bdf66992a --- /dev/null +++ b/src/script/socket.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include +#include +#include + +struct mScriptSocket { + Socket socket; + struct Address address; + int32_t error; + uint16_t port; +}; +mSCRIPT_DECLARE_STRUCT(mScriptSocket); + +static const struct _mScriptSocketErrorMapping { + int32_t nativeError; + enum mSocketErrorCode mappedError; +} _mScriptSocketErrorMappings[] = { + { EAGAIN, mSCRIPT_SOCKERR_AGAIN }, + { EADDRINUSE, mSCRIPT_SOCKERR_ADDRESS_IN_USE }, + { ECONNREFUSED, mSCRIPT_SOCKERR_CONNECTION_REFUSED }, + { EACCES, mSCRIPT_SOCKERR_DENIED }, + { EPERM, mSCRIPT_SOCKERR_DENIED }, + { ENOTRECOVERABLE, mSCRIPT_SOCKERR_FAILED }, + { ENETUNREACH, mSCRIPT_SOCKERR_NETWORK_UNREACHABLE }, + { ETIMEDOUT, mSCRIPT_SOCKERR_TIMEOUT }, + { EINVAL, mSCRIPT_SOCKERR_UNSUPPORTED }, + { EPROTONOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED }, +#ifndef USE_GETHOSTBYNAME +#ifdef _WIN32 + { WSATRY_AGAIN, mSCRIPT_SOCKERR_AGAIN }, + { WSANO_RECOVERY, mSCRIPT_SOCKERR_FAILED }, + { WSANO_DATA, mSCRIPT_SOCKERR_NO_DATA }, + { WSAHOST_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND }, + { WSATYPE_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND }, + { WSA_NOT_ENOUGH_MEMORY, mSCRIPT_SOCKERR_OUT_OF_MEMORY }, + { WSAEAFNOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED }, + { WSAEINVAL, mSCRIPT_SOCKERR_UNSUPPORTED }, + { WSAESOCKTNOSUPPORT, mSCRIPT_SOCKERR_UNSUPPORTED }, +#else + { EAI_AGAIN, mSCRIPT_SOCKERR_AGAIN }, + { EAI_FAIL, mSCRIPT_SOCKERR_FAILED }, + { EAI_NODATA, mSCRIPT_SOCKERR_NO_DATA }, + { EAI_NONAME, mSCRIPT_SOCKERR_NOT_FOUND }, + { EAI_MEMORY, mSCRIPT_SOCKERR_OUT_OF_MEMORY }, +#endif +#else + { -TRY_AGAIN, mSCRIPT_SOCKERR_AGAIN }, + { -NO_RECOVERY, mSCRIPT_SOCKERR_FAILED }, + { -NO_DATA, mSCRIPT_SOCKERR_NO_DATA }, + { -HOST_NOT_FOUND, mSCRIPT_SOCKERR_NOT_FOUND }, +#endif +}; +static const int _mScriptSocketNumErrorMappings = sizeof(_mScriptSocketErrorMappings) / sizeof(struct _mScriptSocketErrorMapping); + +static void _mScriptSocketSetError(struct mScriptSocket* ssock, int32_t err) { + if (!err) { + ssock->error = mSCRIPT_SOCKERR_OK; + return; + } + int i; + for (i = 0; i < _mScriptSocketNumErrorMappings; i++) { + if (_mScriptSocketErrorMappings[i].nativeError == err) { + ssock->error = _mScriptSocketErrorMappings[i].mappedError; + return; + } + } + ssock->error = mSCRIPT_SOCKERR_UNKNOWN_ERROR; +} + +static void _mScriptSocketSetSocket(struct mScriptSocket* ssock, Socket socket) { + if (SOCKET_FAILED(socket)) { + ssock->socket = INVALID_SOCKET; + _mScriptSocketSetError(ssock, SocketError()); + } else { + ssock->socket = socket; + ssock->error = mSCRIPT_SOCKERR_OK; + } +} + +struct mScriptValue* _mScriptSocketCreate() { + struct mScriptSocket client = { + .socket = INVALID_SOCKET, + .error = mSCRIPT_SOCKERR_OK, + .port = 0 + }; + + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptSocket)); + result->value.opaque = calloc(1, sizeof(struct mScriptSocket)); + *(struct mScriptSocket*) result->value.opaque = client; + result->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + return result; +} + +void _mScriptSocketClose(struct mScriptSocket* ssock) { + if (!SOCKET_FAILED(ssock->socket)) { + SocketClose(ssock->socket); + } +} + +struct mScriptValue* _mScriptSocketAccept(struct mScriptSocket* ssock) { + struct mScriptValue* value = _mScriptSocketCreate(); + struct mScriptSocket* client = (struct mScriptSocket*) value->value.opaque; + _mScriptSocketSetSocket(client, SocketAccept(ssock->socket, &client->address)); + if (!client->error) { + SocketSetBlocking(client->socket, false); + } + return value; +} + +int32_t _mScriptSocketOpen(struct mScriptSocket* ssock, const char* addressStr, uint16_t port) { + struct Address* addr = NULL; + if (addressStr && addressStr[0]) { + int32_t err = SocketResolveHost(addressStr, &ssock->address); + if (err) { + _mScriptSocketSetError(ssock, err); + return err; + } + addr = &ssock->address; + } + ssock->port = port; + _mScriptSocketSetSocket(ssock, SocketOpenTCP(port, addr)); + if (!ssock->error) { + SocketSetBlocking(ssock->socket, false); + } + return ssock->error; +} + +int32_t _mScriptSocketConnect(struct mScriptSocket* ssock, const char* addressStr, uint16_t port) { + int32_t err = SocketResolveHost(addressStr, &ssock->address); + if (err) { + _mScriptSocketSetError(ssock, err); + return err; + } + ssock->port = port; + _mScriptSocketSetSocket(ssock, SocketConnectTCP(port, &ssock->address)); + if (!ssock->error) { + SocketSetBlocking(ssock->socket, false); + } + return ssock->error; +} + +int32_t _mScriptSocketListen(struct mScriptSocket* ssock, uint32_t queueLength) { + _mScriptSocketSetError(ssock, SocketListen(ssock->socket, queueLength)); + return ssock->error; +} + +int32_t _mScriptSocketSend(struct mScriptSocket* ssock, struct mScriptString* data) { + ssize_t written = SocketSend(ssock->socket, data->buffer, data->size); + if (written < 0) { + _mScriptSocketSetError(ssock, SocketError()); + return -ssock->error; + } + ssock->error = mSCRIPT_SOCKERR_OK; + return written; +} + +struct mScriptValue* _mScriptSocketRecv(struct mScriptSocket* ssock, uint32_t maxBytes) { + struct mScriptValue* value = mScriptStringCreateEmpty(maxBytes); + struct mScriptString* data = value->value.string; + ssize_t bytes = SocketRecv(ssock->socket, data->buffer, maxBytes); + if (bytes <= 0) { + data->size = 0; + _mScriptSocketSetError(ssock, SocketError()); + } else { + data->size = bytes; + ssock->error = mSCRIPT_SOCKERR_OK; + } + return value; +} + +// This works sufficiently well for a single socket, but it could be better. +// Ideally, all sockets would be tracked and selected on together for efficiency. +uint32_t _mScriptSocketSelectOne(struct mScriptSocket* ssock, int64_t timeoutMillis) { + Socket reads[] = { ssock->socket }; + Socket errors[] = { ssock->socket }; + int result = SocketPoll(1, reads, NULL, errors, timeoutMillis); + if (!result) { + return 0; + } else if (errors[0] != INVALID_SOCKET) { + _mScriptSocketSetError(ssock, SocketError()); + return -1; + } + return 1; +} + +mSCRIPT_BIND_FUNCTION(mScriptSocketCreate_Binding, W(mScriptSocket), _mScriptSocketCreate, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptSocket, close, _mScriptSocketClose, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, W(mScriptSocket), accept, _mScriptSocketAccept, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, open, _mScriptSocketOpen, 2, CHARP, address, U16, port); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, connect, _mScriptSocketConnect, 2, CHARP, address, U16, port); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptSocket, S32, listen, _mScriptSocketListen, 1, U32, queueLength); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, send, _mScriptSocketSend, 1, STR, data); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, WSTR, recv, _mScriptSocketRecv, 1, U32, maxBytes); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptSocket, S32, select, _mScriptSocketSelectOne, 1, S64, timeoutMillis); + +mSCRIPT_DEFINE_STRUCT(mScriptSocket) + mSCRIPT_DEFINE_CLASS_DOCSTRING("An internal implementation of a TCP network socket.") + mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptSocket, close) + mSCRIPT_DEFINE_DOCSTRING("Closes the socket. If the socket is already closed, this function does nothing.") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, close) + mSCRIPT_DEFINE_DOCSTRING("Creates a new socket for an incoming connection from a listening server socket.") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, accept) + mSCRIPT_DEFINE_DOCSTRING( + "Binds the socket to a specified address and port. " + "If no address is specified, the socket is bound to all network interfaces." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, open) + mSCRIPT_DEFINE_DOCSTRING( + "Opens a TCP connection to the specified address and port.\n" + "**Caution:** This is a blocking call. The emulator will not respond until " + "the connection either succeeds or fails." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, connect) + mSCRIPT_DEFINE_DOCSTRING( + "Begins listening for incoming connections. The socket must have first been " + "bound with the `open` function." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, listen) + mSCRIPT_DEFINE_DOCSTRING( + "Sends data over a socket. Returns the number of bytes written, or -1 if an " + "error occurs." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, send) + mSCRIPT_DEFINE_DOCSTRING( + "Reads data from a socket, up to the specified number of bytes. " + "If the socket has been disconnected, this function returns an empty string. " + "Use `select` to test if data is available to be read." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, recv) + mSCRIPT_DEFINE_DOCSTRING( + "Checks the status of the socket. " + "Returns 1 if data is available to be read. " + "Returns -1 if an error has occurred on the socket." + ) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptSocket, select) + mSCRIPT_DEFINE_DOCSTRING( + "One of the `SOCKERR` constants describing the last error on the socket." + ) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptSocket, S32, error) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptSocket, listen) + mSCRIPT_S32(1) +mSCRIPT_DEFINE_DEFAULTS_END; + + +void mScriptContextAttachSocket(struct mScriptContext* context) { + mScriptContextExportNamespace(context, "_socket", (struct mScriptKVPair[]) { + mSCRIPT_KV_PAIR(create, &mScriptSocketCreate_Binding), + mSCRIPT_KV_SENTINEL + }); + mScriptContextSetDocstring(context, "_socket", "Basic TCP sockets library"); + mScriptContextSetDocstring(context, "_socket.create", "Creates a new socket object"); + mScriptContextExportConstants(context, "SOCKERR", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, UNKNOWN_ERROR), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, OK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, AGAIN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, ADDRESS_IN_USE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, CONNECTION_REFUSED), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, DENIED), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, FAILED), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NETWORK_UNREACHABLE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NOT_FOUND), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, NO_DATA), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, OUT_OF_MEMORY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, TIMEOUT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_SOCKERR, UNSUPPORTED), + mSCRIPT_KV_SENTINEL + }); +}