diff --git a/src/inet.h b/src/inet.h new file mode 100644 index 0000000..3b0453e --- /dev/null +++ b/src/inet.h @@ -0,0 +1,36 @@ +/*=========================================================================*\ +* Internet domain class +* RCS ID: $Id$ +\*=========================================================================*/ +#ifndef INET_H_ +#define INET_H_ + +#include +#include "lssock.h" + +/* class name */ +#define INET_CLASS "luasocket(inet)" + +/*-------------------------------------------------------------------------*\ +* Socket fields +\*-------------------------------------------------------------------------*/ +#define INET_FIELDS SOCK_FIELDS + +/*-------------------------------------------------------------------------*\ +* Socket structure +\*-------------------------------------------------------------------------*/ +typedef t_sock t_inet; +typedef t_inet *p_inet; + +/*-------------------------------------------------------------------------*\ +* Exported functions +\*-------------------------------------------------------------------------*/ +void inet_open(lua_State *L); +void inet_construct(lua_State *L, p_inet inet); +void inet_inherit(lua_State *L, cchar *lsclass); + +cchar *inet_tryconnect(p_sock sock, cchar *address, ushort); +cchar *inet_trybind(p_sock sock, cchar *address, ushort); +cchar *inet_trysocket(p_inet inet, int type); + +#endif /* INET_H_ */ diff --git a/src/select.h b/src/select.h new file mode 100644 index 0000000..c3267ad --- /dev/null +++ b/src/select.h @@ -0,0 +1,7 @@ +#ifndef SLCT_H_ +#define SLCT_H_ + +void select_addclass(lua_State *L, cchar *lsclass); +void select_open(lua_State *L); + +#endif diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..c9dee20 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,18 @@ +#ifndef SOCK_H_ +#define SOCK_H_ + +#include +#include "lsfd.h" + +#define SOCK_CLASS "luasocket(sock)" + +#define SOCK_FIELDS FD_FIELDS + +typedef t_fd t_sock; +typedef t_sock *p_sock; + +void sock_open(lua_State *L); +void sock_construct(lua_State *L, p_sock sock); +void sock_inherit(lua_State *L, cchar *lsclass); + +#endif /* SOCK_H_ */ diff --git a/src/timeout.c b/src/timeout.c new file mode 100644 index 0000000..266a86e --- /dev/null +++ b/src/timeout.c @@ -0,0 +1,160 @@ +/*=========================================================================*\ +* Timeout management functions +\*=========================================================================*/ +#include +#include + +#include "lspriv.h" +#include "lstm.h" + +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +#ifdef _DEBUG +static int tm_lua_time(lua_State *L); +static int tm_lua_sleep(lua_State *L); +#endif + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sets timeout limits +* Input +* tm: timeout control structure +* mode: block or return timeout +* value: timeout value in miliseconds +\*-------------------------------------------------------------------------*/ +void tm_set(p_tm tm, int tm_block, int tm_return) +{ + tm->tm_block = tm_block; + tm->tm_return = tm_return; +} + +/*-------------------------------------------------------------------------*\ +* Returns timeout limits +* Input +* tm: timeout control structure +* mode: block or return timeout +* value: timeout value in miliseconds +\*-------------------------------------------------------------------------*/ +void tm_get(p_tm tm, int *tm_block, int *tm_return) +{ + if (tm_block) *tm_block = tm->tm_block; + if (tm_return) *tm_return = tm->tm_return; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the current io operation +* an IO write operation. +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +int tm_getremaining(p_tm tm) +{ + /* no timeout */ + if (tm->tm_block < 0 && tm->tm_return < 0) + return -1; + /* there is no block timeout, we use the return timeout */ + else if (tm->tm_block < 0) + return MAX(tm->tm_return - tm_gettime() + tm->tm_start, 0); + /* there is no return timeout, we use the block timeout */ + else if (tm->tm_return < 0) + return tm->tm_block; + /* both timeouts are specified */ + else return MIN(tm->tm_block, + MAX(tm->tm_return - tm_gettime() + tm->tm_start, 0)); +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in sock structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +void tm_markstart(p_tm tm) +{ + tm->tm_start = tm_gettime(); + tm->tm_end = tm->tm_start; +} + +/*-------------------------------------------------------------------------*\ +* Returns the length of the operation in ms +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +int tm_getelapsed(p_tm tm) +{ + return tm->tm_end - tm->tm_start; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in ms, relative to system startup. +* Returns +* time in ms. +\*-------------------------------------------------------------------------*/ +#ifdef WIN32 +int tm_gettime(void) +{ + return GetTickCount(); +} +#else +int tm_gettime(void) +{ + struct tms t; + return (times(&t)*1000)/CLK_TCK; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +void tm_open(lua_State *L) +{ + (void) L; +#ifdef _DEBUG + lua_pushcfunction(L, tm_lua_time); + lua_setglobal(L, "_time"); + lua_pushcfunction(L, tm_lua_sleep); + lua_setglobal(L, "_sleep"); +#endif +} + +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +#ifdef _DEBUG +static int tm_lua_time(lua_State *L) +{ + lua_pushnumber(L, tm_gettime()/1000.0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +int tm_lua_sleep(lua_State *L) +{ + double n = luaL_check_number(L, 1); +#ifdef WIN32 + Sleep(n*1000); +#else + sleep(n); +#endif + return 0; +} +#endif diff --git a/src/timeout.h b/src/timeout.h new file mode 100644 index 0000000..af7e591 --- /dev/null +++ b/src/timeout.h @@ -0,0 +1,20 @@ +#ifndef _TM_H +#define _TM_H + +typedef struct t_tm_tag { + int tm_return; + int tm_block; + int tm_start; + int tm_end; +} t_tm; +typedef t_tm *p_tm; + +void tm_set(p_tm tm, int tm_block, int tm_return); +int tm_getremaining(p_tm tm); +int tm_getelapsed(p_tm tm); +int tm_gettime(void); +void tm_get(p_tm tm, int *tm_block, int *tm_return); +void tm_markstart(p_tm tm); +void tm_open(lua_State *L); + +#endif diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 0000000..0dc0df8 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,287 @@ +/*=========================================================================*\ +* UDP socket object implementation (inherits from sock and inet) +\*=========================================================================*/ +#include + +#include +#include + +#include "lsinet.h" +#include "lsudp.h" +#include "lscompat.h" +#include "lsselect.h" + +/*=========================================================================*\ +* Internal function prototypes. +\*=========================================================================*/ +static int udp_lua_send(lua_State *L); +static int udp_lua_sendto(lua_State *L); +static int udp_lua_receive(lua_State *L); +static int udp_lua_receivefrom(lua_State *L); +static int udp_lua_setpeername(lua_State *L); +static int udp_lua_setsockname(lua_State *L); + +static int udp_global_udpsocket(lua_State *L); + +static struct luaL_reg funcs[] = { + {"send", udp_lua_send}, + {"sendto", udp_lua_sendto}, + {"receive", udp_lua_receive}, + {"receivefrom", udp_lua_receivefrom}, + {"setpeername", udp_lua_setpeername}, + {"setsockname", udp_lua_setsockname}, +}; + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +void udp_open(lua_State *L) +{ + unsigned int i; + priv_newclass(L, UDP_CLASS); + udp_inherit(L, UDP_CLASS); + /* declare global functions */ + lua_pushcfunction(L, udp_global_udpsocket); + lua_setglobal(L, "udpsocket"); + for (i = 0; i < sizeof(funcs)/sizeof(funcs[0]); i++) + priv_newglobalmethod(L, funcs[i].name); + /* make class selectable */ + select_addclass(L, UDP_CLASS); +} + +/*-------------------------------------------------------------------------*\ +* Hook object methods to methods table. +\*-------------------------------------------------------------------------*/ +void udp_inherit(lua_State *L, cchar *lsclass) +{ + unsigned int i; + inet_inherit(L, lsclass); + for (i = 0; i < sizeof(funcs)/sizeof(funcs[0]); i++) { + lua_pushcfunction(L, funcs[i].func); + priv_setmethod(L, lsclass, funcs[i].name); + } +} + +/*-------------------------------------------------------------------------*\ +* Initializes socket structure +\*-------------------------------------------------------------------------*/ +void udp_construct(lua_State *L, p_udp udp) +{ + inet_construct(L, (p_inet) udp); + udp->udp_connected = 0; +} + +/*-------------------------------------------------------------------------*\ +* Creates a socket structure and initializes it. A socket object is +* left in the Lua stack. +* Returns +* pointer to allocated structure +\*-------------------------------------------------------------------------*/ +p_udp udp_push(lua_State *L) +{ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + priv_setclass(L, UDP_CLASS); + udp_construct(L, udp); + return udp; +} + +/*=========================================================================*\ +* Socket table constructors +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a udp socket object and returns it to the Lua script. +* Lua Input: [options] +* options: socket options table +* Lua Returns +* On success: udp socket +* On error: nil, followed by an error message +\*-------------------------------------------------------------------------*/ +static int udp_global_udpsocket(lua_State *L) +{ + int oldtop = lua_gettop(L); + p_udp udp = udp_push(L); + cchar *err = inet_trysocket((p_inet) udp, SOCK_DGRAM); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + if (oldtop < 1) return 1; + err = compat_trysetoptions(L, udp->fd); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + return 1; +} + +/*=========================================================================*\ +* Socket table methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +* Lua Input: sock [, wanted] +* sock: client socket created by the connect function +* wanted: the number of bytes expected (default: LUASOCKET_UDPBUFFERSIZE) +* Lua Returns +* On success: datagram received +* On error: nil, followed by an error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_receive(lua_State *L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + unsigned char buffer[UDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_opt_number(L, 2, sizeof(buffer)); + int err; + p_tm tm = &udp->base_tm; + wanted = MIN(wanted, sizeof(buffer)); + tm_markstart(tm); + err = compat_recv(udp->fd, buffer, wanted, &got, tm_getremaining(tm)); + if (err == PRIV_CLOSED) err = PRIV_REFUSED; + if (err != PRIV_DONE) lua_pushnil(L); + else lua_pushlstring(L, buffer, got); + priv_pusherror(L, err); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Receives a datagram from a UDP socket +* Lua Input: sock [, wanted] +* sock: client socket created by the connect function +* wanted: the number of bytes expected (default: LUASOCKET_UDPBUFFERSIZE) +* Lua Returns +* On success: datagram received, ip and port of sender +* On error: nil, followed by an error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_receivefrom(lua_State *L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + p_tm tm = &udp->base_tm; + struct sockaddr_in peer; + size_t peer_len = sizeof(peer); + unsigned char buffer[UDP_DATAGRAMSIZE]; + size_t wanted = (size_t) luaL_opt_number(L, 2, sizeof(buffer)); + size_t got; + int err; + if (udp->udp_connected) lua_error(L, "receivefrom on connected socket"); + tm_markstart(tm); + wanted = MIN(wanted, sizeof(buffer)); + err = compat_recvfrom(udp->fd, buffer, wanted, &got, tm_getremaining(tm), + (SA *) &peer, &peer_len); + if (err == PRIV_CLOSED) err = PRIV_REFUSED; + if (err == PRIV_DONE) { + lua_pushlstring(L, buffer, got); + lua_pushstring(L, inet_ntoa(peer.sin_addr)); + lua_pushnumber(L, ntohs(peer.sin_port)); + return 3; + } else { + lua_pushnil(L); + priv_pusherror(L, err); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Send data through a connected UDP socket +* Lua Input: sock, data +* sock: udp socket +* data: data to be sent +* Lua Returns +* On success: nil, followed by the total number of bytes sent +* On error: error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_send(lua_State *L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + p_tm tm = &udp->base_tm; + size_t wanted, sent = 0; + int err; + cchar *data = luaL_check_lstr(L, 2, &wanted); + if (!udp->udp_connected) lua_error(L, "send on unconnected socket"); + tm_markstart(tm); + err = compat_send(udp->fd, data, wanted, &sent, tm_getremaining(tm)); + priv_pusherror(L, err == PRIV_CLOSED ? PRIV_REFUSED : err); + lua_pushnumber(L, sent); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Send data through a unconnected UDP socket +* Lua Input: sock, data, ip, port +* sock: udp socket +* data: data to be sent +* ip: ip address of target +* port: port in target +* Lua Returns +* On success: nil, followed by the total number of bytes sent +* On error: error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_sendto(lua_State *L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + size_t wanted, sent = 0; + cchar *data = luaL_check_lstr(L, 2, &wanted); + cchar *ip = luaL_check_string(L, 3); + ushort port = (ushort) luaL_check_number(L, 4); + p_tm tm = &udp->base_tm; + struct sockaddr_in peer; + int err; + if (udp->udp_connected) lua_error(L, "sendto on connected socket"); + memset(&peer, 0, sizeof(peer)); + if (!inet_aton(ip, &peer.sin_addr)) lua_error(L, "invalid ip address"); + peer.sin_family = AF_INET; + peer.sin_port = htons(port); + tm_markstart(tm); + err = compat_sendto(udp->fd, data, wanted, &sent, tm_getremaining(tm), + (SA *) &peer, sizeof(peer)); + priv_pusherror(L, err == PRIV_CLOSED ? PRIV_REFUSED : err); + lua_pushnumber(L, sent); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Associates a local address to an UDP socket +* Lua Input: address, port +* address: host name or ip address to bind to +* port: port to bind to +* Lua Returns +* On success: nil +* On error: error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_setsockname(lua_State * L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + cchar *address = luaL_check_string(L, 2); + ushort port = (ushort) luaL_check_number(L, 3); + cchar *err = inet_trybind((p_inet) udp, address, port); + if (err) lua_pushstring(L, err); + else lua_pushnil(L); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sets a peer for a UDP socket +* Lua Input: address, port +* address: remote host name +* port: remote host port +* Lua Returns +* On success: nil +* On error: error message +\*-------------------------------------------------------------------------*/ +static int udp_lua_setpeername(lua_State *L) +{ + p_udp udp = (p_udp) lua_touserdata(L, 1); + cchar *address = luaL_check_string(L, 2); + ushort port = (ushort) luaL_check_number(L, 3); + cchar *err = inet_tryconnect((p_inet) udp, address, port); + if (!err) { + udp->udp_connected = 1; + lua_pushnil(L); + } else lua_pushstring(L, err); + return 1; +} + diff --git a/src/udp.h b/src/udp.h new file mode 100644 index 0000000..3c82c29 --- /dev/null +++ b/src/udp.h @@ -0,0 +1,24 @@ +#ifndef UDP_H_ +#define UDP_H_ + +#include "lsinet.h" + +#define UDP_CLASS "luasocket(UDP socket)" + +#define UDP_DATAGRAMSIZE 576 + +#define UDP_FIELDS \ + INET_FIELDS; \ + int udp_connected + +typedef struct t_udp_tag { + UDP_FIELDS; +} t_udp; +typedef t_udp *p_udp; + +void udp_inherit(lua_State *L, cchar *lsclass); +void udp_construct(lua_State *L, p_udp udp); +void udp_open(lua_State *L); +p_udp udp_push(lua_State *L); + +#endif