import luafilesystem-3.1.0

This commit is contained in:
Matt Boney 2023-11-16 15:47:31 -07:00
parent 94952bb821
commit d54559cf13
39 changed files with 6786 additions and 0 deletions

View file

@ -0,0 +1,154 @@
/*=========================================================================*\
* Auxiliar routines for class hierarchy manipulation
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include <string.h>
#include <stdio.h>
/*-------------------------------------------------------------------------*\
* 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);
}

View file

@ -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 <module>{<class>}. 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 */

View file

@ -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;
}

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,129 @@
/*=========================================================================*\
* Simple exception support
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "except.h"
#include <stdio.h>
#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;
}

View file

@ -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

View file

@ -0,0 +1,537 @@
/*=========================================================================*\
* Internet domain functions
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "inet.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*=========================================================================*\
* 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;