Skip to content

Commit

Permalink
UDP socket in net module (LWIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
djphoenix committed Jul 3, 2016
1 parent 3a7f01b commit 2bd3bd4
Showing 1 changed file with 345 additions and 0 deletions.
345 changes: 345 additions & 0 deletions app/modules/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "lwip/ip_addr.h"
#include "espconn.h"
#include "lwip/dns.h"
#include "lwip/udp.h"

#define TCP ESPCONN_TCP
#define UDP ESPCONN_UDP
Expand Down Expand Up @@ -1702,6 +1703,334 @@ static int expose_array(lua_State* L, char *array, unsigned short len) {
}
#endif

typedef struct {
struct udp_pcb *pcb;
int cb_recv_ref;
int self_ref;
} net_udp_ud;

static void net_udp_recv( net_udp_ud *ud, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port ) {
if ( !ud || !pcb ||
ud->pcb != pcb ||
ud->self_ref == LUA_NOREF ||
ud->cb_recv_ref == LUA_NOREF)
{
pbuf_free(p);
return;
}

lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_recv_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushinteger(L, port);
char iptmp[20];
size_t ipl = c_sprintf(iptmp, IPSTR, IP2STR(&addr->addr));
lua_pushlstring(L, iptmp, ipl);
lua_pushlstring(L, p->payload, p->len);
lua_call(L, 4, 0);

pbuf_free(p);
}

static int net_createUDPSocket( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud*) lua_newuserdata(L, sizeof(net_udp_ud));

ud->pcb = udp_new();
udp_recv(ud->pcb, (udp_recv_fn)net_udp_recv, ud);
ud->cb_recv_ref = LUA_NOREF;
ud->self_ref = LUA_NOREF;

luaL_getmetatable(L, "net.udp");
lua_setmetatable(L, -2);

return 1;
}

static int luaL_lwip_checkerr(err_t res, lua_State *L) {
switch (res) {
case ERR_OK: return 0; \
case ERR_MEM: return luaL_error(L, "out of memory");
case ERR_BUF: return luaL_error(L, "buffer error");
case ERR_TIMEOUT: return luaL_error(L, "timeout");
case ERR_RTE: return luaL_error(L, "routing error");
case ERR_INPROGRESS: return luaL_error(L, "operation in progress");
case ERR_VAL: return luaL_error(L, "illegal value");
case ERR_WOULDBLOCK: return luaL_error(L, "operation would block");
case ERR_ABRT: return luaL_error(L, "connection aborted");
case ERR_RST: return luaL_error(L, "connection reset");
case ERR_CLSD: return luaL_error(L, "connection closed");
case ERR_CONN: return luaL_error(L, "not connected");
case ERR_ARG: return luaL_error(L, "illegal argument");
case ERR_USE: return luaL_error(L, "address in use");
case ERR_IF: return luaL_error(L, "low-level netif error");
case ERR_ISCONN: return luaL_error(L, "already connected");
default: return luaL_error(L, "invalid LWIP state");
}
}

static int net_udp_getaddr( lua_State* L );

static int net_udp_bind( lua_State* L ) {
net_udp_ud *ud;
ip_addr_t addr;
u16_t port;

ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

port = luaL_checkinteger(L, 2);
if (lua_isstring(L, 3)) {
size_t sl;
const char* domain = luaL_checklstring(L, 3, &sl);
addr.addr = ipaddr_addr(domain);
if (addr.addr == 0xFFFFFFFF) {
return luaL_error(L, "Invalid IP address");
}
} else {
addr.addr = 0;
}

luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
udp_disconnect(ud->pcb);

int err;
if ((err = luaL_lwip_checkerr(udp_bind(ud->pcb, &addr, port), L)) != 0) {
return err;
}

lua_pushvalue(L, 1); // copy to the top of stack
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);

return net_udp_getaddr(L);
}

static int net_udp_connect( lua_State* L ) {
net_udp_ud *ud;
ip_addr_t addr;
u16_t port;
size_t sl;

ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

port = luaL_checkinteger(L, 2);
const char* domain = luaL_checklstring(L, 3, &sl);

if (port == 0) {
return luaL_error(L, "Invalid port");
}
addr.addr = ipaddr_addr(domain);
if (domain == 0 || addr.addr == 0 || addr.addr == 0xFFFFFFFF) {
return luaL_error(L, "Invalid IP address");
}

int err;
if ((err = luaL_lwip_checkerr(udp_connect(ud->pcb, &addr, port), L)) != 0) {
return err;
}

if (ud->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1); // copy to the top of stack
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}

return net_udp_getaddr(L);
}

static int net_udp_send( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

size_t sl;
const char* payload = luaL_checklstring(L, 2, &sl);
if (payload == 0) {
return luaL_error(L, "invalid payload");
}

if (ud->pcb->remote_port == 0 || ud->pcb->remote_ip.addr == 0) {
return luaL_lwip_checkerr(ERR_CONN, L);
}

struct pbuf *buf = pbuf_alloc(PBUF_TRANSPORT, sl, PBUF_RAM);
os_memmove(buf->payload, payload, sl);
int err;
if ((err = luaL_lwip_checkerr(udp_send(ud->pcb, buf), L)) != 0) {
pbuf_free(buf);
return err;
}
pbuf_free(buf);

return 0;
}

