Commit be5dafb2 authored by Johny Mattsson's avatar Johny Mattsson
Browse files

Imported @djphoenix's new native LWIP net module.

Based on his revision 2d290a24a0914be88e5ca4ac7b1018392fe75fe2
(https://github.com/djphoenix/nodemcu-firmware).

All LWIP callback handling changed to use the NodeMCU task interface
to make it usable on RTOS.

IPv4 assumptions have been removed, and this net module /should/ now
be IPv6 ready, but aside from compilation no testing has been done
in this area.

SSL integration points not touched - some work needed there once we
have an mbedTLS module.
parent e67c6f09
...@@ -34,7 +34,7 @@ config LUA_BUILTIN_DEBUG ...@@ -34,7 +34,7 @@ config LUA_BUILTIN_DEBUG
config LUA_BUILTIN_DEBUG_EXTENDED config LUA_BUILTIN_DEBUG_EXTENDED
depends on LUA_BUILTIN_DEBUG depends on LUA_BUILTIN_DEBUG
bool "Extended debug support bool "Extended debug support"
default "n" default "n"
help help
Includes the full debug module, rather than just getregistry and traceback. Includes the full debug module, rather than just getregistry and traceback.
...@@ -46,17 +46,13 @@ depends on LUA_BUILTIN_DEBUG ...@@ -46,17 +46,13 @@ depends on LUA_BUILTIN_DEBUG
endmenu endmenu
config LUA_MODULE_NODE
bool "Node module"
default "y"
help
Includes the node module (recommended).
config LUA_MODULE_FILE
bool "File module" config LUA_MODULE_BTHCI
default "y" bool "BlueTooth HCI interface module"
default "n"
help help
Includes the file module (recommended). Includes the simple BlueTooth HCI module.
config LUA_MODULE_ENCODER config LUA_MODULE_ENCODER
bool "Encoder module" bool "Encoder module"
...@@ -65,16 +61,28 @@ config LUA_MODULE_ENCODER ...@@ -65,16 +61,28 @@ config LUA_MODULE_ENCODER
Includes the encoder module. This provides hex and base64 encoding and Includes the encoder module. This provides hex and base64 encoding and
decoding functionality. decoding functionality.
config LUA_MODULE_FILE
bool "File module"
default "y"
help
Includes the file module (recommended).
config LUA_MODULE_NET
bool "Net module"
default "y"
help
Includes the net module (recommended).
config LUA_MODULE_NODE
bool "Node module"
default "y"
help
Includes the node module (recommended).
config LUA_MODULE_WIFI config LUA_MODULE_WIFI
bool "WiFi module" bool "WiFi module"
default "y" default "y"
help help
Includes the WiFi module (recommended). Includes the WiFi module (recommended).
config LUA_MODULE_BTHCI
bool "BlueTooth HCI interface module"
default "n"
help
Includes the simple BlueTooth HCI module.
endmenu endmenu
// Module for network
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "lmem.h"
#include "ip_fmt.h"
#include "task/task.h"
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include "lwip/err.h"
#include "lwip/ip_addr.h"
#include "lwip/dns.h"
#include "lwip/igmp.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
// Some LWIP macros cause complaints with ptr NULL checks, so shut them off :(
#pragma GCC diagnostic ignored "-Waddress"
typedef enum net_type {
TYPE_TCP_SERVER = 0,
TYPE_TCP_CLIENT,
TYPE_UDP_SOCKET
} net_type;
typedef const char net_table_name[14];
static const net_table_name NET_TABLES[] = {
"net.tcpserver",
"net.tcpsocket",
"net.udpsocket"
};
#define NET_TABLE_TCP_SERVER NET_TABLES[0]
#define NET_TABLE_TCP_CLIENT NET_TABLES[1]
#define NET_TABLE_UDP_SOCKET NET_TABLES[2]
#define TYPE_TCP TYPE_TCP_CLIENT
#define TYPE_UDP TYPE_UDP_SOCKET
typedef struct lnet_userdata {
enum net_type type;
int self_ref;
union {
struct tcp_pcb *tcp_pcb;
struct udp_pcb *udp_pcb;
void *pcb;
};
union {
struct {
int cb_accept_ref;
} server;
struct {
int wait_dns;
int cb_dns_ref;
int cb_receive_ref;
int cb_sent_ref;
// Only for TCP:
int hold;
int cb_connect_ref;
int cb_disconnect_ref;
int cb_reconnect_ref;
} client;
};
} lnet_userdata;
// --- Event handling
typedef struct {
ip_addr_t src_ip;
uint16_t src_port;
uint16_t payload_len;
char payload[0];
} lnet_recvdata;
typedef struct {
enum {
DNSFOUND,
DNSSTATIC,
CONNECTED,
ACCEPT,
RECVDATA,
SENTDATA,
ERR
} event;
union {
lnet_userdata *ud;
int cb_ref;
};
union {
struct tcp_pcb *accept_newpcb;
lnet_recvdata recvdata;
ip_addr_t resolved_ip;
int err;
};
} lnet_event;
static task_handle_t net_event;
// --- LWIP errors
int lwip_lua_checkerr (lua_State *L, err_t err) {
switch (err) {
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 problem");
case ERR_INPROGRESS: return luaL_error(L, "in progress");
case ERR_VAL: return luaL_error(L, "illegal value");
case ERR_WOULDBLOCK: return luaL_error(L, "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, "netif error");
case ERR_ISCONN: return luaL_error(L, "already connected");
default: return luaL_error(L, "unknown error");
}
}
// --- Create
lnet_userdata *net_create( lua_State *L, enum net_type type ) {
const char *mt = NET_TABLES[type];
lnet_userdata *ud = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata));
if (!ud) return NULL;
luaL_getmetatable(L, mt);
lua_setmetatable(L, -2);
ud->type = type;
ud->self_ref = LUA_NOREF;
ud->pcb = NULL;
switch (type) {
case TYPE_TCP_CLIENT:
ud->client.cb_connect_ref = LUA_NOREF;
ud->client.cb_reconnect_ref = LUA_NOREF;
ud->client.cb_disconnect_ref = LUA_NOREF;
ud->client.hold = 0;
case TYPE_UDP_SOCKET:
ud->client.wait_dns = 0;
ud->client.cb_dns_ref = LUA_NOREF;
ud->client.cb_receive_ref = LUA_NOREF;
ud->client.cb_sent_ref = LUA_NOREF;
break;
case TYPE_TCP_SERVER:
ud->server.cb_accept_ref = LUA_NOREF;
break;
}
return ud;
}
// --- LWIP callbacks and task_post helpers
static bool post_net_err (lnet_userdata *ud, err_t err) {
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev)
return false;
ev->event = ERR;
ev->ud = ud;
ev->err = err;
if (!task_post_medium (net_event, (task_param_t)ev)) {
free (ev);
return false;
}
return true;
}
static void net_err_cb(void *arg, err_t err) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return;
ud->pcb = NULL; // Will be freed at LWIP level
post_net_err (ud, err);
}
static bool post_net_connected (lnet_userdata *ud) {
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev)
return false;
ev->event = CONNECTED;
ev->ud = ud;
if (!task_post_medium (net_event, (task_param_t)ev)) {
free (ev);
return false;
}
return true;
}
static err_t net_connected_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || ud->pcb != tpcb) return ERR_ABRT;
if (err != ERR_OK) {
net_err_cb(arg, err);
return ERR_ABRT;
}
post_net_connected (ud);
return ERR_OK;
}
static bool post_net_dns (lnet_userdata *ud, const char *name, const ip_addr_t *ipaddr)
{
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev || !ipaddr)
return false;
ev->event = DNSFOUND;
ev->ud = ud;
ev->resolved_ip = *ipaddr;
if (!task_post_medium (net_event, (task_param_t)ev)) {
free (ev);
return false;
}
return true;
}
static void net_dns_cb(const char *name, const ip_addr_t *ipaddr, void *arg) {
ip_addr_t addr;
if (ipaddr != NULL) addr = *ipaddr;
else addr = ip_addr_any;
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud) return;
post_net_dns (ud, name, &addr);
}
static bool post_net_recv (lnet_userdata *ud, struct pbuf *p, const ip_addr_t *ip, u16_t port)
{
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event) + p->len);
if (!ev)
return false;
ev->event = RECVDATA;
ev->ud = ud;
if (ip)
ev->recvdata.src_ip = *ip;
ev->recvdata.src_port = port;
ev->recvdata.payload_len = p->len;
pbuf_copy_partial (p, &ev->recvdata.payload, p->len, 0);
if (!task_post_high (net_event, (task_param_t)ev))
{
free (ev);
return false;
}
return true;
}
static void net_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || !ud->pcb || ud->type != TYPE_UDP_SOCKET || ud->self_ref == LUA_NOREF) {
if (p) pbuf_free(p);
return;
}
post_net_recv (ud, p, addr, port);
}
static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF)
return ERR_ABRT;
if (!p) {
net_err_cb(arg, err);
return tcp_close(tpcb);
}
if (post_net_recv (ud, p, 0, 0))
tcp_recved(tpcb, p->len);
return ERR_OK;
}
static bool post_net_sent (lnet_userdata *ud) {
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev)
return false;
ev->event = SENTDATA;
ev->ud = ud;
if (!task_post_medium (net_event, (task_param_t)ev)) {
free (ev);
return false;
}
return true;
}
static err_t net_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return ERR_ABRT;
if (ud->client.cb_sent_ref == LUA_NOREF) return ERR_OK;
post_net_sent (ud);
// TODO: if we can't post this, we effectively stall this socket, how to fix?
return ERR_OK;
}
static bool post_net_accept (lnet_userdata *ud, struct tcp_pcb *newpcb) {
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev)
return false;
ev->event = SENTDATA;
ev->ud = ud;
ev->accept_newpcb = newpcb;
if (!task_post_medium (net_event, (task_param_t)ev)) {
free (ev);
return false;
}
return true;
}
static err_t net_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) {
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud || ud->type != TYPE_TCP_SERVER || !ud->pcb) return ERR_ABRT;
if (ud->self_ref == LUA_NOREF || ud->server.cb_accept_ref == LUA_NOREF) return ERR_ABRT;
return post_net_accept (ud, newpcb) ? ERR_OK : ERR_ABRT;
}
// --- Lua API - create
extern int tls_socket_create( lua_State *L );
// Lua: net.createUDPSocket()
int net_createUDPSocket( lua_State *L ) {
net_create(L, TYPE_UDP_SOCKET);
return 1;
}
// Lua: net.createServer(type, timeout)
int net_createServer( lua_State *L ) {
int type, timeout;
type = luaL_optlong(L, 1, TYPE_TCP);
timeout = luaL_optlong(L, 2, 30);
if (type == TYPE_UDP) return net_createUDPSocket( L );
if (type != TYPE_TCP) return luaL_error(L, "invalid type");
net_create(L, TYPE_TCP_SERVER);
// TODO: timeout
(void)timeout;
return 1;
}
// Lua: net.createConnection(type, secure)
int net_createConnection( lua_State *L ) {
int type, secure;
type = luaL_optlong(L, 1, TYPE_TCP);
secure = luaL_optlong(L, 2, 0);
if (type == TYPE_UDP) return net_createUDPSocket( L );
if (type != TYPE_TCP) return luaL_error(L, "invalid type");
if (secure) {
#ifdef CLIENT_SSL_ENABLE
return tls_socket_create( L );
#else
return luaL_error(L, "secure connections not enabled");
#endif
}
net_create(L, TYPE_TCP_CLIENT);
return 1;
}
// --- Get & check userdata
lnet_userdata *net_get_udata_s( lua_State *L, int stack ) {
if (!lua_isuserdata(L, stack)) return NULL;
lnet_userdata *ud = (lnet_userdata *)lua_touserdata(L, stack);
switch (ud->type) {
case TYPE_TCP_CLIENT:
case TYPE_TCP_SERVER:
case TYPE_UDP_SOCKET:
break;
default: return NULL;
}
const char *mt = NET_TABLES[ud->type];
ud = luaL_checkudata(L, stack, mt);
return ud;
}
#define net_get_udata(L) net_get_udata_s(L, 1)
// --- Lua API
// Lua: server:listen(port, addr, function(c)), socket:listen(port, addr)
int net_listen( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type == TYPE_TCP_CLIENT)
return luaL_error(L, "invalid user data");
if (ud->pcb)
return luaL_error(L, "already listening");
int stack = 2;
uint16_t port = 0;
const char *domain = "0.0.0.0";
if (lua_isnumber(L, stack))
port = lua_tointeger(L, stack++);
if (lua_isstring(L, stack)) {
size_t dl = 0;
domain = luaL_checklstring(L, stack++, &dl);
}
ip_addr_t addr;
if (!ipaddr_aton(domain, &addr))
return luaL_error(L, "invalid IP address");
if (ud->type == TYPE_TCP_SERVER) {
if (lua_isfunction(L, stack) || lua_islightfunction(L, stack)) {
lua_pushvalue(L, stack++);
luaL_unref(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref);
ud->server.cb_accept_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
return luaL_error(L, "need callback");
}
}
err_t err = ERR_OK;
switch (ud->type) {
case TYPE_TCP_SERVER:
ud->tcp_pcb = tcp_new();
if (!ud->tcp_pcb)
return luaL_error(L, "cannot allocate PCB");
err = tcp_bind(ud->tcp_pcb, &addr, port);
if (err == ERR_OK) {
tcp_arg(ud->tcp_pcb, ud);
struct tcp_pcb *pcb = tcp_listen(ud->tcp_pcb);
if (!pcb) {
err = ERR_MEM;
} else {
ud->tcp_pcb = pcb;
tcp_accept(ud->tcp_pcb, net_accept_cb);
}
}
break;
case TYPE_UDP_SOCKET:
ud->udp_pcb = udp_new();
if (!ud->udp_pcb)
return luaL_error(L, "cannot allocate PCB");
udp_recv(ud->udp_pcb, net_udp_recv_cb, ud);
err = udp_bind(ud->udp_pcb, &addr, port);
break;
default: break;
}
if (err != ERR_OK) {
switch (ud->type) {
case TYPE_TCP_SERVER:
tcp_close(ud->tcp_pcb);
ud->tcp_pcb = NULL;
break;
case TYPE_UDP_SOCKET:
udp_remove(ud->udp_pcb);
ud->udp_pcb = NULL;
break;
default: break;
}
return lwip_lua_checkerr(L, err);
}
if (ud->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1);
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
return 0;
}
// Lua: client:connect(port, addr)
int net_connect( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type != TYPE_TCP_CLIENT)
return luaL_error(L, "invalid user data");
if (ud->pcb)
return luaL_error(L, "already connected");
uint16_t port = luaL_checkinteger(L, 2);
if (port == 0)
return luaL_error(L, "specify port");
const char *domain = "127.0.0.1";
if (lua_isstring(L, 3)) {
size_t dl = 0;
domain = luaL_checklstring(L, 3, &dl);
}
ud->tcp_pcb = tcp_new();
if (!ud->tcp_pcb)
return luaL_error(L, "cannot allocate PCB");
tcp_arg(ud->tcp_pcb, ud);
tcp_err(ud->tcp_pcb, net_err_cb);
tcp_recv(ud->tcp_pcb, net_tcp_recv_cb);
tcp_sent(ud->tcp_pcb, net_sent_cb);
ud->tcp_pcb->remote_port = port;
ip_addr_t addr;
ud->client.wait_dns ++;
int unref = 0;
if (ud->self_ref == LUA_NOREF) {
unref = 1;
lua_pushvalue(L, 1);
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
err_t err = dns_gethostbyname(domain, &addr, net_dns_cb, ud);
if (err == ERR_OK) {
net_dns_cb(domain, &addr, ud);
} else if (err != ERR_INPROGRESS) {
ud->client.wait_dns --;
if (unref) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
}
tcp_abort(ud->tcp_pcb);
ud->tcp_pcb = NULL;
return lwip_lua_checkerr(L, err);
}
return 0;
}
// Lua: client/socket:on(name, callback)
int net_on( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type == TYPE_TCP_SERVER)
return luaL_error(L, "invalid user data");
int *refptr = NULL;
const char *name = luaL_checkstring(L, 2);
if (!name) return luaL_error(L, "need callback name");
switch (ud->type) {
case TYPE_TCP_CLIENT:
if (strcmp("connection",name)==0)
{ refptr = &ud->client.cb_connect_ref; break; }
if (strcmp("disconnection",name)==0)
{ refptr = &ud->client.cb_disconnect_ref; break; }
if (strcmp("reconnection",name)==0)
{ refptr = &ud->client.cb_reconnect_ref; break; }
case TYPE_UDP_SOCKET:
if (strcmp("dns",name)==0)
{ refptr = &ud->client.cb_dns_ref; break; }
if (strcmp("receive",name)==0)
{ refptr = &ud->client.cb_receive_ref; break; }
if (strcmp("sent",name)==0)
{ refptr = &ud->client.cb_sent_ref; break; }
break;
default: return luaL_error(L, "invalid user data");
}
if (refptr == NULL)
return luaL_error(L, "invalid callback name");
if (lua_isfunction(L, 3) || lua_islightfunction(L, 3)) {
lua_pushvalue(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, *refptr);
*refptr = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (lua_isnil(L, 3)) {
luaL_unref(L, LUA_REGISTRYINDEX, *refptr);
*refptr = LUA_NOREF;
} else {
return luaL_error(L, "invalid callback function");
}
return 0;
}
// Lua: client:send(data, function(c)), socket:send(port, ip, data, function(s))
int net_send( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type == TYPE_TCP_SERVER)
return luaL_error(L, "invalid user data");
ip_addr_t addr;
uint16_t port = 0;
const char *data;
size_t datalen = 0;
int stack = 2;
if (ud->type == TYPE_UDP_SOCKET) {
size_t dl = 0;
port = luaL_checkinteger(L, stack++);
if (port == 0) return luaL_error(L, "need port");
const char *domain = luaL_checklstring(L, stack++, &dl);
if (!domain) return luaL_error(L, "need IP address");
if (!ipaddr_aton(domain, &addr)) return luaL_error(L, "invalid IP address");
}
data = luaL_checklstring(L, stack++, &datalen);
if (!data || datalen == 0) return luaL_error(L, "no data to send");
if (lua_isfunction(L, stack) || lua_islightfunction(L, stack)) {
lua_pushvalue(L, stack++);
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
ud->client.cb_sent_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if (ud->type == TYPE_UDP_SOCKET && !ud->pcb) {
ud->udp_pcb = udp_new();
if (!ud->udp_pcb)
return luaL_error(L, "cannot allocate PCB");
udp_recv(ud->udp_pcb, net_udp_recv_cb, ud);
ip_addr_t laddr = IPADDR_ANY_TYPE_INIT;
err_t err = udp_bind(ud->udp_pcb, &laddr, 0);
if (err != ERR_OK) {
udp_remove(ud->udp_pcb);
ud->udp_pcb = NULL;
return lwip_lua_checkerr(L, err);
}
if (ud->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1);
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
if (!ud->pcb || ud->self_ref == LUA_NOREF)
return luaL_error(L, "not connected");
err_t err;
if (ud->type == TYPE_UDP_SOCKET) {
struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
if (!pb)
return luaL_error(L, "cannot allocate message buffer");
pbuf_take(pb, data, datalen);
err = udp_sendto(ud->udp_pcb, pb, &addr, port);
pbuf_free(pb);
if (ud->client.cb_sent_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
}
} else if (ud->type == TYPE_TCP_CLIENT) {
err = tcp_write(ud->tcp_pcb, data, datalen, TCP_WRITE_FLAG_COPY);
}
else {
err = ERR_VAL;
}
return lwip_lua_checkerr(L, err);
}
// Lua: client:hold()
int net_hold( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type != TYPE_TCP_CLIENT)
return luaL_error(L, "invalid user data");
ud->client.hold = 1;
return 0;
}
// Lua: client:unhold()
int net_unhold( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type != TYPE_TCP_CLIENT)
return luaL_error(L, "invalid user data");
ud->client.hold = 0;
return 0;
}
// Lua: client/socket:dns(domain, callback(socket, addr))
int net_dns( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type == TYPE_TCP_SERVER)
return luaL_error(L, "invalid user data");
size_t dl = 0;
const char *domain = luaL_checklstring(L, 2, &dl);
if (!domain)
return luaL_error(L, "no domain specified");
if (lua_isfunction(L, 3) || lua_islightfunction(L, 3)) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref);
lua_pushvalue(L, 3);
ud->client.cb_dns_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if (ud->client.cb_dns_ref == LUA_NOREF)
return luaL_error(L, "no callback specified");
ud->client.wait_dns ++;
int unref = 0;
if (ud->self_ref == LUA_NOREF) {
unref = 1;
lua_pushvalue(L, 1);
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
ip_addr_t addr;
err_t err = dns_gethostbyname(domain, &addr, net_dns_cb, ud);
if (err == ERR_OK) {
net_dns_cb(domain, &addr, ud);
} else if (err != ERR_INPROGRESS) {
ud->client.wait_dns --;
if (unref) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
}
return lwip_lua_checkerr(L, err);
}
return 0;
}
// Lua: client:getpeer()
int net_getpeer( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud || ud->type != TYPE_TCP_CLIENT)
return luaL_error(L, "invalid user data");
if (!ud->pcb) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
uint16_t port = ud->tcp_pcb->remote_port;
ip_addr_t addr = ud->tcp_pcb->remote_ip;
if (port == 0) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
char addr_str[IP_STR_SZ];
ipstr (addr_str, &addr);
lua_pushinteger(L, port);
lua_pushstring(L, addr_str);
return 2;
}
// Lua: client/server/socket:getaddr()
int net_getaddr( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud) return luaL_error(L, "invalid user data");
if (!ud->pcb) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
uint16_t port = 0;
ip_addr_t addr;
switch (ud->type) {
case TYPE_TCP_CLIENT:
case TYPE_TCP_SERVER:
addr = ud->tcp_pcb->local_ip;
port = ud->tcp_pcb->local_port;
break;
case TYPE_UDP_SOCKET:
addr = ud->udp_pcb->local_ip;
port = ud->udp_pcb->local_port;
break;
}
if (port == 0) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
char addr_str[IP_STR_SZ];
ipstr (addr_str, &addr);
lua_pushinteger(L, port);
lua_pushstring(L, addr_str);
return 2;
}
// Lua: client/server/socket:close()
int net_close( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud) return luaL_error(L, "invalid user data");
if (ud->pcb) {
switch (ud->type) {
case TYPE_TCP_CLIENT:
if (ERR_OK != tcp_close(ud->tcp_pcb)) {
tcp_arg(ud->tcp_pcb, NULL);
tcp_abort(ud->tcp_pcb);
}
ud->tcp_pcb = NULL;
break;
case TYPE_TCP_SERVER:
tcp_close(ud->tcp_pcb);
ud->tcp_pcb = NULL;
break;
case TYPE_UDP_SOCKET:
udp_remove(ud->udp_pcb);
ud->udp_pcb = NULL;
break;
}
} else {
return luaL_error(L, "not connected");
}
if (ud->type == TYPE_TCP_SERVER ||
(ud->pcb == NULL && ud->client.wait_dns == 0)) {
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;
}
int net_delete( lua_State *L ) {
lnet_userdata *ud = net_get_udata(L);
if (!ud) return luaL_error(L, "no user data");
if (ud->pcb) {
switch (ud->type) {
case TYPE_TCP_CLIENT:
tcp_arg(ud->tcp_pcb, NULL);
tcp_abort(ud->tcp_pcb);
ud->tcp_pcb = NULL;
break;
case TYPE_TCP_SERVER:
tcp_close(ud->tcp_pcb);
ud->tcp_pcb = NULL;
break;
case TYPE_UDP_SOCKET:
udp_remove(ud->udp_pcb);
ud->udp_pcb = NULL;
break;
}
}
switch (ud->type) {
case TYPE_TCP_CLIENT:
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref);
ud->client.cb_connect_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_disconnect_ref);
ud->client.cb_disconnect_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_reconnect_ref);
ud->client.cb_reconnect_ref = LUA_NOREF;
case TYPE_UDP_SOCKET:
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref);
ud->client.cb_dns_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref);
ud->client.cb_receive_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
ud->client.cb_sent_ref = LUA_NOREF;
break;
case TYPE_TCP_SERVER:
luaL_unref(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref);
ud->server.cb_accept_ref = LUA_NOREF;
break;
}
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;
}
// --- Multicast
static int net_multicastJoinLeave( lua_State *L, int join) {
size_t il;
ip4_addr_t multicast_addr;
ip4_addr_t if_addr;
const char *multicast_ip;
const char *if_ip;
NODE_DBG("net_multicastJoin is called.\n");
if(! lua_isstring(L,1) ) return luaL_error( L, "wrong arg type" );
if_ip = luaL_checklstring( L, 1, &il );
if (if_ip != NULL)
if ( if_ip[0] == '\0' || strcasecmp(if_ip,"any") == 0)
{
if_ip = "0.0.0.0";
il = 7;
}
if (if_ip == NULL || il > 15 || il < 7) return luaL_error( L, "invalid if ip" );
ip4addr_aton (if_ip, &if_addr);
if(! lua_isstring(L,2) ) return luaL_error( L, "wrong arg type" );
multicast_ip = luaL_checklstring( L, 2, &il );
if (multicast_ip == NULL || il > 15 || il < 7) return luaL_error( L, "invalid multicast ip" );
ip4addr_aton (multicast_ip, &multicast_addr);
if (join)
{
igmp_joingroup(&if_addr, &multicast_addr);
}
else
{
igmp_leavegroup(&if_addr, &multicast_addr);
}
return 0;
}
// Lua: net.multicastJoin(ifip, multicastip)
// if ifip "" or "any" all interfaces are affected
static int net_multicastJoin( lua_State* L ) {
return net_multicastJoinLeave(L,1);
}
// Lua: net.multicastLeave(ifip, multicastip)
// if ifip "" or "any" all interfaces are affected
static int net_multicastLeave( lua_State* L ) {
return net_multicastJoinLeave(L,0);
}
// --- DNS
static void net_dns_static_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
lnet_event *ev = (lnet_event *)callback_arg;
ev->resolved_ip = ipaddr ? *ipaddr : ip_addr_any;
if (!task_post_medium (net_event, (task_param_t)ev))
free (ev);
}
// Lua: net.dns.resolve( domain, function(ip) )
static int net_dns_static( lua_State* L ) {
size_t dl;
const char* domain = luaL_checklstring(L, 1, &dl);
if (!domain && dl > 128) {
return luaL_error(L, "wrong domain");
}
// Note: this will be free'd using regular free(), so can't luaM_malloc()
lnet_event *ev = (lnet_event *)malloc (sizeof (lnet_event));
if (!ev)
return luaL_error (L, "out of memory");
ev->event = DNSSTATIC;
luaL_checkanyfunction(L, 2);
lua_pushvalue(L, 2); // copy argument (func) to the top of stack
ev->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
err_t err = dns_gethostbyname(
domain, &ev->resolved_ip, net_dns_static_cb, ev);
if (err == ERR_OK) {
net_dns_static_cb(domain, &ev->resolved_ip, ev);
return 0;
} else if (err == ERR_INPROGRESS) {
return 0;
} else {
int e = lwip_lua_checkerr(L, err);
free(ev);
return e;
}
return 0;
}
// Lua: s = net.dns.setdnsserver(ip_addr, [index])
static int net_setdnsserver( lua_State* L ) {
size_t l;
ip_addr_t ipaddr;
const char *server = luaL_checklstring( L, 1, &l );
if (l>16 || server == NULL ||
!ipaddr_aton (server, &ipaddr) || ip_addr_isany (&ipaddr))
return luaL_error( L, "invalid dns server ip" );
int numdns = luaL_optint(L, 2, 0);
if (numdns >= DNS_MAX_SERVERS)
return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1);
dns_setserver(numdns,&ipaddr);
return 0;
}
// Lua: s = net.dns.getdnsserver([index])
static int net_getdnsserver( lua_State* L ) {
int numdns = luaL_optint(L, 1, 0);
if (numdns >= DNS_MAX_SERVERS)
return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1);
// ip_addr_t ipaddr;
// dns_getserver(numdns,&ipaddr);
// Bug fix by @md5crypt https://github.com/nodemcu/nodemcu-firmware/pull/500
ip_addr_t ipaddr = dns_getserver(numdns);
if ( ip_addr_isany(&ipaddr) ) {
lua_pushnil( L );
} else {
char temp[IP_STR_SZ];
ipstr (temp, &ipaddr);
lua_pushstring( L, temp );
}
return 1;
}
// --- Lua event dispatch
static void ldnsfound_cb (lua_State *L, lnet_userdata *ud, ip_addr_t *addr) {
if (ud->self_ref != LUA_NOREF && ud->client.cb_dns_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
if (!ip_addr_isany (addr)) {
char iptmp[IP_STR_SZ];
ipstr (iptmp, addr);
lua_pushstring(L, iptmp);
} else {
lua_pushnil(L);
}
lua_call(L, 2, 0);
}
ud->client.wait_dns --;
if (ud->pcb && ud->type == TYPE_TCP_CLIENT && ud->tcp_pcb->state == CLOSED) {
tcp_connect(ud->tcp_pcb, addr, ud->tcp_pcb->remote_port, net_connected_cb);
} else if (!ud->pcb && ud->client.wait_dns == 0) {
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);
}
}
static void ldnsstatic_cb (lua_State *L, int cb_ref, ip_addr_t *addr) {
if (cb_ref == LUA_NOREF)
return;
lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref);
luaL_unref (L, LUA_REGISTRYINDEX, cb_ref);
if (!ip_addr_isany (addr)) {
char iptmp[IP_STR_SZ];
ipstr (iptmp, addr);
lua_pushstring(L, iptmp);
} else {
lua_pushnil(L);
}
lua_call(L, 1, 0);
}
static void lconnected_cb (lua_State *L, lnet_userdata *ud) {
if (ud->self_ref != LUA_NOREF && ud->client.cb_connect_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
}
}
static void laccept_cb (lua_State *L, lnet_userdata *ud, struct tcp_pcb *newpcb) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref);
lnet_userdata *nud = net_create(L, TYPE_TCP_CLIENT);
lua_pushvalue(L, 2);
nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
nud->tcp_pcb = newpcb;
tcp_arg(nud->tcp_pcb, nud);
tcp_err(nud->tcp_pcb, net_err_cb);
tcp_recv(nud->tcp_pcb, net_tcp_recv_cb);
tcp_sent(nud->tcp_pcb, net_sent_cb);
tcp_accepted(ud->tcp_pcb);
lua_call(L, 1, 0);
}
static void lrecv_cb (lua_State *L, lnet_userdata *ud, const lnet_recvdata *rd) {
if (ud->client.cb_receive_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref);
int num_args = 2;
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushlstring(L, rd->payload, rd->payload_len);
if (ud->type == TYPE_UDP_SOCKET) {
num_args += 2;
char iptmp[IP_STR_SZ];
ipstr (iptmp, &rd->src_ip);
lua_pushinteger(L, rd->src_port);
lua_pushstring(L, iptmp);
}
lua_call(L, num_args, 0);
}
}
static void lsent_cb (lua_State *L, lnet_userdata *ud) {
if (ud->client.cb_sent_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
}
}
static void lerr_cb (lua_State *L, lnet_userdata *ud, err_t err)
{
int ref;
if (err != ERR_OK && ud->client.cb_reconnect_ref != LUA_NOREF)
ref = ud->client.cb_reconnect_ref;
else ref = ud->client.cb_disconnect_ref;
if (ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushinteger(L, err);
lua_call(L, 2, 0);
}
if (ud->client.wait_dns == 0) {
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);
}
}
static void handle_net_event (task_param_t param, task_prio_t prio)
{
lnet_event *ev = (lnet_event *)param;
(void)prio;
lua_State *L = lua_getstate();
switch (ev->event)
{
case DNSFOUND: ldnsfound_cb (L, ev->ud, &ev->resolved_ip); break;
case DNSSTATIC: ldnsstatic_cb (L, ev->cb_ref, &ev->resolved_ip); break;
case CONNECTED: lconnected_cb (L, ev->ud); break;
case ACCEPT: laccept_cb (L, ev->ud, ev->accept_newpcb); break;
case RECVDATA: lrecv_cb (L, ev->ud, &ev->recvdata); break;
case SENTDATA: lsent_cb (L, ev->ud); break;
case ERR: lerr_cb (L, ev->ud, ev->err); break;
}
free (ev);
}
// --- Tables
extern const LUA_REG_TYPE tls_cert_map[];
// Module function map
static const LUA_REG_TYPE net_tcpserver_map[] = {
{ LSTRKEY( "listen" ), LFUNCVAL( net_listen ) },
{ LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_tcpserver_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE net_tcpsocket_map[] = {
{ LSTRKEY( "connect" ), LFUNCVAL( net_connect ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "on" ), LFUNCVAL( net_on ) },
{ LSTRKEY( "send" ), LFUNCVAL( net_send ) },
{ LSTRKEY( "hold" ), LFUNCVAL( net_hold ) },
{ LSTRKEY( "unhold" ), LFUNCVAL( net_unhold ) },
{ LSTRKEY( "dns" ), LFUNCVAL( net_dns ) },
{ LSTRKEY( "getpeer" ), LFUNCVAL( net_getpeer ) },
{ LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_tcpsocket_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE net_udpsocket_map[] = {
{ LSTRKEY( "listen" ), LFUNCVAL( net_listen ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "on" ), LFUNCVAL( net_on ) },
{ LSTRKEY( "send" ), LFUNCVAL( net_send ) },
{ LSTRKEY( "dns" ), LFUNCVAL( net_dns ) },
{ LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_udpsocket_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE net_dns_map[] = {
{ LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) },
{ LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) },
{ LSTRKEY( "resolve" ), LFUNCVAL( net_dns_static ) },
{ 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 ) },
#ifdef CLIENT_SSL_ENABLE
{ LSTRKEY( "cert" ), LROVAL( tls_cert_map ) },
#endif
{ LSTRKEY( "TCP" ), LNUMVAL( TYPE_TCP ) },
{ LSTRKEY( "UDP" ), LNUMVAL( TYPE_UDP ) },
{ LSTRKEY( "__metatable" ), LROVAL( net_map ) },
{ LNILKEY, LNILVAL }
};
int luaopen_net( lua_State *L ) {
igmp_init();
luaL_rometatable(L, NET_TABLE_TCP_SERVER, (void *)net_tcpserver_map);
luaL_rometatable(L, NET_TABLE_TCP_CLIENT, (void *)net_tcpsocket_map);
luaL_rometatable(L, NET_TABLE_UDP_SOCKET, (void *)net_udpsocket_map);
net_event = task_get_id (handle_net_event);
return 0;
}
NODEMCU_MODULE(NET, "net", net_map, luaopen_net);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment