inline luasocket

Just builds for now, isn't available yet to Lua code.
This commit is contained in:
Kartik K. Agaram 2021-11-20 20:26:17 -08:00
parent 38b21ef79b
commit 6bdc3d17a7
51 changed files with 9430 additions and 1 deletions

View File

@ -28,7 +28,8 @@ CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
lundump.o lvm.o lzio.o \
kilo.o
LIB_O= lauxlib.o lbaselib.o lcurseslib.o ldblib.o liolib.o lmathlib.o \
loslib.o ltablib.o lstrlib.o loadlib.o linit.o lcurses/curses.o
loslib.o ltablib.o lstrlib.o loadlib.o linit.o \
lcurses/curses.o luasocket/socket.a
LUA_T= teliva
LUA_O= lua.o
@ -58,7 +59,11 @@ $(LUA_T): $(LUA_O) $(LUA_A)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
luasocket/socket.a:
make -C luasocket linux
clean:
make -C luasocket clean
$(RM) $(ALL_T) $(ALL_O)
depend:

154
src/luasocket/auxiliar.c Normal file
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);
}

54
src/luasocket/auxiliar.h Normal file
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 */

270
src/luasocket/buffer.c Normal file
View File

@ -0,0 +1,270 @@
/*=========================================================================*\
* 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 = lua_gettop(L);
luaL_Buffer b;
size_t size;
const char *part = luaL_optlstring(L, 3, "", &size);
timeout_markstart(buf->tm);
/* initialize buffer with optional extra prefix
* (useful for concatenating previous partial results) */
luaL_buffinit(L, &b);
luaL_addlstring(&b, part, size);
/* receive new patterns */
if (!lua_isnumber(L, 2)) {
const char *p= luaL_optstring(L, 2, "*l");
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
else luaL_argcheck(L, 0, 2, "invalid receive pattern");
/* get a fixed number of bytes (minus what was already partially
* received) */
} else {
double n = lua_tonumber(L, 2);
size_t wanted = (size_t) n;
luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
if (size == 0 || wanted > size)
err = recvraw(buf, wanted-size, &b);
}
/* check if there was an error */
if (err != IO_DONE) {
/* we can't push 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;
}

52
src/luasocket/buffer.h Normal file
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 */

39
src/luasocket/compat.c Normal file
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

22
src/luasocket/compat.h Normal file
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

129
src/luasocket/except.c Normal file
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;
}

46
src/luasocket/except.h Normal file
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

329
src/luasocket/ftp.lua Normal file
View File

@ -0,0 +1,329 @@
-----------------------------------------------------------------------------
-- FTP support for the Lua language
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local string = require("string")
local math = require("math")
local socket = require("socket")
local url = require("socket.url")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
socket.ftp = {}
local _M = socket.ftp
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- timeout in seconds before the program gives up on a connection
_M.TIMEOUT = 60
-- default port for ftp service
local PORT = 21
-- this is the default anonymous password. used when no password is
-- provided in url. should be changed to your e-mail.
_M.USER = "ftp"
_M.PASSWORD = "anonymous@anonymous.org"
-----------------------------------------------------------------------------
-- Low level FTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function _M.open(server, port, create)
local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create))
local f = base.setmetatable({ tp = tp }, metat)
-- make sure everything gets closed in an exception
f.try = socket.newtry(function() f:close() end)
return f
end
function metat.__index:portconnect()
self.try(self.server:settimeout(_M.TIMEOUT))
self.data = self.try(self.server:accept())
self.try(self.data:settimeout(_M.TIMEOUT))
end
function metat.__index:pasvconnect()
self.data = self.try(socket.tcp())
self.try(self.data:settimeout(_M.TIMEOUT))
self.try(self.data:connect(self.pasvt.address, self.pasvt.port))
end
function metat.__index:login(user, password)
self.try(self.tp:command("user", user or _M.USER))
local code, reply = 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 code, 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 code, reply = self.try(self.tp:check("229"))
local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)"
local d, prt, address, 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, port = 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, port = 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, reply = 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