static int net_udp_sendto( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

size_t sl, il;
const char* payload = luaL_checklstring(L, 2, &sl);
u16_t port = luaL_checkinteger(L, 3);
const char* domain = luaL_checklstring(L, 4, &il);
ip_addr_t addr;

if (payload == 0) {
return luaL_error(L, "invalid payload");
}
if (port == 0) {
return luaL_error(L, "Invalid port");
}
addr.addr = ipaddr_addr(domain);
if (domain == 0 || addr.addr == 0 || addr.addr == 0xFFFFFFFF) {
return luaL_error(L, "Invalid IP address");
}

struct pbuf *buf = pbuf_alloc(PBUF_TRANSPORT, sl, PBUF_RAM);
os_memmove(buf->payload, payload, sl);
int err;
if ((err = luaL_lwip_checkerr(udp_sendto(ud->pcb, buf, &addr, port), L)) != 0) {
pbuf_free(buf);
return err;
}
pbuf_free(buf);

return 0;
}

static int net_udp_getaddr( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

if (ud->self_ref == LUA_NOREF) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}

char ipaddr[20];
c_sprintf(ipaddr, IPSTR, IP2STR(&ud->pcb->local_ip.addr));

lua_pushinteger(L, ud->pcb->local_port);
lua_pushstring(L, ipaddr);

return 2;
}

static int net_udp_getpeer( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

if (ud->self_ref == LUA_NOREF) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}

if (ud->pcb->remote_port == 0 || ud->pcb->remote_ip.addr == 0) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}

char ipaddr[20];
c_sprintf(ipaddr, IPSTR, IP2STR(&ud->pcb->remote_ip.addr));

lua_pushinteger(L, ud->pcb->remote_port);
lua_pushstring(L, ipaddr);

return 2;
}

static int net_udp_close( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

udp_disconnect(ud->pcb);

lua_gc(L, LUA_GCSTOP, 0);
luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
lua_gc(L, LUA_GCRESTART, 0);

return 0;
}

static int net_udp_on( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

size_t sl;
const char* event = luaL_checklstring(L, 2, &sl);

luaL_checkanyfunction(L, 3);
lua_pushvalue(L, 3); // copy argument (func) to the top of stack

if(c_strcmp(event, "receive") == 0){
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_recv_ref);
ud->cb_recv_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
return luaL_error(L, "invalid method");
}

return 0;
}

static int net_udp_delete( lua_State* L ) {
net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp");
luaL_argcheck(L, ud, 1, "UDP socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}

if (ud->pcb) {
udp_disconnect(ud->pcb);
udp_remove(ud->pcb);
ud->pcb = 0;
}
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_recv_ref);
ud->cb_recv_ref = LUA_NOREF;

lua_gc(L, LUA_GCSTOP, 0);
luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
lua_gc(L, LUA_GCRESTART, 0);

return 0;
}

// Module function map
static const LUA_REG_TYPE net_server_map[] = {
{ LSTRKEY( "listen" ), LFUNCVAL( net_server_listen ) },
Expand Down Expand Up @@ -1751,9 +2080,24 @@ static const LUA_REG_TYPE net_dns_map[] = {
{ LNILKEY, LNILVAL }
};

static const LUA_REG_TYPE net_udp_map[] = {
{ LSTRKEY( "bind" ), LFUNCVAL( net_udp_bind ) },
{ LSTRKEY( "on" ), LFUNCVAL( net_udp_on ) },
{ LSTRKEY( "connect" ), LFUNCVAL( net_udp_connect ) },
{ LSTRKEY( "sendto" ), LFUNCVAL( net_udp_sendto ) },
{ LSTRKEY( "send" ), LFUNCVAL( net_udp_send ) },
{ LSTRKEY( "getaddr" ), LFUNCVAL( net_udp_getaddr ) },
{ LSTRKEY( "getpeer" ), LFUNCVAL( net_udp_getpeer ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_udp_close ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_udp_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_udp_map ) },
{ LNILKEY, LNILVAL }
};

static const LUA_REG_TYPE net_map[] = {
{ LSTRKEY( "createServer" ), LFUNCVAL( net_createServer ) },
{ LSTRKEY( "createConnection" ), LFUNCVAL( net_createConnection ) },
{ LSTRKEY( "createUDPSocket" ), LFUNCVAL( net_createUDPSocket ) },
{ LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) },
{ LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) },
{ LSTRKEY( "dns" ), LROVAL( net_dns_map ) },
Expand All @@ -1778,6 +2122,7 @@ int luaopen_net( lua_State *L ) {
#if 0
luaL_rometatable(L, "net.array", (void *)net_array_map); // create metatable for net.array
#endif
luaL_rometatable(L, "net.udp", (void *)net_udp_map); // create metatable for net.udp

return 0;
}
Expand Down

0 comments on commit 2bd3bd4

Please sign in to comment.