changeset 192:1c2788f9f55f

Luae added
author David Demelier <markand@malikania.fr>
date Wed, 27 Nov 2013 11:26:26 +0100
parents 5f75779cc7eb
children 258087829c66
files C++/Lua/LuaSocket.cpp C++/Lua/LuaSocket.h Luae.cpp Luae.h
diffstat 4 files changed, 1397 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Lua/LuaSocket.cpp	Wed Nov 27 10:53:20 2013 +0100
+++ b/C++/Lua/LuaSocket.cpp	Wed Nov 27 11:26:26 2013 +0100
@@ -1,12 +1,12 @@
 /*
- * LuaSocket.cpp -- portable Lua socket wrappers
- *
- * Copyright (c) 2013, David Demelier <markand@malikania.fr>
- *
+ * LuaSocket.cpp -- Lua bindings for Sockets
+ * 
+ * Copyright (c) 2013 David Demelier <markand@malikania.fr>
+ * 
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- *
+ * 
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
@@ -16,70 +16,396 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define SC_TYPE			"Socket"
-#define ADDR_TYPE		"SocketAddress"
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+#include <unordered_map>
 
-#define GET_SC(L, idx)		*static_cast<Socket **>(luaL_checkudata(L, idx, SC_TYPE))
-#define GET_ADDR(L, idx)	*static_cast<SocketAddress **>(luaL_checkudata(L, idx, ADDR_TYPE))
+#include <Socket.h>
+#include <SocketAddress.h>
+#include <SocketListener.h>
 
-#include <cstring>
-#include <cerrno>
-
+#include "Luae.h"
 #include "LuaSocket.h"
 
-#include "Socket.h"
+#define SOCKET_TYPE		"Socket"
+#define ADDRESS_TYPE		"SocketAddress"
+#define LISTENER_TYPE		"SocketListener"
+
+namespace irccd {
+
+namespace {
+
+/* ---------------------------------------------------------
+ * Enumerations
+ * --------------------------------------------------------- */
+
+typedef std::unordered_map<std::string, int> EnumMap;
+
+EnumMap createSockFamilies()
+{
+	EnumMap map;
 
-static int l_new(lua_State *L)
+	map["Inet"]	= AF_INET;
+	map["Inet6"]	= AF_INET6;
+
+#if !defined(_WIN32)
+	map["Unix"]	= AF_UNIX;
+#endif
+
+	return map;
+}
+
+EnumMap createSockTypes()
+{
+	EnumMap map;
+
+	map["Stream"]	= SOCK_STREAM;
+	map["Datagram"]	= SOCK_DGRAM;
+
+	return map;
+}
+
+EnumMap createSockProtocols()
 {
-	int domain	= luaL_checkinteger(L, 1);
-	int type	= luaL_checkinteger(L, 2);
-	int protocol	= 0;
-	Socket *s, **ptr;
+	EnumMap map;
+
+	map["Tcp"]	= IPPROTO_TCP;
+	map["Udp"]	= IPPROTO_UDP;
+	map["IPv4"]	= IPPROTO_IP;
+	map["IPv6"]	= IPPROTO_IPV6;
+
+	return map;
+}
+
+EnumMap sockFamilies	= createSockFamilies();
+EnumMap sockTypes	= createSockTypes();
+EnumMap sockProtocols	= createSockProtocols();
+
+/* ---------------------------------------------------------
+ * Socket options
+ * --------------------------------------------------------- */
+
+/*
+ * Windows setsockopt() says that options which takes a bool may use
+ * the bool type. On some Unix systems (including FreeBSD), passing
+ * a bool to SO_REUSEADDR for example, will result in a EINVAL.
+ */
+#if defined(_WIN32)
+
+typedef bool 		OptionBool;
+typedef int		OptionInteger;
+
+#else
+
+typedef int 		OptionBool;
+typedef int		OptionInteger;
+
+#endif
+
+enum class ArgType
+{
+	Invalid,
+	Boolean,
+	Integer
+};
+
+struct Option
+{
+	int		m_level;
+	int		m_optname;
+	ArgType		m_argtype;
+
+	Option()
+		: m_level(0)
+		, m_optname(0)
+		, m_argtype(ArgType::Invalid)
+	{
+	}
+
+	Option(int level, int optname, ArgType type)
+		: m_level(level)
+		, m_optname(optname)
+		, m_argtype(type)
+	{
+	}
+};
+
+typedef std::unordered_map<std::string,
+	std::unordered_map<std::string, Option>
+> OptionMap;
+
+/*
+ * Map here the socket options from the C side to Lua. It's very
+ * close to the standard API.
+ *
+ * This map is used to retrieve which argument we must pass to the
+ * Socket::set function. It also setup the enumerations to be bound
+ * as tables.
+ */
+OptionMap mapOfOptions()
+{
+	OptionMap map;
+
+	/*
+	 * Standard sockets options SOL_SOCKET
+	 */
 
-	if (lua_gettop(L) >= 3)
-		protocol = luaL_checkinteger(L, 3);
+#if defined(SO_REUSEADDR)
+	map["socket"]["reuse-address"]	= Option(SOL_SOCKET,
+						 SO_REUSEADDR,
+						 ArgType::Boolean);
+#endif
+#if defined(SO_BROADCAST)
+	map["socket"]["broadcast"]	= Option(SOL_SOCKET,
+						 SO_BROADCAST,
+						 ArgType::Boolean);
+#endif
+#if defined(SO_DEBUG)
+	map["socket"]["debug"]		= Option(SOL_SOCKET,
+						 SO_DEBUG,
+						 ArgType::Boolean);
+#endif
+#if defined(SO_KEEPALIVE)
+	map["socket"]["keep-alive"]	= Option(SOL_SOCKET,
+						 SO_KEEPALIVE,
+						 ArgType::Boolean);
+#endif
+#if defined(SO_RCVBUF)
+	map["socket"]["receive-buffer"]	= Option(SOL_SOCKET,
+						 SO_RCVBUF,
+						 ArgType::Integer);
+#endif
+
+	/*
+	 * TCP socket options
+	 */
+
+#if defined(TCP_NODELAY)
+	map["tcp"]["no-delay"]		= Option(IPPROTO_TCP,
+						 TCP_NODELAY,
+						 ArgType::Boolean);
+#endif
+
+	/*
+	 * IPv6 options
+	 */
+
+#if defined(IPV6_V6ONLY)
+	map["ipv6"]["v6only"]		= Option(IPPROTO_IPV6,
+						 IPV6_V6ONLY,
+						 ArgType::Boolean);
+#endif
+
+	return map;
+}
+
+OptionMap options = mapOfOptions();
+
+/* ---------------------------------------------------------
+ * Socket functions
+ * --------------------------------------------------------- */
+
+void mapToTable(lua_State *L,
+		const EnumMap &map,
+		int index,
+		const std::string &name)
+{
+	lua_createtable(L, 0, 0);
+
+	for (auto p : map) {
+		lua_pushinteger(L, p.second);
+		lua_setfield(L, -2, p.first.c_str());
+	}
+
+	if (index < 0)
+		-- index;
 
-	try
-	{
-		s = new Socket(domain, type, protocol);
+	lua_setfield(L, index, name.c_str());
+}
+
+int genericReceive(lua_State *L, bool udp)
+{
+	Socket *s	= Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	int requested	= luaL_checkinteger(L, 2);
+	SocketAddress *sa;
+	int nret;
+	char *data;
+	size_t nbread;
+
+	/*
+	 * This parameter only needed for UDP sockets
+	 */
+	if (udp)
+		sa = Luae::toType<SocketAddress *>(L, 2, ADDRESS_TYPE);
+
+	/*
+	 * Allocate a temporarly buffer for receiveing the data.
+	 */
+	data = static_cast<char *>(std::malloc(requested));
+	if (data == nullptr) {
+		lua_pushnil(L);
+		lua_pushstring(L, std::strerror(errno));
+
+		return 2;
 	}
-	catch (SocketError error)
-	{
+
+	try {
+		if (!udp)
+			nbread = s->recv(data, requested);
+		else
+			nbread = s->recvfrom(data, requested, *sa);
+
+		lua_pushlstring(L, data, nbread);
+
+		nret = 1;
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		nret = 2;
+	}
+
+	std::free(data);
+
+	return nret;
+}
+
+int genericSend(lua_State *L, bool udp)
+{
+	Socket *s	= Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	const char *msg	= luaL_checkstring(L, 2);
+	SocketAddress *sa;
+	long nbsent;
+
+	/*
+	 * This parameter only needed for UDP sockets
+	 */
+	if (udp)
+		sa = Luae::toType<SocketAddress *>(L, 3, ADDRESS_TYPE);
+
+	try {
+		if (!udp)
+			nbsent = s->send(msg, strlen(msg));
+		else
+			nbsent = s->sendto(msg, strlen(msg), *sa);
+	} catch (SocketError error) {
 		lua_pushnil(L);
 		lua_pushstring(L, error.what());
 
 		return 2;
 	}
 
-	ptr = static_cast<Socket **>(lua_newuserdata(L, sizeof (void *)));
-	*ptr = s;
-	luaL_setmetatable(L, SC_TYPE);
+	lua_pushnumber(L, nbsent);
+
+	return 1;
+}
+
+int socketNew(lua_State *L)
+{
+	int domain;
+	int type = SOCK_STREAM;
+	int protocol = 0;
+
+	// Domain is the only one mandatory
+	domain = luaL_checkinteger(L, 1);
+
+	if (lua_gettop(L) >= 2)
+		type = luaL_checkinteger(L, 2);
+	if (lua_gettop(L) >= 3)
+		protocol = luaL_checkinteger(L, 3);
+
+	try {
+		new (L, SOCKET_TYPE) Socket(domain, type, protocol);
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		return 2;
+	}
+
+	return 1;
+}
+
+int socketBlockMode(lua_State *L)
+{
+	Socket *s	= Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	bool mode	= lua_toboolean(L, 2);
+
+	try {
+		s->blockMode(mode);
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		return 2;
+	}
+
+	lua_pushboolean(L, true);
 
 	return 1;
 }
 
-static int l_blockMode(lua_State *L)
+int socketBind(lua_State *L)
 {
-	Socket *s	= GET_SC(L, 1);
-	bool mode	= lua_toboolean(L, 2);
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	SocketAddress *a = Luae::toType<SocketAddress *>(L, 2, ADDRESS_TYPE);
+
+	/*
+	 * Get nil + error message for chained expression like:
+	 * s:bind(address.bindInet { port = 80, family = 1 })
+	 */
+	if (lua_type(L, 1) == LUA_TNIL && lua_type(L, 2) == LUA_TSTRING) {
+		lua_pushnil(L);
+		lua_pushvalue(L, 2);
+
+		return 2;
+	}
 
-	s->blockMode(mode);
-	
+	try {
+		s->bind(*a);
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		return 2;
+	}
+
+	lua_pushboolean(L, true);
+
+	return 1;
+}
+
+int socketClose(lua_State *L)
+{
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+
+	s->close();
+
 	return 0;
 }
 
-static int l_bind(lua_State *L)
+int socketConnect(lua_State *L)
 {
-	Socket *s	= GET_SC(L, 1);
-	SocketAddress *a = GET_ADDR(L, 2);
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	SocketAddress *a = Luae::toType<SocketAddress *>(L, 2, ADDRESS_TYPE);
 
-	try
-	{
-		s->bind(*a);
+	/*
+	 * Get nil + error message for chained expression like:
+	 * s:bind(address.bindInet { port = 80, family = 1 })
+	 */
+	if (lua_type(L, 1) == LUA_TNIL && lua_type(L, 2) == LUA_TSTRING) {
+		lua_pushnil(L);
+		lua_pushvalue(L, 2);
+
+		return 2;
 	}
-	catch (SocketError error)
-	{
-		lua_pushboolean(L, false);
+
+	try {
+		s->connect(*a);
+	} catch (SocketError error) {
+		lua_pushnil(L);
 		lua_pushstring(L, error.what());
 
 		return 2;
@@ -90,18 +416,39 @@
 	return 1;
 }
 
-static int l_connect(lua_State *L)
+int socketAccept(lua_State *L)
 {
-	Socket *s	= GET_SC(L, 1);
-	SocketAddress *a = GET_ADDR(L, 2);
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	Socket client;
+	SocketAddress info;
+
+	try {
+		client = s->accept(info);
+		new (L, SOCKET_TYPE) Socket(client);
+		new (L, ADDRESS_TYPE) SocketAddress(info);
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
 
-	try
-	{
-		s->connect(*a);
+		return 3;
 	}
-	catch (SocketError error)
-	{
-		lua_pushboolean(L, false);
+
+	return 2;
+}
+
+int socketListen(lua_State *L)
+{
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	int max = 64;
+
+	if (lua_gettop(L) >= 2)
+		max = luaL_checkinteger(L, 2);
+
+	try {
+		s->listen(max);
+	} catch (SocketError error) {
+		lua_pushnil(L);
 		lua_pushstring(L, error.what());
 
 		return 2;
@@ -112,81 +459,377 @@
 	return 1;
 }
 
-static int l_accept(lua_State *L)
+int socketReceive(lua_State *L)
+{
+	return genericReceive(L, false);
+}
+
+int socketReceiveFrom(lua_State *L)
+{
+	return genericReceive(L, true);
+}
+
+int socketSend(lua_State *L)
+{
+	return genericSend(L, false);
+}
+
+int socketSendTo(lua_State *L)
+{
+	return genericSend(L, true);
+}
+
+int socketSet(lua_State *L)
 {
-	Socket *s = GET_SC(L, 1);
-	Socket *c, **ptr;
+	Socket *s	= Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	const char *lvl	= luaL_checkstring(L, 2);
+	const char *nm	= luaL_checkstring(L, 3);
+	int nret;
+
+	try {
+		OptionBool bvalue;
+		OptionInteger ivalue;
+		void *ptr = nullptr;
+		size_t size;
+
+		auto o = options.at(lvl).at(nm);
+
+		switch (o.m_argtype) {
+		case ArgType::Boolean:
+			bvalue = lua_toboolean(L, 4);
+			ptr = static_cast<void *>(&bvalue);
+			size = sizeof (OptionBool);
+			break;
+		case ArgType::Integer:
+			ivalue = luaL_checkinteger(L, 4);
+			ptr = static_cast<void *>(&ivalue);
+			size = sizeof (OptionInteger);
+			break;
+		default:
+			break;
+		}
+
+		if (ptr != nullptr)
+			s->set(o.m_level, o.m_optname, ptr, size);
+		lua_pushboolean(L, true);
+
+		nret = 1;
+	} catch (std::out_of_range) {
+		lua_pushnil(L);
+		lua_pushstring(L, "invalid level or option name");
+
+		nret = 2;
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		nret = 2;
+	}
+
+	return nret;
+}
+
+int sockEq(lua_State *L)
+{
+	Socket *s1 = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+	Socket *s2 = Luae::toType<Socket *>(L, 2, SOCKET_TYPE);
+
+	lua_pushboolean(L, *s1 == *s2);
+
+	return 1;
+}
 
-	try
-	{
-		c = new Socket(s->accept());
+int sockToString(lua_State *L)
+{
+	Socket *s = Luae::toType<Socket *>(L, 1, SOCKET_TYPE);
+
+	lua_pushfstring(L, "socket %d", s->getType());
+
+	return 1;
+}
+
+int sockGc(lua_State *L)
+{
+	Luae::toType<Socket *>(L, 1, SOCKET_TYPE)->~Socket();
+
+	return 0;
+}
+
+const luaL_Reg sockFunctions[] = {
+	{ "new",		socketNew		},
+	{ nullptr,		nullptr			}
+};
+
+const luaL_Reg sockMethods[] = {
+	{ "blockMode",		socketBlockMode		},
+	{ "bind",		socketBind		},
+	{ "close",		socketClose		},
+	{ "connect",		socketConnect		},
+	{ "accept",		socketAccept		},
+	{ "listen",		socketListen		},
+	{ "receive",		socketReceive		},
+	{ "receiveFrom",	socketReceiveFrom	},
+	{ "send",		socketSend		},
+	{ "sendTo",		socketSendTo		},
+	{ "set",		socketSet		},
+	{ nullptr,		nullptr			}
+};
+
+const luaL_Reg sockMeta[] = {
+	{ "__eq",		sockEq			},
+	{ "__tostring",		sockToString		},
+	{ "__gc",		sockGc			},
+	{ nullptr,		nullptr			}
+};
+
+/* ---------------------------------------------------------
+ * Socket address functions
+ * --------------------------------------------------------- */
+
+int addrConnectInet(lua_State *L)
+{
+	luaL_checktype(L, 1, LUA_TTABLE);
+
+	std::string host	= Luae::requireField<std::string>(L, 1, "host");
+	int port		= Luae::requireField<int>(L, 1, "port");
+	int family		= Luae::requireField<int>(L, 1, "family");
+
+	try {
+		new (L, ADDRESS_TYPE) ConnectAddressIP(host, port, family);
+	} catch (SocketError error) {
+		lua_pushnil(L);
+		lua_pushstring(L, error.what());
+
+		return 2;
 	}
-	catch (SocketError error)
-	{
-		lua_pushboolean(L, false);
+
+	return 1;
+}
+
+int addrBindInet(lua_State *L)
+{
+	std::string address = "*";
+	int port, family;
+
+	luaL_checktype(L, 1, LUA_TTABLE);
+
+	// Mandatory fields
+	port = Luae::requireField<int>(L, 1, "port");
+	family = Luae::requireField<int>(L, 1, "family");
+
+	// Optional fields
+	if (Luae::typeField(L, 1, "address") == LUA_TSTRING)
+		address = Luae::requireField<std::string>(L, 1, "address");
+
+	try {
+		new (L, ADDRESS_TYPE) BindAddressIP(address, port, family);
+	} catch (SocketError error) {
+		lua_pushnil(L);
 		lua_pushstring(L, error.what());
 
 		return 2;
 	}
 
-	ptr = static_cast<Socket **>(lua_newuserdata(L, sizeof (void *)));
-	*ptr = c;
-	luaL_setmetatable(L, ADDR_TYPE);
+	return 1;
+}
+
+#if !defined(_WIN32)
+
+int addrUnix(lua_State *L)
+{
+	const char *path = luaL_checkstring(L, 1);
+	bool rem = false;
+
+	if (lua_gettop(L) >= 2)
+		rem = lua_toboolean(L, 2);
+
+	new (L, ADDRESS_TYPE) AddressUnix(path, rem);
+
+	return 1;
+}
+
+#endif
+
+int addrToString(lua_State *L)
+{
+	SocketAddress *sa = Luae::toType<SocketAddress *>(L, 1, ADDRESS_TYPE);
+
+	lua_pushfstring(L, "address of length %d", sa->length());
 
 	return 1;
 }
 
-static int l_listen(lua_State *L)
+int addrGc(lua_State *L)
 {
-	Socket *s	= GET_SC(L, 1);
-	int max		= luaL_checkinteger(L, 2);
+	Luae::toType<SocketAddress *>(L, 1, ADDRESS_TYPE)->~SocketAddress();
+
+	return 0;
+}
 
-	try
-	{
-		s->listen(max);
-	}
-	catch (SocketError error)
-	{
-		lua_pushboolean(L, false);
-		lua_pushstring(L, error.what());
+const luaL_Reg addrFunctions[] = {
+	{ "connectInet",	addrConnectInet		},
+	{ "bindInet",		addrBindInet		},
+#if !defined(_WIN32)
+	{ "unix",		addrUnix		},
+#endif
+	{ nullptr,		nullptr			}
+};
 
-		return 2;
-	}
+const luaL_Reg addrMeta[] = {
+	{ "__tostring",		addrToString		},
+	{ "__gc",		addrGc			},
+	{ nullptr,		nullptr			}
+};
 
-	lua_pushboolean(L, true);
+/* ---------------------------------------------------------
+ * Socket listener functions
+ * --------------------------------------------------------- */
+
+int listenerNew(lua_State *L)
+{
+	new (L, LISTENER_TYPE) SocketListener();
 
 	return 1;
 }
 
-static int l_recv(lua_State *L)
+int listenerAdd(lua_State *L)
+{
+	SocketListener *l = Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE);
+	Socket *s = Luae::toType<Socket *>(L, 2, SOCKET_TYPE);
+
+	l->add(*s);
+
+	return 0;
+}
+
+int listenerRemove(lua_State *L)
+{
+	SocketListener *l = Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE);
+	Socket *s = Luae::toType<Socket *>(L, 2, SOCKET_TYPE);
+
+	l->remove(*s);
+
+	return 0;
+}
+
+int listenerClear(lua_State *L)
 {
-	Socket *s	= GET_SC(L, 1);
-	unsigned len	= luaL_checkinteger(L, 2);
-	char *data;
+	Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE)->clear();
+
+	return 0;
+}
+
+int listenerSelect(lua_State *L)
+{
+	SocketListener *l = Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE);
+	int seconds = 0, ms = 0, nret;
+
+	if (lua_gettop(L) >= 2)
+		seconds = luaL_checkinteger(L, 2);
+	if (lua_gettop(L) >= 3)
+		ms = luaL_checkinteger(L, 3);
 
-	if ((data = (char *)malloc(len)) == NULL)
-	{
+	try {
+		Socket selected = l->select(seconds, ms);
+		new (L, SOCKET_TYPE) Socket(selected);
+
+		nret = 1;
+	} catch (SocketError error) {
 		lua_pushnil(L);
-		lua_pushstring(L, )
+		lua_pushstring(L, error.what());
+
+		nret = 2;
+	} catch (SocketTimeout timeout) {
+		lua_pushnil(L);
+		lua_pushstring(L, timeout.what());
+
+		nret = 2;
 	}
 
+	return nret;
+}
+
+int listenerToStr(lua_State *L)
+{
+	SocketListener *l = Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE);
+
+	lua_pushfstring(L, "listener of %d clients", l->size());
+
+	return 1;
+}
+
+int listenerGc(lua_State *L)
+{
+	Luae::toType<SocketListener *>(L, 1, LISTENER_TYPE)->~SocketListener();
+
+	return 0;
+}
+
+const luaL_Reg listenerFunctions[] = {
+	{ "new",			listenerNew	},
+	{ nullptr,			nullptr		}
+};
+
+const luaL_Reg listenerMethods[] = {
+	{ "add",			listenerAdd	},
+	{ "remove",			listenerRemove	},
+	{ "clear",			listenerClear	},
+	{ "select",			listenerSelect	},
+	{ nullptr,			nullptr		}
+};
+
+const luaL_Reg listenerMeta[] = {
+	{ "__tostring",			listenerToStr	},
+	{ "__gc",			listenerGc	},
+	{ nullptr,			nullptr		}
+};
 
 }
 
-static const luaL_Reg functions[] = {
-	{ "new",			l_new		},
-	{ nullptr,			nullptr		}
-};
+int luaopen_socket(lua_State *L)
+{
+	// Socket functions
+	luaL_newlib(L, sockFunctions);
 
-static const luaL_Reg methods[] = {
-	{ "blockMode",			l_blockMode	},
-	{ "bind",			l_bind		},
-	{ "connect",			l_connect	},
-	{ nullptr,			nullptr		}
-};
+	// Map families, types
+	mapToTable(L, sockFamilies, -1, "family");
+	mapToTable(L, sockTypes, -1, "type");
+	mapToTable(L, sockProtocols, -1, "protocol");
 
-extern "C" int luaopen_socket(lua_State *L)
-{
+	// Create Socket type
+	luaL_newmetatable(L, SOCKET_TYPE);
+	luaL_setfuncs(L, sockMeta, 0);
+	luaL_newlib(L, sockMethods);
+	lua_setfield(L, -2, "__index");
+	lua_pop(L, 1);
 
 	return 1;
 }
+
+int luaopen_socket_address(lua_State *L)
+{
+	luaL_newlib(L, addrFunctions);
+
+	// Create SocketAddress type
+	luaL_newmetatable(L, ADDRESS_TYPE);
+	luaL_setfuncs(L, addrMeta, 0);
+	lua_pop(L, 1);
+
+	return 1;
+}
+
+int luaopen_socket_listener(lua_State *L)
+{
+	luaL_newlib(L, listenerFunctions);
+
+	// Create the SocketListener type
+	luaL_newmetatable(L, LISTENER_TYPE);
+	luaL_setfuncs(L, listenerMeta, 0);
+	luaL_newlib(L, listenerMethods);
+	lua_setfield(L, -2, "__index");
+	lua_pop(L, 1);
+
+	return 1;
+}
+
+} // !irccd
--- a/C++/Lua/LuaSocket.h	Wed Nov 27 10:53:20 2013 +0100
+++ b/C++/Lua/LuaSocket.h	Wed Nov 27 11:26:26 2013 +0100
@@ -1,7 +1,7 @@
 /*
- * LuaSocket.h -- portable Lua socket wrappers
+ * LuaSocket.h -- Lua bindings for sockets
  *
- * Copyright (c) 2013, David Demelier <markand@malikania.fr>
+ * Copyright (c) 2013 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,18 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#ifndef _LUA_SOCKET_H_
+#define _LUA_SOCKET_H_
+
 #include <lua.hpp>
 
-extern "C" int luaopen_socket(lua_State *L);
+namespace irccd
+{
+
+int luaopen_socket(lua_State *L);
+int luaopen_socket_address(lua_State *L);
+int luaopen_socket_listener(lua_State *L);
+
+} // !irccd
+
+#endif // !_LUA_SOCKET_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Luae.cpp	Wed Nov 27 11:26:26 2013 +0100
@@ -0,0 +1,282 @@
+/*
+ * Lua.cpp -- Lua helpers and such
+ *
+ * Copyright (c) 2013 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "Luae.h"
+
+namespace irccd {
+
+LuaState::LuaState()
+{
+	m_state = Ptr(luaL_newstate());
+}
+
+LuaState::LuaState(lua_State *L)
+{
+	m_state = Ptr(L);
+}
+
+LuaState::LuaState(LuaState &&state)
+{
+	m_state = std::move(state.m_state);
+}
+
+LuaState &LuaState::operator=(LuaState &&state)
+{
+	m_state = std::move(state.m_state);
+
+	return *this;
+}
+
+LuaState::operator lua_State*()
+{
+	return m_state.get();
+}
+
+LuaValue LuaValue::copy(lua_State *L, int index)
+{
+	LuaValue v;
+
+	v.type = lua_type(L, index);
+
+	switch (v.type) {
+	case LUA_TBOOLEAN:
+		v.boolean = lua_toboolean(L, index);
+		break;
+	case LUA_TNUMBER:
+		v.number = lua_tonumber(L, index);
+		break;
+	case LUA_TSTRING:
+		v.str = lua_tostring(L, index);
+		break;
+	case LUA_TTABLE:
+	{
+		LuaValue k;
+
+		if (index < 0)
+			-- index;
+
+		lua_pushnil(L);
+		while (lua_next(L, index)) {
+			v.table.push_back(std::make_pair(copy(L, -2), copy(L, -1)));
+			lua_pop(L, 1);
+		}
+
+		break;
+	}
+	default:
+		v.type = LUA_TNIL;
+		break;
+	}
+
+	return v;
+}
+
+void LuaValue::push(lua_State *L, const LuaValue &value)
+{
+	switch (value.type) {
+	case LUA_TBOOLEAN:
+		lua_pushboolean(L, value.boolean);
+		break;
+	case LUA_TSTRING:
+		lua_pushlstring(L,  value.str.c_str(), value.str.size());
+		break;
+	case LUA_TNUMBER:
+		lua_pushnumber(L, value.number);
+		break;
+	case LUA_TTABLE:
+	{
+		lua_createtable(L, 0, 0);
+
+		for (auto p : value.table) {
+			LuaValue::push(L, p.first);
+			LuaValue::push(L, p.second);
+
+			lua_settable(L, -3);
+		}
+		break;
+	}
+	default:
+		lua_pushnil(L);
+		break;
+	}
+}
+
+LuaValue::LuaValue()
+	: type(LUA_TNIL)
+{
+}
+
+template <>
+bool Luae::getField(lua_State *L, int idx, const std::string &name)
+{
+	bool value = false;
+
+	lua_getfield(L, idx, name.c_str());
+	if (lua_type(L, -1) == LUA_TBOOLEAN)
+		value = lua_toboolean(L, -1) == 1;
+	lua_pop(L, 1);
+
+	return value;
+}
+
+template <>
+double Luae::getField(lua_State *L, int idx, const std::string &name)
+{
+	double value = 0;
+
+	lua_getfield(L, idx, name.c_str());
+	if (lua_type(L, -1) == LUA_TNUMBER)
+		value = lua_tonumber(L, -1);
+	lua_pop(L, 1);
+
+	return value;
+}
+
+template <>
+int Luae::getField(lua_State *L, int idx, const std::string &name)
+{
+	int value = 0;
+
+	lua_getfield(L, idx, name.c_str());
+	if (lua_type(L, -1) == LUA_TNUMBER)
+		value = lua_tointeger(L, -1);
+	lua_pop(L, 1);
+
+	return value;
+}
+
+template <>
+std::string Luae::getField(lua_State *L, int idx, const std::string &name)
+{
+	std::string value;
+
+	lua_getfield(L, idx, name.c_str());
+	if (lua_type(L, -1) == LUA_TSTRING)
+		value = lua_tostring(L, -1);
+	lua_pop(L, 1);
+
+	return value;
+}
+
+int Luae::typeField(lua_State *L, int idx, const std::string &name)
+{
+	int type;
+
+	LUA_STACK_CHECKBEGIN(L);
+
+	lua_getfield(L, idx, name.c_str());
+	type = lua_type(L, -1);
+	lua_pop(L, 1);
+
+	LUA_STACK_CHECKEQUALS(L);
+
+	return type;
+}
+
+void Luae::preload(lua_State *L, const std::string &name, lua_CFunction func)
+{
+	LUA_STACK_CHECKBEGIN(L);
+
+	lua_getglobal(L, "package");
+	lua_getfield(L, -1, "preload");
+	lua_pushcfunction(L, func);
+	lua_setfield(L, -2, name.c_str());
+	lua_pop(L, 2);
+
+	LUA_STACK_CHECKEQUALS(L);
+}
+
+void Luae::readTable(lua_State *L, int idx, ReadFunction func)
+{
+	LUA_STACK_CHECKBEGIN(L);
+
+	lua_pushnil(L);
+
+	if (idx < 0)
+		--idx;
+
+	while (lua_next(L, idx)) {
+		func(L, lua_type(L, -2), lua_type(L, -1));
+		lua_pop(L, 1);
+	}
+
+	LUA_STACK_CHECKEQUALS(L);
+}
+
+int Luae::referenceField(lua_State *L, int idx, int type, const std::string &name)
+{
+	int ref = LUA_REFNIL;
+
+	lua_getfield(L, idx, name.c_str());
+
+	if (lua_type(L, -1) == type) {
+		lua_pushvalue(L, -1);
+		ref = luaL_ref(L, LUA_REGISTRYINDEX);
+	}
+
+	lua_pop(L, 1);
+
+	return ref;
+}
+
+void Luae::require(lua_State *L, const std::string &name, lua_CFunction func, bool global)
+{
+	LUA_STACK_CHECKBEGIN(L);
+
+	luaL_requiref(L, name.c_str(), func, global);
+	lua_pop(L, 1);
+
+	LUA_STACK_CHECKEQUALS(L);
+}
+
+void Luae::initRegistry(lua_State *L)
+{
+	lua_createtable(L, 0, 0);
+	lua_createtable(L, 0, 1);
+	lua_pushstring(L, "v");
+	lua_setfield(L, -2, "__mode");
+	lua_setmetatable(L, -2);
+	lua_setfield(L, LUA_REGISTRYINDEX, "refs");
+}
+
+} // !irccd
+
+void * operator new(size_t size, lua_State *L)
+{
+	return lua_newuserdata(L, size);
+}
+
+void * operator new(size_t size, lua_State *L, const char *metaname)
+{
+	void *object;
+
+	object = lua_newuserdata(L, size);
+	luaL_setmetatable(L, metaname);
+
+	return object;
+}
+
+void operator delete(void *, lua_State *)
+{
+}
+
+void operator delete(void *, lua_State *L, const char *)
+{
+	lua_pushnil(L);
+	lua_setmetatable(L, -2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Luae.h	Wed Nov 27 11:26:26 2013 +0100
@@ -0,0 +1,361 @@
+/*
+ * Lua.h -- Lua helpers and such
+ *
+ * Copyright (c) 2013 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LUA_H_
+#define _LUA_H_
+
+#include <cassert>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <lua.hpp>
+
+namespace irccd {
+
+/**
+ * @class LuaState
+ * @brief Wrapper for lua_State
+ *
+ * This class automatically create a new Lua state and add implicit
+ * cast operator plus RAII destruction.
+ */
+class LuaState {
+private:
+	struct Deleter {
+		void operator()(lua_State *L)
+		{
+			lua_close(L);
+		}
+	};
+
+	using Ptr = std::unique_ptr<lua_State, Deleter>;
+
+	Ptr m_state;
+
+public:
+	LuaState(const LuaState &) = delete;
+	LuaState &operator=(const LuaState &) = delete;
+
+	/**
+	 * Default constructor. Create a new state.
+	 */
+	LuaState();
+
+	/**
+	 * Use the already created state.
+	 *
+	 * @param L the state to use
+	 */
+	LuaState(lua_State *L);
+
+	/**
+	 * Move constructor.
+	 *
+	 * @param state the Lua state to move
+	 */
+	LuaState(LuaState &&state);
+
+	/**
+	 * Move assignment operator.
+	 *
+	 * @param state the Lua state to move
+	 */
+	LuaState &operator=(LuaState &&state);
+
+	/**
+	 * Implicit cast operator for convenient usage to C Lua API.
+	 *
+	 * @return the state as lua_State *
+	 */
+	operator lua_State*();
+};
+
+/**
+ * @class LuaValue
+ * @brief A fake variant for Lua values
+ *
+ * This class is primarly used for copying Lua values without checking
+ * the types, useful to pass data.
+ */
+class LuaValue {
+private:
+	union {
+		lua_Number	 number;
+		bool		 boolean;
+	};
+
+	int type;
+	std::string str;
+	std::vector<std::pair<LuaValue, LuaValue>> table;
+
+public:
+	/**
+	 * Dump a value at the specific index.
+	 *
+	 * @param L the Lua state
+	 * @param index the value
+	 * @return a tree of values
+	 */
+	static LuaValue copy(lua_State *L, int index);
+
+	/**
+	 * Push a value to a state.
+	 *
+	 * @param L the Lua state
+	 * @param value the value to push
+	 */
+	static void push(lua_State *L, const LuaValue &value);
+
+	/**
+	 * Default constructor (type nil)
+	 */
+	LuaValue();
+};
+
+/**
+ * @class Luae
+ * @brief Add lot of convenience for Lua
+ *
+ * This class adds lot of functions for Lua and C++.
+ */
+class Luae
+{
+public:
+	using ReadFunction = std::function<void(lua_State *L, int tkey, int tvalue)>;
+
+	/**
+	 * Get a field of a specific type from a table. Specialized for the
+	 * types: int, double, bool and string.
+	 *
+	 * @param L the Lua state
+	 * @param idx the table index
+	 * @param name the field name
+	 * @return the converted type.
+	 */
+	template <typename T>
+	static T getField(lua_State *L, int idx, const std::string &name);
+
+	/**
+	 * Require a field from a table.
+	 *
+	 * @param L the Lua state
+	 * @param idx the table index
+	 * @param name the field name
+	 * @return the value or call luaL_error
+	 */
+	template <typename T>
+	static T requireField(lua_State *L, int idx, const std::string &name)
+	{
+		lua_getfield(L, idx, name.c_str());
+
+		if (lua_type(L, -1) == LUA_TNIL)
+			luaL_error(L, "missing field `%s'", name.c_str());
+			// NOT REACHED
+
+		lua_pop(L, 1);
+
+		return getField<T>(L, idx, name);
+	}
+
+	/**
+	 * Check a table field.
+	 *
+	 * @param L the Lua state
+	 * @param idx the table index
+	 * @param name the field name
+	 * @return the type
+	 */
+	static int typeField(lua_State *L, int idx, const std::string &name);
+
+	/**
+	 * Read a table, the function func is called for each element in the
+	 * table. Parameter tkey is the Lua type of the key, parameter tvalue is
+	 * the Lua type of the value. The key is available at index -2 and the
+	 * value at index -1.
+	 *
+	 * <strong>Do not pop anything within the function.</strong>
+	 *
+	 * @param L the Lua state
+	 * @param idx the table index
+	 * @param func the function to call
+	 */
+	static void readTable(lua_State *L, int idx, ReadFunction func);
+
+	/**
+	 * Preload a library, it will be added to package.preload so the
+	 * user can successfully call require "name". In order to work, you need
+	 * to open luaopen_package and luaopen_base first.
+	 *
+	 * @param L the Lua state
+	 * @param name the module name
+	 * @param func the opening library
+	 * @see require
+	 */
+	static void preload(lua_State *L,
+			    const std::string &name,
+			    lua_CFunction func);
+
+	/**
+	 * Reference a field from a table at the index. The reference is created in
+	 * the registry only if type matches.
+	 *
+	 * @param L the Lua state
+	 * @param idx the table index
+	 * @param type the type requested
+	 * @param name the field name
+	 * @return the reference or LUA_REFNIL on problem
+	 */
+	static int referenceField(lua_State *L,
+				  int idx,
+				  int type,
+				  const std::string &name);
+
+	/**
+	 * Load a library just like it was loaded with require.
+	 *
+	 * @param L the Lua state
+	 * @param name the module name
+	 * @param func the function
+	 * @param global store as global
+	 */
+	static void require(lua_State *L,
+			    const std::string &name,
+			    lua_CFunction func,
+			    bool global);
+
+	/**
+	 * Initialize the registry for shared objects.
+	 *
+	 * @param L the Lua state
+	 */
+	static void initRegistry(lua_State *L);
+
+	/**
+	 * Push a shared object to Lua, it also push it to the "refs"
+	 * table with __mode = "v". That is if we need to push the object
+	 * again we use the same reference so Lua get always the same
+	 * userdata and gain the following benefits:
+	 *
+	 * 1. The user can use the userdata as table key
+	 * 2. A performance gain thanks to less allocations
+	 *
+	 * @param L the Lua state
+	 * @param o the object to push
+	 * @param name the object metatable name
+	 */
+	template <typename T>
+	static void pushShared(lua_State *L,
+			std::shared_ptr<T> o,
+			const std::string &name)
+	{
+		lua_getfield(L, LUA_REGISTRYINDEX, "refs");
+		assert(lua_type(L, -1) == LUA_TTABLE);
+
+		lua_rawgetp(L, -1, o.get());
+
+		if (lua_type(L, -1) == LUA_TNIL) {
+			lua_pop(L, 1);
+
+			new (L, name.c_str()) std::shared_ptr<T>(o);
+			
+			lua_pushvalue(L, -1);
+			lua_rawsetp(L, -3, o.get());
+		}
+
+		lua_replace(L, -2);
+	}
+
+	/**
+	 * Get an object from Lua that was previously push with pushShared.
+	 *
+	 * @param L the Lua state
+	 * @param index the object index
+	 * @param meta the object metatable name
+	 * @return the object
+	 */
+	template <typename T>
+	static std::shared_ptr<T> getShared(lua_State *L, int index, const char *meta)
+	{
+		using Ptr = std::shared_ptr<T>;
+		
+		Ptr *ptr = static_cast<Ptr *>(luaL_checkudata(L, index, meta));
+		
+		return *ptr;
+	}
+
+	/**
+	 * Convert a new placement made object, without testing if its a real
+	 * object.
+	 *
+	 * @param L the Lua state
+	 * @param idx the object index
+	 * @return the converted object
+	 */
+	template<class T>
+	static T toType(lua_State *L, int idx)
+	{
+		return reinterpret_cast<T>(lua_touserdata(L, idx));
+	}
+
+	/**
+	 * Convert a class created with new placement.
+	 *
+	 * @param L the Lua state
+	 * @param idx the value index
+	 * @param metaname the metatable name
+	 * @return the converted object
+	 */
+	template <typename T>
+	static T toType(lua_State *L, int idx, const char *metaname)
+	{
+		return reinterpret_cast<T>(luaL_checkudata(L, idx, metaname));
+	}
+};
+
+#if !defined(NDEBUG)
+
+#define LUA_STACK_CHECKBEGIN(L)						\
+	int __topstack = lua_gettop((L))
+
+#define LUA_STACK_CHECKEQUALS(L)					\
+	assert(lua_gettop((L)) == __topstack)
+
+#define LUA_STACK_CHECKEND(L, cond)					\
+	assert(lua_gettop((L)) cond == __topstack)
+
+#else
+
+#define LUA_STACK_CHECKBEGIN(L)
+#define LUA_STACK_CHECKEQUALS(L)
+#define LUA_STACK_CHECKEND(L, cond)
+
+#endif
+
+} // !irccd
+
+void *operator new(size_t size, lua_State *L);
+
+void *operator new(size_t size, lua_State *L, const char *metaname);
+
+void operator delete(void *ptr, lua_State *L);
+
+void operator delete(void *ptr, lua_State *L, const char *metaname);
+
+#endif // !_LUA_H_