104
src/luasocket/headers.lua Normal file
View File

@ -0,0 +1,104 @@
-----------------------------------------------------------------------------
-- Canonic header field capitalization
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
local socket = require("socket")
socket.headers = {}
local _M = socket.headers
_M.canonic = {
["accept"] = "Accept",
["accept-charset"] = "Accept-Charset",
["accept-encoding"] = "Accept-Encoding",
["accept-language"] = "Accept-Language",
["accept-ranges"] = "Accept-Ranges",
["action"] = "Action",
["alternate-recipient"] = "Alternate-Recipient",
["age"] = "Age",
["allow"] = "Allow",
["arrival-date"] = "Arrival-Date",
["authorization"] = "Authorization",
["bcc"] = "Bcc",
["cache-control"] = "Cache-Control",
["cc"] = "Cc",
["comments"] = "Comments",
["connection"] = "Connection",
["content-description"] = "Content-Description",
["content-disposition"] = "Content-Disposition",
["content-encoding"] = "Content-Encoding",
["content-id"] = "Content-ID",
["content-language"] = "Content-Language",
["content-length"] = "Content-Length",
["content-location"] = "Content-Location",
["content-md5"] = "Content-MD5",
["content-range"] = "Content-Range",
["content-transfer-encoding"] = "Content-Transfer-Encoding",
["content-type"] = "Content-Type",
["cookie"] = "Cookie",
["date"] = "Date",
["diagnostic-code"] = "Diagnostic-Code",
["dsn-gateway"] = "DSN-Gateway",
["etag"] = "ETag",
["expect"] = "Expect",
["expires"] = "Expires",
["final-log-id"] = "Final-Log-ID",
["final-recipient"] = "Final-Recipient",
["from"] = "From",
["host"] = "Host",
["if-match"] = "If-Match",
["if-modified-since"] = "If-Modified-Since",
["if-none-match"] = "If-None-Match",
["if-range"] = "If-Range",
["if-unmodified-since"] = "If-Unmodified-Since",
["in-reply-to"] = "In-Reply-To",
["keywords"] = "Keywords",
["last-attempt-date"] = "Last-Attempt-Date",
["last-modified"] = "Last-Modified",
["location"] = "Location",
["max-forwards"] = "Max-Forwards",
["message-id"] = "Message-ID",
["mime-version"] = "MIME-Version",
["original-envelope-id"] = "Original-Envelope-ID",
["original-recipient"] = "Original-Recipient",
["pragma"] = "Pragma",
["proxy-authenticate"] = "Proxy-Authenticate",
["proxy-authorization"] = "Proxy-Authorization",
["range"] = "Range",
["received"] = "Received",
["received-from-mta"] = "Received-From-MTA",
["references"] = "References",
["referer"] = "Referer",
["remote-mta"] = "Remote-MTA",
["reply-to"] = "Reply-To",
["reporting-mta"] = "Reporting-MTA",
["resent-bcc"] = "Resent-Bcc",
["resent-cc"] = "Resent-Cc",
["resent-date"] = "Resent-Date",
["resent-from"] = "Resent-From",
["resent-message-id"] = "Resent-Message-ID",
["resent-reply-to"] = "Resent-Reply-To",
["resent-sender"] = "Resent-Sender",
["resent-to"] = "Resent-To",
["retry-after"] = "Retry-After",
["return-path"] = "Return-Path",
["sender"] = "Sender",
["server"] = "Server",
["smtp-remote-recipient"] = "SMTP-Remote-Recipient",
["status"] = "Status",
["subject"] = "Subject",
["te"] = "TE",
["to"] = "To",
["trailer"] = "Trailer",
["transfer-encoding"] = "Transfer-Encoding",
["upgrade"] = "Upgrade",
["user-agent"] = "User-Agent",
["vary"] = "Vary",
["via"] = "Via",
["warning"] = "Warning",
["will-retry-until"] = "Will-Retry-Until",
["www-authenticate"] = "WWW-Authenticate",
["x-mailer"] = "X-Mailer",
}
return _M

420
src/luasocket/http.lua Normal file
View File

@ -0,0 +1,420 @@
-----------------------------------------------------------------------------
-- 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 }}
-- default scheme and port for document retrieval
local SCHEME = 'http'
local PORT = SCHEMES[SCHEME].port
-----------------------------------------------------------------------------
-- 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, part = 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)
-- 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
headers = 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

537
src/luasocket/inet.c Normal file
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, 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, 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

56
src/luasocket/inet.h Normal file
View File

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

28
src/luasocket/io.c Normal file
View File

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

70
src/luasocket/io.h Normal file
View File

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

319
src/luasocket/ltn12.lua Normal file
View File

@ -0,0 +1,319 @@
-----------------------------------------------------------------------------
-- 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 _M = {}
if module then -- heuristic for exporting a global package table
ltn12 = _M
end
local filter,source,sink,pump = {},{},{},{}
_M.filter = filter
_M.source = source
_M.sink = sink
_M.pump = pump
local unpack = unpack or table.unpack
local select = base.select
-- 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 = base.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

104
src/luasocket/luasocket.c Normal file
View File

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

36
src/luasocket/luasocket.h Normal file
View File

@ -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-rc1"
#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 */

466
src/luasocket/makefile Normal file
View File

@ -0,0 +1,466 @@
# 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
# 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 -g
LDFLAGS_linux= -o
LD_linux=ld
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))
A=a
SOCKET_A=socket.$(A)
MIME_A=mime.$(A)
UNIX_A=unix.$(A)
SERIAL_A=serial.$(A)
SOCKET=$(SOCKET_$(PLAT))
#------
# Settings selected for platform
#
CC=$(CC_$(PLAT))
DEF=$(DEF_$(PLAT))
AR= ar rc
RANLIB= ranlib
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_A) $(MIME_A)
$(SOCKET_A): $(SOCKET_OBJS)
$(AR) $@ $(SOCKET_OBJS)
$(RANLIB) $@
$(MIME_A): $(MIME_OBJS)
$(AR) $@ $(MIME_OBJS)
$(RANLIB) $@
all-unix: all $(UNIX_SO) $(SERIAL_SO)
$(UNIX_A): $(UNIX_OBJS)
$(AR) $@ $(UNIX_OBJS)
$(RANLIB) $@
$(SERIAL_A): $(SERIAL_OBJS)
$(AR) $@ $(SERIAL_OBJS)
$(RANLIB) $@
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_A) $(SOCKET_OBJS) $(SERIAL_OBJS)
rm -f $(MIME_A) $(UNIX_A) $(SERIAL_A) $(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

92
src/luasocket/mbox.lua Normal file
View File

@ -0,0 +1,92 @@
local _M = {}
if module then
mbox = _M
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, j = 1, 1, 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

852
src/luasocket/mime.c Normal file
View File

@ -0,0 +1,852 @@
/*=========================================================================*\
* MIME support functions
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "mime.h"
#include <string.h>
#include <ctype.h>
/*=========================================================================*\
* 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;
}

22
src/luasocket/mime.h Normal file
View File

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

89
src/luasocket/mime.lua Normal file
View File

@ -0,0 +1,89 @@
-----------------------------------------------------------------------------
-- 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 string = require("string")
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
local function format(chunk)
if chunk then
if chunk == "" then return "''"
else return string.len(chunk) end
else return "nil" end
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

454
src/luasocket/options.c Normal file
View File

@ -0,0 +1,454 @@
/*=========================================================================*\
* Common option interface
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include "options.h"
#include "inet.h"
#include <string.h>
/*=========================================================================*\
* 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);
}
// -------------------------------------------------------
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));
}

102
src/luasocket/options.h Normal file
View File

@ -0,0 +1,102 @@
#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
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);
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

28
src/luasocket/pierror.h Normal file
View File

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

214
src/luasocket/select.c Normal file
View File

@ -0,0 +1,214 @@
/*=========================================================================*\
* Select implementation
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "socket.h"
#include "timeout.h"
#include "select.h"
#include <string.h>
/*=========================================================================*\
* 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;
}
}

23
src/luasocket/select.h Normal file
View File

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

171
src/luasocket/serial.c Normal file
View File

@ -0,0 +1,171 @@
/*=========================================================================*\
* Serial stream
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include "socket.h"
#include "options.h"
#include "unix.h"
#include <string.h>
#include <sys/un.h>
/*
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;
}

256
src/luasocket/smtp.lua Normal file
View File

@ -0,0 +1,256 @@
-----------------------------------------------------------------------------
-- SMTP client support for the Lua language.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local coroutine = require("coroutine")
local string = require("string")
local math = require("math")
local os = require("os")
local socket = require("socket")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
local headers = require("socket.headers")
local mime = require("mime")
socket.smtp = {}
local _M = socket.smtp
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- timeout for connection
_M.TIMEOUT = 60
-- default server used to send e-mails
_M.SERVER = "localhost"
-- default port
_M.PORT = 25
-- domain used in HELO command and default sendmail
-- If we are under a CGI, try to get from environment
_M.DOMAIN = os.getenv("SERVER_NAME") or "localhost"
-- default time zone (means we don't know)
_M.ZONE = "-0000"
---------------------------------------------------------------------------
-- Low level SMTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function metat.__index:greet(domain)
self.try(self.tp:check("2.."))
self.try(self.tp:command("EHLO", domain or _M.DOMAIN))
return socket.skip(1, self.try(self.tp:check("2..")))
end
function metat.__index:mail(from)
self.try(self.tp:command("MAIL", "FROM:" .. from))
return self.try(self.tp:check("2.."))
end
function metat.__index:rcpt(to)
self.try(self.tp:command("RCPT", "TO:" .. to))
return self.try(self.tp:check("2.."))
end
function metat.__index:data(src, step)
self.try(self.tp:command("DATA"))
self.try(self.tp:check("3.."))
self.try(self.tp:source(src, step))
self.try(self.tp:send("\r\n.\r\n"))
return self.try(self.tp:check("2.."))
end
function metat.__index:quit()
self.try(self.tp:command("QUIT"))
return self.try(self.tp:check("2.."))
end
function metat.__index:close()
return self.tp:close()
end
function metat.__index:login(user, password)
self.try(self.tp:command("AUTH", "LOGIN"))
self.try(self.tp:check("3.."))
self.try(self.tp:send(mime.b64(user) .. "\r\n"))
self.try(self.tp:check("3.."))
self.try(self.tp:send(mime.b64(password) .. "\r\n"))
return self.try(self.tp:check("2.."))
end
function metat.__index:plain(user, password)
local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
self.try(self.tp:command("AUTH", auth))
return self.try(self.tp:check("2.."))
end
function metat.__index:auth(user, password, ext)
if not user or not password then return 1 end
if string.find(ext, "AUTH[^\n]+LOGIN") then
return self:login(user, password)
elseif string.find(ext, "AUTH[^\n]+PLAIN") then
return self:plain(user, password)
else
self.try(nil, "authentication not supported")
end
end
-- send message or throw an exception
function metat.__index:send(mailt)
self:mail(mailt.from)
if base.type(mailt.rcpt) == "table" then
for i,v in base.ipairs(mailt.rcpt) do
self:rcpt(v)
end
else
self:rcpt(mailt.rcpt)
end
self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
end
function _M.open(server, port, create)
local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT,
_M.TIMEOUT, create))
local s = base.setmetatable({tp = tp}, metat)
-- make sure tp is closed if we get an exception
s.try = socket.newtry(function()
s:close()
end)
return s
end
-- convert headers to lowercase
local function lower_headers(headers)
local lower = {}
for i,v in base.pairs(headers or lower) do
lower[string.lower(i)] = v
end
return lower
end
---------------------------------------------------------------------------
-- Multipart message source
-----------------------------------------------------------------------------
-- returns a hopefully unique mime boundary
local seqno = 0
local function newboundary()
seqno = seqno + 1
return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
math.random(0, 99999), seqno)
end
-- send_message forward declaration
local send_message
-- yield the headers all at once, it's faster
local function send_headers(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f,v in base.pairs(tosend) do
h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h
end
coroutine.yield(h)
end
-- yield multipart message body from a multipart message table
local function send_multipart(mesgt)
-- make sure we have our boundary and send headers
local bd = newboundary()
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or 'multipart/mixed'
headers['content-type'] = headers['content-type'] ..
'; boundary="' .. bd .. '"'
send_headers(headers)
-- send preamble
if mesgt.body.preamble then
coroutine.yield(mesgt.body.preamble)
coroutine.yield("\r\n")
end
-- send each part separated by a boundary
for i, m in base.ipairs(mesgt.body) do
coroutine.yield("\r\n--" .. bd .. "\r\n")
send_message(m)
end
-- send last boundary
coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
-- send epilogue
if mesgt.body.epilogue then
coroutine.yield(mesgt.body.epilogue)
coroutine.yield("\r\n")
end
end
-- yield message body from a source
local function send_source(mesgt)
-- make sure we have a content-type
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or
'text/plain; charset="iso-8859-1"'
send_headers(headers)
-- send body from source
while true do
local chunk, err = mesgt.body()
if err then coroutine.yield(nil, err)
elseif chunk then coroutine.yield(chunk)
else break end
end
end
-- yield message body from a string
local function send_string(mesgt)
-- make sure we have a content-type
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or
'text/plain; charset="iso-8859-1"'
send_headers(headers)
-- send body from string
coroutine.yield(mesgt.body)
end
-- message source
function send_message(mesgt)
if base.type(mesgt.body) == "table" then send_multipart(mesgt)
elseif base.type(mesgt.body) == "function" then send_source(mesgt)
else send_string(mesgt) end
end
-- set defaul headers
local function adjust_headers(mesgt)
local lower = lower_headers(mesgt.headers)
lower["date"] = lower["date"] or
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE)
lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
-- this can't be overriden
lower["mime-version"] = "1.0"
return lower
end
function _M.message(mesgt)
mesgt.headers = adjust_headers(mesgt)
-- create and return message source
local co = coroutine.create(function() send_message(mesgt) end)
return function()
local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end
end
---------------------------------------------------------------------------
-- High level SMTP API
-----------------------------------------------------------------------------
_M.send = socket.protect(function(mailt)
local s = _M.open(mailt.server, mailt.port, mailt.create)
local ext = s:greet(mailt.domain)
s:auth(mailt.user, mailt.password, ext)
s:send(mailt)
s:quit()
return s:close()
end)
return _M

73
src/luasocket/socket.h Normal file
View File

@ -0,0 +1,73 @@
#ifndef SOCKET_H
#define SOCKET_H
/*=========================================================================*\
* Socket compatibilization module
* LuaSocket toolkit
*
* BSD Sockets and WinSock are similar, but there are a few irritating
* differences. Also, not all *nix platforms behave the same. This module
* (and the associated usocket.h and wsocket.h) factor these differences and
* creates a interface compatible with the io.h module.
\*=========================================================================*/
#include "io.h"
/*=========================================================================*\
* Platform specific compatibilization
\*=========================================================================*/
#ifdef _WIN32
#include "wsocket.h"
#else
#include "usocket.h"
#endif
/*=========================================================================*\
* The connect and accept functions accept a timeout and their
* implementations are somewhat complicated. We chose to move
* the timeout control into this module for these functions in
* order to simplify the modules that use them.
\*=========================================================================*/
#include "timeout.h"
/* 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 */

149
src/luasocket/socket.lua Normal file
View File

@ -0,0 +1,149 @@
-----------------------------------------------------------------------------
-- LuaSocket helper module
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local math = require("math")
local socket = require("socket.core")
local _M = socket
-----------------------------------------------------------------------------
-- Exported auxiliar functions
-----------------------------------------------------------------------------
function _M.connect4(address, port, laddress, lport)
return socket.connect(address, port, laddress, lport, "inet")
end
function _M.connect6(address, port, laddress, lport)
return socket.connect(address, port, laddress, lport, "inet6")
end
function _M.bind(host, port, backlog)
if host == "*" then host = "0.0.0.0" end
local addrinfo, err = socket.dns.getaddrinfo(host);
if not addrinfo then return nil, err end
local sock, res
err = "no info on address"
for i, alt in base.ipairs(addrinfo) do
if alt.family == "inet" then
sock, err = socket.tcp4()
else
sock, err = socket.tcp6()
end
if not sock then return nil, err end
sock:setoption("reuseaddr", true)
res, err = sock:bind(alt.addr, port)
if not res then
sock:close()
else
res, err = sock:listen(backlog)
if not res then
sock:close()
else
return sock
end
end
end
return nil, err
end
_M.try = _M.newtry()
function _M.choose(table)
return function(name, opt1, opt2)
if base.type(name) ~= "string" then
name, opt1, opt2 = "default", name, opt1
end
local f = table[name or "nil"]
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
else return f(opt1, opt2) end
end
end
-----------------------------------------------------------------------------
-- Socket sources and sinks, conforming to LTN12
-----------------------------------------------------------------------------
-- create namespaces inside LuaSocket namespace
local sourcet, sinkt = {}, {}
_M.sourcet = sourcet
_M.sinkt = sinkt
_M.BLOCKSIZE = 2048
sinkt["close-when-done"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if not chunk then
sock:close()
return 1
else return sock:send(chunk) end
end
})
end
sinkt["keep-open"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if chunk then return sock:send(chunk)
else return 1 end
end
})
end
sinkt["default"] = sinkt["keep-open"]
_M.sink = _M.choose(sinkt)
sourcet["by-length"] = function(sock, length)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if length <= 0 then return nil end
local size = math.min(socket.BLOCKSIZE, length)
local chunk, err = sock:receive(size)
if err then return nil, err end
length = length - string.len(chunk)
return chunk
end
})
end
sourcet["until-closed"] = function(sock)
local done
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if done then return nil end
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
if not err then return chunk
elseif err == "closed" then
sock:close()
done = 1
return partial
else return nil, err end
end
})
end
sourcet["default"] = sourcet["until-closed"]
_M.source = _M.choose(sourcet)
return _M

471
src/luasocket/tcp.c Normal file
View File

@ -0,0 +1,471 @@
/*=========================================================================*\
* TCP object
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include "socket.h"
#include "inet.h"
#include "options.h"
#include "tcp.h"
#include <string.h>
/*=========================================================================*\
* 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},
{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;
}

43
src/luasocket/tcp.h Normal file
View File

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

226
src/luasocket/timeout.c Normal file
View File

@ -0,0 +1,226 @@
/*=========================================================================*\
* Timeout management functions
* LuaSocket toolkit
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include "timeout.h"
#include <stdio.h>
#include <limits.h>
#include <float.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <time.h>
#include <sys/time.h>
#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

40
src/luasocket/timeout.h Normal file
View File

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

134
src/luasocket/tp.lua Normal file
View File

@ -0,0 +1,134 @@
-----------------------------------------------------------------------------
-- Unified SMTP/FTP subsystem
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local socket = require("socket")
local ltn12 = require("ltn12")
socket.tp = {}
local _M = socket.tp
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
_M.TIMEOUT = 60
-----------------------------------------------------------------------------
-- Implementation
-----------------------------------------------------------------------------
-- gets server reply (works for SMTP and FTP)
local function get_reply(c)
local code, current, sep
local line, err = c:receive()
local reply = line
if err then return nil, err end
code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
if not code then return nil, "invalid server reply" end
if sep == "-" then -- reply is multiline
repeat
line, err = c:receive()
if err then return nil, err end
current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
reply = reply .. "\n" .. line
-- reply ends with same code
until code == current and sep == " "
end
return code, reply
end
-- metatable for sock object
local metat = { __index = {} }
function metat.__index:getpeername()
return self.c:getpeername()
end
function metat.__index:getsockname()
return self.c:getpeername()
end
function metat.__index:check(ok)
local code, reply = get_reply(self.c)
if not code then return nil, reply end
if base.type(ok) ~= "function" then
if base.type(ok) == "table" then
for i, v in base.ipairs(ok) do
if string.find(code, v) then
return base.tonumber(code), reply
end
end
return nil, reply
else
if string.find(code, ok) then return base.tonumber(code), reply
else return nil, reply end
end
else return ok(base.tonumber(code), reply) end
end
function metat.__index:command(cmd, arg)
cmd = string.upper(cmd)
if arg then
return self.c:send(cmd .. " " .. arg.. "\r\n")
else
return self.c:send(cmd .. "\r\n")
end
end
function metat.__index:sink(snk, pat)
local chunk, err = self.c:receive(pat)
return snk(chunk, err)
end
function metat.__index:send(data)
return self.c:send(data)
end
function metat.__index:receive(pat)
return self.c:receive(pat)
end
function metat.__index:getfd()
return self.c:getfd()
end
function metat.__index:dirty()
return self.c:dirty()
end
function metat.__index:getcontrol()
return self.c
end
function metat.__index:source(source, step)
local sink = socket.sink("keep-open", self.c)
local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
return ret, err
end
-- closes the underlying c
function metat.__index:close()
self.c:close()
return 1
end
-- connect with server and return c object
function _M.connect(host, port, timeout, create)
local c, e = (create or socket.tcp)()
if not c then return nil, e end
c:settimeout(timeout or _M.TIMEOUT)
local r, e = c:connect(host, port)
if not r then
c:close()
return nil, e
end
return base.setmetatable({c = c}, metat)
end
return _M

488
src/luasocket/udp.c Normal file
View File

@ -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 <string.h>
#include <stdlib.h>
/* 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, 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, 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);
}

39
src/luasocket/udp.h Normal file
View File

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

69
src/luasocket/unix.c Normal file
View File

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

26
src/luasocket/unix.h Normal file
View File

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

405
src/luasocket/unixdgram.c Normal file
View File

@ -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 <string.h>
#include <stdlib.h>
#include <sys/un.h>
#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;
}
}

28
src/luasocket/unixdgram.h Normal file
View File

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

355
src/luasocket/unixstream.c Normal file
View File

@ -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 <string.h>
#include <sys/un.h>
/*=========================================================================*\
* 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;
}
}

View File

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

331
src/luasocket/url.lua Normal file
View File

@ -0,0 +1,331 @@
-----------------------------------------------------------------------------
-- URI parsing, composition and relative URL resolution
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local base = _G
local table = require("table")
local socket = require("socket")
socket.url = {}
local _M = socket.url
-----------------------------------------------------------------------------
-- Module version
-----------------------------------------------------------------------------
_M._VERSION = "URL 1.0.3"
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function _M.escape(s)
return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
return string.format("%%%02x", string.byte(c))
end))
end
-----------------------------------------------------------------------------
-- Protects a path segment, to prevent it from interfering with the
-- url parsing.
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
local function make_set(t)
local s = {}
for i,v in base.ipairs(t) do
s[t[i]] = 1
end
return s
end
-- these are allowed within a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",",
}
local function protect_segment(s)
return string.gsub(s, "([^A-Za-z0-9_])", function (c)
if segment_set[c] then return c
else return string.format("%%%02X", string.byte(c)) end
end)
end
-----------------------------------------------------------------------------
-- Unencodes a escaped hexadecimal string into its binary representation
-- Input
-- s: escaped hexadecimal string to be unencoded
-- Returns
-- unescaped binary representation of escaped hexadecimal binary
-----------------------------------------------------------------------------
function _M.unescape(s)
return (string.gsub(s, "%%(%x%x)", function(hex)
return string.char(base.tonumber(hex, 16))
end))
end
-----------------------------------------------------------------------------
-- Removes '..' and '.' components appropriately from a path.
-- Input
-- path
-- Returns
-- dot-normalized path
local function remove_dot_components(path)
local marker = string.char(1)
repeat
local was = path
path = path:gsub('//', '/'..marker..'/', 1)
until path == was
repeat
local was = path
path = path:gsub('/%./', '/', 1)
until path == was
repeat
local was = path
path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1)
until path == was
path = path:gsub('[^/]+/%.%./*$', '')
path = path:gsub('/%.%.$', '/')
path = path:gsub('/%.$', '/')
path = path:gsub('^/%.%./', '/')
path = path:gsub(marker, '')
return path
end
-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
-- base_path
-- relative_path
-- Returns
-- corresponding absolute path
-----------------------------------------------------------------------------
local function absolute_path(base_path, relative_path)
if string.sub(relative_path, 1, 1) == "/" then
return remove_dot_components(relative_path) end
base_path = base_path:gsub("[^/]*$", "")
if not base_path:find'/$' then base_path = base_path .. '/' end
local path = base_path .. relative_path
path = remove_dot_components(path)
return path
end
-----------------------------------------------------------------------------
-- Parses a url and returns a table with all its parts according to RFC 2396
-- The following grammar describes the names given to the URL parts
-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
-- <authority> ::= <userinfo>@<host>:<port>
-- <userinfo> ::= <user>[:<password>]
-- <path> :: = {<segment>/}<segment>
-- 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 {/<path>} is considered part of <path>
-----------------------------------------------------------------------------
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

454
src/luasocket/usocket.c Normal file
View File

@ -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 <string.h>
#include <signal.h>
/*-------------------------------------------------------------------------*\
* Wait for readable/writable/connected socket with timeout
\*-------------------------------------------------------------------------*/
#ifndef SOCKET_SELECT
#include <sys/poll.h>
#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 gai_strerror(err);
}
}

59
src/luasocket/usocket.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef USOCKET_H
#define USOCKET_H
/*=========================================================================*\
* Socket compatibilization module for Unix
* LuaSocket toolkit
\*=========================================================================*/
/*=========================================================================*\
* BSD include files
\*=========================================================================*/
/* error codes */
#include <errno.h>
/* close function */
#include <unistd.h>
/* fnctnl function and associated constants */
#include <fcntl.h>
/* struct sockaddr */
#include <sys/types.h>
/* socket function */
#include <sys/socket.h>
/* struct timeval */
#include <sys/time.h>
/* gethostbyname and gethostbyaddr functions */
#include <netdb.h>
/* sigpipe handling */
#include <signal.h>
/* IP stuff*/
#include <netinet/in.h>
#include <arpa/inet.h>
/* TCP options (nagle algorithm disable) */
#include <netinet/tcp.h>
#include <net/if.h>
#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 */

434
src/luasocket/wsocket.c Executable file
View File

@ -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 <string.h>
#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 gai_strerror(err);
}
}

33
src/luasocket/wsocket.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef WSOCKET_H
#define WSOCKET_H
/*=========================================================================*\
* Socket compatibilization module for Win32
* LuaSocket toolkit
\*=========================================================================*/
/*=========================================================================*\
* WinSock include files
\*=========================================================================*/
#include <winsock2.h>
#include <ws2tcpip.h>
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 */