Commit 3adba91b authored by Yury Popov's avatar Yury Popov Committed by Arnim Läuger
Browse files

Net functionality rewrite on top of LWIP (#1379)

* Rewrite net.dns.resolve on LWIP

* Move IGMP (net multicast) to LWIP

* Cleanup net module

* Move secure connection operations to tls module

* Net module on LWIP

* Server timeout parameter

* TCP hold

* Fixes in documentation

* Documentation fixes

* Note TLS module depends on net

* Add TLS module to user_modules.h

* Callback on connect event

* Fix depends net module on tls module

* Fix unhold exponential time issue
parent 57950413
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
//#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_STRUCT
//#define LUA_USE_MODULES_SWITEC //#define LUA_USE_MODULES_SWITEC
//#define LUA_USE_MODULES_TM1829 //#define LUA_USE_MODULES_TM1829
#define LUA_USE_MODULES_TLS
#define LUA_USE_MODULES_TMR #define LUA_USE_MODULES_TMR
//#define LUA_USE_MODULES_TSL2561 //#define LUA_USE_MODULES_TSL2561
//#define LUA_USE_MODULES_U8G //#define LUA_USE_MODULES_U8G
......
...@@ -10,1363 +10,780 @@ ...@@ -10,1363 +10,780 @@
#include "c_types.h" #include "c_types.h"
#include "mem.h" #include "mem.h"
#include "osapi.h"
#include "lwip/err.h"
#include "lwip/ip_addr.h" #include "lwip/ip_addr.h"
#include "espconn.h"
#include "lwip/dns.h" #include "lwip/dns.h"
#include "lwip/igmp.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
#define TCP ESPCONN_TCP #if defined(CLIENT_SSL_ENABLE) && defined(LUA_USE_MODULES_NET) && defined(LUA_USE_MODULES_TLS)
#define UDP ESPCONN_UDP #define TLS_MODULE_PRESENT
static ip_addr_t host_ip; // for dns
#ifdef HAVE_SSL_SERVER_CRT
#include HAVE_SSL_SERVER_CRT
#else
__attribute__((section(".servercert.flash"))) unsigned char net_server_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
#endif #endif
__attribute__((section(".clientcert.flash"))) unsigned char net_client_cert_area[INTERNAL_FLASH_SECTOR_SIZE]; typedef enum net_type {
TYPE_TCP_SERVER = 0,
TYPE_TCP_CLIENT,
TYPE_UDP_SOCKET
} net_type;
#if 0 typedef const char net_table_name[14];
static int expose_array(lua_State* L, char *array, unsigned short len);
#endif
#define MAX_SOCKET 5 static const net_table_name NET_TABLES[] = {
static int socket_num = 0; "net.tcpserver",
static int socket[MAX_SOCKET]; "net.tcpsocket",
static int tcpserver_cb_connect_ref = LUA_NOREF; // for tcp server connected callback "net.udpsocket"
static uint16_t tcp_server_timeover = 30; };
#define NET_TABLE_TCP_SERVER NET_TABLES[0]
#define NET_TABLE_TCP_CLIENT NET_TABLES[1]
#define NET_TABLE_UDP_SOCKET NET_TABLES[2]
static struct espconn *pTcpServer = NULL; #define TYPE_TCP TYPE_TCP_CLIENT
static struct espconn *pUdpServer = NULL; #define TYPE_UDP TYPE_UDP_SOCKET
typedef struct lnet_userdata typedef struct lnet_userdata {
{ enum net_type type;
struct espconn *pesp_conn;
int self_ref; int self_ref;
int cb_connect_ref; union {
int cb_reconnect_ref; struct tcp_pcb *tcp_pcb;
int cb_disconnect_ref; struct udp_pcb *udp_pcb;
int cb_receive_ref; void *pcb;
int cb_send_ref; };
int cb_dns_found_ref; union {
#ifdef CLIENT_SSL_ENABLE struct {
uint8_t secure; int cb_accept_ref;
#endif int timeout;
}lnet_userdata; } 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;
#pragma mark - 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");
}
}
#pragma mark - 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);
static void net_server_disconnected(void *arg) // for tcp server only ud->type = type;
{ ud->self_ref = LUA_NOREF;
NODE_DBG("net_server_disconnected is called.\n"); ud->pcb = NULL;
struct espconn *pesp_conn = arg;
if(pesp_conn == NULL) switch (type) {
return; case TYPE_TCP_CLIENT:
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; ud->client.cb_connect_ref = LUA_NOREF;
if(nud == NULL) ud->client.cb_reconnect_ref = LUA_NOREF;
return; ud->client.cb_disconnect_ref = LUA_NOREF;
lua_State *L = lua_getstate(); ud->client.hold = 0;
#if 0 case TYPE_UDP_SOCKET:
char temp[20] = {0}; ud->client.wait_dns = 0;
c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); ud->client.cb_dns_ref = LUA_NOREF;
NODE_DBG("remote "); ud->client.cb_receive_ref = LUA_NOREF;
NODE_DBG(temp); ud->client.cb_sent_ref = LUA_NOREF;
NODE_DBG(":"); break;
NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); case TYPE_TCP_SERVER:
NODE_DBG(" disconnected.\n"); ud->server.cb_accept_ref = LUA_NOREF;
#endif
if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua
lua_call(L, 1, 0);
}
int i;
lua_gc(L, LUA_GCSTOP, 0);
for(i=0;i<MAX_SOCKET;i++){
if( (LUA_NOREF!=socket[i]) && (socket[i] == nud->self_ref) ){
// found the saved client
nud->pesp_conn->reverse = NULL;
nud->pesp_conn = NULL; // the espconn is made by low level sdk, do not need to free, delete() will not free it.
nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
luaL_unref(L, LUA_REGISTRYINDEX, socket[i]);
socket[i] = LUA_NOREF;
socket_num--;
break; break;
}
}
lua_gc(L, LUA_GCRESTART, 0);
}
static void net_socket_disconnected(void *arg) // tcp only
{
NODE_DBG("net_socket_disconnected is called.\n");
struct espconn *pesp_conn = arg;
if(pesp_conn == NULL)
return;
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
lua_State *L = lua_getstate();
if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua
lua_call(L, 1, 0);
}
if(pesp_conn->proto.tcp)
c_free(pesp_conn->proto.tcp);
pesp_conn->proto.tcp = NULL;
if(nud->pesp_conn)
c_free(nud->pesp_conn);
nud->pesp_conn = NULL; // espconn is already disconnected
lua_gc(L, LUA_GCSTOP, 0);
if(nud->self_ref != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
} }
lua_gc(L, LUA_GCRESTART, 0); return ud;
}
static void net_server_reconnected(void *arg, sint8_t err)
{
NODE_DBG("net_server_reconnected is called.\n");
net_server_disconnected(arg);
}
static void net_socket_reconnected(void *arg, sint8_t err)
{
NODE_DBG("net_socket_reconnected is called.\n");
net_socket_disconnected(arg);
} }
static void net_socket_received(void *arg, char *pdata, unsigned short len) #pragma mark - LWIP callbacks
{
NODE_DBG("net_socket_received is called.\n");
struct espconn *pesp_conn = arg;
if(pesp_conn == NULL)
return;
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
if(nud->cb_receive_ref == LUA_NOREF)
return;
if(nud->self_ref == LUA_NOREF)
return;
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_receive_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua
// expose_array(L, pdata, len);
// *(pdata+len) = 0;
// NODE_DBG(pdata);
// NODE_DBG("\n");
lua_pushlstring(L, pdata, len);
// lua_pushinteger(L, len);
lua_call(L, 2, 0);
}
static void net_socket_sent(void *arg) static void net_err_cb(void *arg, err_t err) {
{ lnet_userdata *ud = (lnet_userdata*)arg;
// NODE_DBG("net_socket_sent is called.\n"); if (!ud || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return;
struct espconn *pesp_conn = arg; ud->pcb = NULL; // Will be freed at LWIP level
if(pesp_conn == NULL)
return;
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
if(nud->cb_send_ref == LUA_NOREF)
return;
if(nud->self_ref == LUA_NOREF)
return;
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_send_ref); int ref;
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua if (err != ERR_OK && ud->client.cb_reconnect_ref != LUA_NOREF)
lua_call(L, 1, 0); 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 net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) static err_t net_connected_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
{ lnet_userdata *ud = (lnet_userdata*)arg;
NODE_DBG("net_dns_found is called.\n"); if (!ud || ud->pcb != tpcb) return ERR_ABRT;
struct espconn *pesp_conn = arg; if (err != ERR_OK) {
if(pesp_conn == NULL){ net_err_cb(arg, err);
NODE_DBG("pesp_conn null.\n"); return ERR_ABRT;
return;
}
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL){
NODE_DBG("nud null.\n");
return;
}
if(nud->cb_dns_found_ref == LUA_NOREF){
NODE_DBG("cb_dns_found_ref null.\n");
return;
} }
lua_State *L = lua_getstate();
if(nud->self_ref == LUA_NOREF){ if (ud->self_ref != LUA_NOREF && ud->client.cb_connect_ref != LUA_NOREF) {
NODE_DBG("self_ref null.\n"); lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref);
return; lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
} }
return ERR_OK;
}
static void net_dns_cb(const char *name, ip_addr_t *ipaddr, void *arg) {
ip_addr_t addr;
if (ipaddr != NULL) addr = *ipaddr;
else addr.addr = 0xFFFFFFFF;
lnet_userdata *ud = (lnet_userdata*)arg;
if (!ud) return;
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
if (ud->self_ref != LUA_NOREF && ud->client.cb_dns_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
if (addr.addr != 0xFFFFFFFF) {
if(ipaddr == NULL) char iptmp[16];
{ bzero(iptmp, 16);
NODE_DBG( "DNS Fail!\n" ); ets_sprintf(iptmp, IPSTR, IP2STR(&addr.addr));
lua_pushnil(L); lua_pushstring(L, iptmp);
}else{ } else {
// ipaddr->addr is a uint32_t ip lua_pushnil(L);
char ip_str[20];
c_memset(ip_str, 0, sizeof(ip_str));
if(ipaddr->addr != 0)
{
c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr)));
} }
lua_pushstring(L, ip_str); // the ip para lua_call(L, 2, 0);
} }
// "enhanced" end ud->client.wait_dns --;
if (ud->pcb && ud->type == TYPE_TCP_CLIENT && ud->tcp_pcb->state == CLOSED) {
lua_call(L, 2, 0); tcp_connect(ud->tcp_pcb, &addr, ud->tcp_pcb->remote_port, net_connected_cb);
} else if (!ud->pcb && ud->client.wait_dns == 0) {
end:
if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0)
|| (pesp_conn->type == ESPCONN_UDP && pesp_conn->proto.udp->remote_port == 0) ){
lua_gc(L, LUA_GCSTOP, 0); lua_gc(L, LUA_GCSTOP, 0);
if(nud->self_ref != LUA_NOREF){ luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); ud->self_ref = LUA_NOREF;
nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
}
lua_gc(L, LUA_GCRESTART, 0); lua_gc(L, LUA_GCRESTART, 0);
} }
} }
static void net_server_connected(void *arg) // for tcp only static void net_recv_cb(lnet_userdata *ud, struct pbuf *p, ip_addr_t *addr, u16_t port) {
{ if (ud->client.cb_receive_ref == LUA_NOREF) {
NODE_DBG("net_server_connected is called.\n"); pbuf_free(p);
struct espconn *pesp_conn = arg;
int i = 0;
lnet_userdata *skt = NULL;
if(pesp_conn == NULL)
return; return;
#if 0
char temp[20] = {0};
c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) );
NODE_DBG("remote ");
NODE_DBG(temp);
NODE_DBG(":");
NODE_DBG("%d",pesp_conn->proto.tcp->remote_port);
NODE_DBG(" connected.\n");
#endif
for(i=0;i<MAX_SOCKET;i++){
if(socket[i] == LUA_NOREF) // found empty slot
{
break;
}
} }
if(i>=MAX_SOCKET) // can't create more socket
{
NODE_ERR("MAX_SOCKET\n");
pesp_conn->reverse = NULL; // not accept this conn
if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
espconn_disconnect(pesp_conn);
return;
}
if(tcpserver_cb_connect_ref == LUA_NOREF)
return;
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
int num_args = 2;
lua_rawgeti(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); // get function lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref);
// create a new client object lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
skt = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata)); lua_pushlstring(L, p->payload, p->len);
if (ud->type == TYPE_UDP_SOCKET) {
if(!skt){ num_args += 2;
NODE_ERR("can't newudata\n"); char iptmp[16];
lua_pop(L, 1); bzero(iptmp, 16);
ets_sprintf(iptmp, IPSTR, IP2STR(&addr->addr));
lua_pushinteger(L, port);
lua_pushstring(L, iptmp);
}
lua_call(L, num_args, 0);
pbuf_free(p);
}
static void net_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, 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; return;
} }
// set its metatable net_recv_cb(ud, p, addr, port);
luaL_getmetatable(L, "net.socket");
lua_setmetatable(L, -2);
// pre-initialize it, in case of errors
skt->self_ref = LUA_NOREF;
lua_pushvalue(L, -1); // copy the top of stack
skt->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); // ref to it self, for module api to find the userdata
socket[i] = skt->self_ref; // save to socket array
socket_num++;
skt->cb_connect_ref = LUA_NOREF; // this socket already connected
skt->cb_reconnect_ref = LUA_NOREF;
skt->cb_disconnect_ref = LUA_NOREF;
skt->cb_receive_ref = LUA_NOREF;
skt->cb_send_ref = LUA_NOREF;
skt->cb_dns_found_ref = LUA_NOREF;
#ifdef CLIENT_SSL_ENABLE
skt->secure = 0; // as a server SSL is not supported.
#endif
skt->pesp_conn = pesp_conn; // point to the espconn made by low level sdk
pesp_conn->reverse = skt; // let espcon carray the info of this userdata(net.socket)
espconn_regist_recvcb(pesp_conn, net_socket_received);
espconn_regist_sentcb(pesp_conn, net_socket_sent);
espconn_regist_disconcb(pesp_conn, net_server_disconnected);
espconn_regist_reconcb(pesp_conn, net_server_reconnected);
// now socket[i] has the client ref, and stack top has the userdata
lua_call(L, 1, 0); // function(conn)
} }
static void net_socket_connected(void *arg) 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;
NODE_DBG("net_socket_connected is called.\n"); if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF)
struct espconn *pesp_conn = arg; return ERR_ABRT;
if(pesp_conn == NULL) if (!p) {
return; net_err_cb(arg, err);
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; return tcp_close(tpcb);
if(nud == NULL) }
return; net_recv_cb(ud, p, 0, 0);
// can receive and send data, even if there is no connected callback in lua. tcp_recved(tpcb, ud->client.hold ? 0 : TCP_WND);
espconn_regist_recvcb(pesp_conn, net_socket_received); return ERR_OK;
espconn_regist_sentcb(pesp_conn, net_socket_sent); }
espconn_regist_disconcb(pesp_conn, net_socket_disconnected);
if(nud->cb_connect_ref == LUA_NOREF) static err_t net_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) {
return; lnet_userdata *ud = (lnet_userdata*)arg;
if(nud->self_ref == LUA_NOREF) if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return ERR_ABRT;
return; if (ud->client.cb_sent_ref == LUA_NOREF) return ERR_OK;
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_connect_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0); lua_call(L, 1, 0);
return ERR_OK;
} }
// Lua: s = net.create(type, secure/timeout, function(conn)) static err_t net_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) {
static int net_create( lua_State* L, const char* mt ) lnet_userdata *ud = (lnet_userdata*)arg;
{ if (!ud || ud->type != TYPE_TCP_SERVER || !ud->pcb) return ERR_ABRT;
NODE_DBG("net_create is called.\n"); if (ud->self_ref == LUA_NOREF || ud->server.cb_accept_ref == LUA_NOREF) return ERR_ABRT;
struct espconn *pesp_conn = NULL;
lnet_userdata *nud, *temp = NULL;
unsigned type;
#ifdef CLIENT_SSL_ENABLE
unsigned secure = 0;
#endif
uint8_t stack = 1;
bool isserver = false;
if (mt!=NULL && c_strcmp(mt, "net.server")==0)
isserver = true;
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
isserver = false;
else
{
NODE_DBG("wrong metatable for net_create.\n");
return 0;
}
type = luaL_checkinteger( L, stack );
if ( type != ESPCONN_TCP && type != ESPCONN_UDP )
return luaL_error( L, "wrong arg type" );
stack++;
#ifdef CLIENT_SSL_ENABLE
if(!isserver){
if ( lua_isnumber(L, stack) )
{
secure = lua_tointeger(L, stack);
stack++;
if ( secure != 0 && secure != 1 ){
return luaL_error( L, "wrong arg type" );
}
} else {
secure = 0; // default to 0
}
}
#endif
if(isserver && type == ESPCONN_TCP){
if ( lua_isnumber(L, stack) )
{
unsigned to = lua_tointeger(L, stack);
stack++;
if ( to < 1 || to > 28800 ){
return luaL_error( L, "wrong arg type" );
}
tcp_server_timeover = (uint16_t)to;
} else {
tcp_server_timeover = 30; // default to 30
}
}
// create a object
nud = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata));
// pre-initialize it, in case of errors
nud->self_ref = LUA_NOREF;
nud->cb_connect_ref = LUA_NOREF;
nud->cb_reconnect_ref = LUA_NOREF;
nud->cb_disconnect_ref = LUA_NOREF;
nud->cb_receive_ref = LUA_NOREF;
nud->cb_send_ref = LUA_NOREF;
nud->cb_dns_found_ref = LUA_NOREF;
nud->pesp_conn = NULL;
#ifdef CLIENT_SSL_ENABLE
nud->secure = secure;
#endif
// set its metatable lua_State *L = lua_getstate();
luaL_getmetatable(L, mt); lua_rawgeti(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref);
lua_setmetatable(L, -2);
// create the espconn struct
if(isserver && type==ESPCONN_TCP && pTcpServer){
if(tcpserver_cb_connect_ref != LUA_NOREF){ // self_ref should be unref in close()
lua_pop(L,1);
return luaL_error(L, "only one tcp server allowed");
}
pesp_conn = nud->pesp_conn = pTcpServer;
} else if(isserver && type==ESPCONN_UDP && pUdpServer){
temp = (lnet_userdata *)pUdpServer->reverse;
if(temp && temp->self_ref != LUA_NOREF){
lua_pop(L,1);
return luaL_error(L, "only one udp server allowed");
}
pesp_conn = nud->pesp_conn = pUdpServer;
} else {
pesp_conn = nud->pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn));
if(!pesp_conn)
return luaL_error(L, "not enough memory");
pesp_conn->proto.tcp = NULL;
pesp_conn->proto.udp = NULL;
pesp_conn->reverse = NULL;
if( type==ESPCONN_TCP )
{
pesp_conn->proto.tcp = (esp_tcp *)c_zalloc(sizeof(esp_tcp));
if(!pesp_conn->proto.tcp){
c_free(pesp_conn);
pesp_conn = nud->pesp_conn = NULL;
return luaL_error(L, "not enough memory");
}
NODE_DBG("TCP server/socket is set.\n");
}
else if( type==ESPCONN_UDP )
{
pesp_conn->proto.udp = (esp_udp *)c_zalloc(sizeof(esp_udp));
if(!pesp_conn->proto.udp){
c_free(pesp_conn);
pesp_conn = nud->pesp_conn = NULL;
return luaL_error(L, "not enough memory");
}
NODE_DBG("UDP server/socket is set.\n");
}
}
pesp_conn->type = type;
pesp_conn->state = ESPCONN_NONE;
// reverse is for the callback function
pesp_conn->reverse = nud;
if(isserver && type==ESPCONN_TCP && pTcpServer==NULL){
pTcpServer = pesp_conn;
} else if(isserver && type==ESPCONN_UDP && pUdpServer==NULL){
pUdpServer = pesp_conn;
}
// if call back function is specified, call it with para userdata
// luaL_checkanyfunction(L, 2);
if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
lua_pushvalue(L, stack); // copy argument (func) to the top of stack
lua_pushvalue(L, -2); // copy the self_ref(userdata) to the top
lua_call(L, 1, 0);
}
return 1;
}
// static int net_close( lua_State* L, const char* mt );
// Lua: net.delete( socket/server )
// call close() first
// server: disconnect server, unref everything
// socket: unref everything
static int net_delete( lua_State* L, const char* mt )
{
NODE_DBG("net_delete is called.\n");
bool isserver = false;
if (mt!=NULL && c_strcmp(mt, "net.server")==0)
isserver = true;
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
isserver = false;
else
{
NODE_DBG("wrong metatable for net_delete.\n");
return 0;
}
// net_close( L, mt ); // close it first 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);
nud->tcp_pcb->so_options |= SOF_KEEPALIVE;
nud->tcp_pcb->keep_idle = ud->server.timeout * 1000;
nud->tcp_pcb->keep_cnt = 1;
tcp_accepted(ud->tcp_pcb);
lnet_userdata *nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); lua_call(L, 1, 0);
luaL_argcheck(L, nud, 1, "Server/Socket expected");
if(nud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if(nud->pesp_conn){ // for client connected to tcp server, this should set NULL in disconnect cb
nud->pesp_conn->reverse = NULL;
if(!isserver) // socket is freed here
{
if(nud->pesp_conn->type == ESPCONN_UDP){
if(nud->pesp_conn->proto.udp)
c_free(nud->pesp_conn->proto.udp);
nud->pesp_conn->proto.udp = NULL;
} else if (nud->pesp_conn->type == ESPCONN_TCP) {
if(nud->pesp_conn->proto.tcp)
c_free(nud->pesp_conn->proto.tcp);
nud->pesp_conn->proto.tcp = NULL;
}
c_free(nud->pesp_conn);
}
nud->pesp_conn = NULL; // for socket, it will free this when disconnected
}
// free (unref) callback ref return net_connected_cb(nud, nud->tcp_pcb, ERR_OK);
if(LUA_NOREF!=nud->cb_connect_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
nud->cb_connect_ref = LUA_NOREF;
}
if(LUA_NOREF!=nud->cb_reconnect_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref);
nud->cb_reconnect_ref = LUA_NOREF;
}
if(LUA_NOREF!=nud->cb_disconnect_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
nud->cb_disconnect_ref = LUA_NOREF;
}
if(LUA_NOREF!=nud->cb_receive_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref);
nud->cb_receive_ref = LUA_NOREF;
}
if(LUA_NOREF!=nud->cb_send_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
nud->cb_send_ref = LUA_NOREF;
}
if(LUA_NOREF!=nud->cb_dns_found_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
nud->cb_dns_found_ref = LUA_NOREF;
}
lua_gc(L, LUA_GCSTOP, 0);
if(LUA_NOREF!=nud->self_ref){
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = LUA_NOREF;
}
lua_gc(L, LUA_GCRESTART, 0);
return 0;
} }
static void socket_connect(struct espconn *pesp_conn) #pragma mark - Lua API - create
{
if(pesp_conn == NULL)
return;
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
if( pesp_conn->type == ESPCONN_TCP ) #ifdef TLS_MODULE_PRESENT
{ extern int tls_socket_create( lua_State *L );
#ifdef CLIENT_SSL_ENABLE
if(nud->secure){
espconn_secure_connect(pesp_conn);
}
else
#endif #endif
{
espconn_connect(pesp_conn);
}
}
else if (pesp_conn->type == ESPCONN_UDP)
{
espconn_create(pesp_conn);
}
NODE_DBG("socket_connect is called.\n");
}
static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg); // Lua: net.createUDPSocket()
static int dns_reconn_count = 0; int net_createUDPSocket( lua_State *L ) {
static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) net_create(L, TYPE_UDP_SOCKET);
{ return 1;
NODE_DBG("socket_dns_found is called.\n");
struct espconn *pesp_conn = arg;
if(pesp_conn == NULL){
NODE_DBG("pesp_conn null.\n");
return;
}
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
lua_State *L = lua_getstate();
if(ipaddr == NULL)
{
dns_reconn_count++;
if( dns_reconn_count >= 5 ){
NODE_ERR( "DNS Fail!\n" );
lua_gc(L, LUA_GCSTOP, 0);
if(nud->self_ref != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
}
lua_gc(L, LUA_GCRESTART, 0);
return;
}
NODE_ERR( "DNS retry %d!\n", dns_reconn_count );
host_ip.addr = 0;
espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_found);
return;
}
// ipaddr->addr is a uint32_t ip
if(ipaddr->addr != 0)
{
dns_reconn_count = 0;
if( pesp_conn->type == ESPCONN_TCP )
{
c_memcpy(pesp_conn->proto.tcp->remote_ip, &(ipaddr->addr), 4);
NODE_DBG("TCP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
NODE_DBG("\n");
}
else if (pesp_conn->type == ESPCONN_UDP)
{
c_memcpy(pesp_conn->proto.udp->remote_ip, &(ipaddr->addr), 4);
NODE_DBG("UDP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
NODE_DBG("\n");
}
socket_connect(pesp_conn);
}
} }
// Lua: server:listen( port, ip, function(con) ) // Lua: net.createServer(type, timeout)
// Lua: socket:connect( port, ip, function(con) ) int net_createServer( lua_State *L ) {
static int net_start( lua_State* L, const char* mt ) int type, timeout;
{
NODE_DBG("net_start is called.\n");
struct espconn *pesp_conn = NULL;
lnet_userdata *nud;
unsigned port;
size_t il;
bool isserver = false;
ip_addr_t ipaddr;
const char *domain;
uint8_t stack = 1;
if (mt!=NULL && c_strcmp(mt, "net.server")==0)
isserver = true;
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
isserver = false;
else
{
NODE_DBG("wrong metatable for net_start.\n");
return 0;
}
nud = (lnet_userdata *)luaL_checkudata(L, stack, mt);
luaL_argcheck(L, nud, stack, "Server/Socket expected");
stack++;
if(nud==NULL){ type = luaL_optlong(L, 1, TYPE_TCP);
NODE_DBG("userdata is nil.\n"); timeout = luaL_optlong(L, 2, 30);
return 0;
}
if(nud->pesp_conn == NULL){ if (type == TYPE_UDP) return net_createUDPSocket( L );
NODE_DBG("nud->pesp_conn is NULL.\n"); if (type != TYPE_TCP) return luaL_error(L, "invalid type");
return 0;
}
pesp_conn = nud->pesp_conn;
port = luaL_checkinteger( L, stack );
stack++;
if( pesp_conn->type == ESPCONN_TCP )
{
if(isserver)
pesp_conn->proto.tcp->local_port = port;
else{
pesp_conn->proto.tcp->remote_port = port;
pesp_conn->proto.tcp->local_port = espconn_port();
}
NODE_DBG("TCP port is set: %d.\n", port);
}
else if (pesp_conn->type == ESPCONN_UDP)
{
if(isserver)
pesp_conn->proto.udp->local_port = port;
else{
pesp_conn->proto.udp->remote_port = port;
pesp_conn->proto.udp->local_port = espconn_port();
}
NODE_DBG("UDP port is set: %d.\n", port);
}
if( lua_isstring(L,stack) ) // deal with the domain string lnet_userdata *u = net_create(L, TYPE_TCP_SERVER);
{ u->server.timeout = timeout;
domain = luaL_checklstring( L, stack, &il ); return 1;
stack++; }
if (domain == NULL)
{
if(isserver)
domain = "0.0.0.0";
else
domain = "127.0.0.1";
}
ipaddr.addr = ipaddr_addr(domain);
if( pesp_conn->type == ESPCONN_TCP )
{
if(isserver)
c_memcpy(pesp_conn->proto.tcp->local_ip, &ipaddr.addr, 4);
else
c_memcpy(pesp_conn->proto.tcp->remote_ip, &ipaddr.addr, 4);
NODE_DBG("TCP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
NODE_DBG("\n");
}
else if (pesp_conn->type == ESPCONN_UDP)
{
if(isserver)
c_memcpy(pesp_conn->proto.udp->local_ip, &ipaddr.addr, 4);
else
c_memcpy(pesp_conn->proto.udp->remote_ip, &ipaddr.addr, 4);
NODE_DBG("UDP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
NODE_DBG("\n");
}
}
// call back function when a connection is obtained, tcp only // Lua: net.createConnection(type, secure)
if ( pesp_conn->type == ESPCONN_TCP ) { int net_createConnection( lua_State *L ) {
if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ int type, secure;
lua_pushvalue(L, stack); // copy argument (func) to the top of stack
if(isserver) // for tcp server connected callback
{
if(tcpserver_cb_connect_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref);
tcpserver_cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
else
{
if(nud->cb_connect_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
}
if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server type = luaL_optlong(L, 1, TYPE_TCP);
lua_pushvalue(L, 1); // copy to the top of stack secure = luaL_optlong(L, 2, 0);
if(nud->self_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if( pesp_conn->type == ESPCONN_TCP ) if (type == TYPE_UDP) return net_createUDPSocket( L );
{ if (type != TYPE_TCP) return luaL_error(L, "invalid type");
if(isserver){ // no secure server support for now if (secure) {
espconn_regist_connectcb(pesp_conn, net_server_connected); #ifdef TLS_MODULE_PRESENT
// tcp server, SSL is not supported return tls_socket_create( L );
#ifdef CLIENT_SSL_ENABLE #else
// if(nud->secure) return luaL_error(L, "secure connections not enabled");
// espconn_secure_accept(pesp_conn);
// else
#endif
espconn_accept(pesp_conn); // if it's a server, no need to dns.
espconn_regist_time(pesp_conn, tcp_server_timeover, 0);
}
else{
espconn_regist_connectcb(pesp_conn, net_socket_connected);
espconn_regist_reconcb(pesp_conn, net_socket_reconnected);
#ifdef CLIENT_SSL_ENABLE
if(nud->secure){
if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
espconn_secure_disconnect(pesp_conn);
// espconn_secure_connect(pesp_conn);
}
else
#endif #endif
{
if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
espconn_disconnect(pesp_conn);
// espconn_connect(pesp_conn);
}
}
}
else if (pesp_conn->type == ESPCONN_UDP)
{
espconn_regist_recvcb(pesp_conn, net_socket_received);
espconn_regist_sentcb(pesp_conn, net_socket_sent);
if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
espconn_delete(pesp_conn);
if(isserver)
espconn_create(pesp_conn); // if it's a server, no need to dns.
} }
net_create(L, TYPE_TCP_CLIENT);
if(!isserver){ return 1;
if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0))
{
host_ip.addr = 0;
dns_reconn_count = 0;
if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, socket_dns_found)){
socket_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip.
}
}
else
{
socket_connect(pesp_conn);
}
}
return 0;
} }
// Lua: server/socket:close() #pragma mark - Get & check userdata
// server disconnect everything, unref everything
// client disconnect and unref itself
static int net_close( lua_State* L, const char* mt )
{
NODE_DBG("net_close is called.\n");
bool isserver = false;
int i = 0;
lnet_userdata *nud = NULL, *skt = NULL;
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
luaL_argcheck(L, nud, 1, "Server/Socket expected");
if(nud == NULL)
return 0;
if(nud->pesp_conn == NULL)
return 0;
if (mt!=NULL && c_strcmp(mt, "net.server")==0) lnet_userdata *net_get_udata_s( lua_State *L, int stack ) {
isserver = true; if (!lua_isuserdata(L, stack)) return NULL;
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) lnet_userdata *ud = (lnet_userdata *)lua_touserdata(L, stack);
isserver = false; switch (ud->type) {
else case TYPE_TCP_CLIENT:
{ case TYPE_TCP_SERVER:
NODE_DBG("wrong metatable for net_close.\n"); case TYPE_UDP_SOCKET:
return 0; break;
} default: return NULL;
}
if(isserver && nud->pesp_conn->type == ESPCONN_TCP && tcpserver_cb_connect_ref != LUA_NOREF){ const char *mt = NET_TABLES[ud->type];
luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); ud = luaL_checkudata(L, stack, mt);
tcpserver_cb_connect_ref = LUA_NOREF; return ud;
} }
#define net_get_udata(L) net_get_udata_s(L, 1)
int n = lua_gettop(L);
skt = nud; #pragma mark - Lua API
do{ // Lua: server:listen(port, addr, function(c)), socket:listen(port, addr)
if(isserver && skt == NULL){ int net_listen( lua_State *L ) {
if(socket[i] != LUA_NOREF){ // there is client socket exists lnet_userdata *ud = net_get_udata(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, socket[i]); // get the referenced user_data to stack top if (!ud || ud->type == TYPE_TCP_CLIENT)
#if 0 return luaL_error(L, "invalid user data");
socket[i] = LUA_NOREF; if (ud->pcb)
socket_num--; return luaL_error(L, "already listening");
#endif // do this in net_server_disconnected int stack = 2;
i++; uint16_t port = 0;
if(lua_isuserdata(L,-1)){ const char *domain = "0.0.0.0";
skt = lua_touserdata(L,-1); 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 { } else {
lua_pop(L, 1); ud->tcp_pcb = pcb;
continue; tcp_accept(ud->tcp_pcb, net_accept_cb);
}
}else{
// skip LUA_NOREF
i++;
continue;
}
}
if(skt==NULL){
NODE_DBG("userdata is nil.\n");
continue;
}
if(skt->pesp_conn) // disconnect the connection
{
if(skt->pesp_conn->type == ESPCONN_TCP)
{
#ifdef CLIENT_SSL_ENABLE
if(skt->secure){
if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
espconn_secure_disconnect(skt->pesp_conn);
}
else
#endif
{
if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
espconn_disconnect(skt->pesp_conn);
}
}else if(skt->pesp_conn->type == ESPCONN_UDP)
{
if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
espconn_delete(skt->pesp_conn);
// a udp server/socket unref it self here. not in disconnect.
if(LUA_NOREF!=skt->self_ref){ // for a udp self_ref is NOREF
luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref);
skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata
} }
} }
} break;
#if 0 case TYPE_UDP_SOCKET:
// unref the self_ref ud->udp_pcb = udp_new();
if(LUA_NOREF!=skt->self_ref){ // for a server self_ref is NOREF if (!ud->udp_pcb)
luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref); return luaL_error(L, "cannot allocate PCB");
skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata udp_recv(ud->udp_pcb, net_udp_recv_cb, ud);
} err = udp_bind(ud->udp_pcb, &addr, port);
#endif break;
lua_settop(L, n); // reset the stack top
skt = NULL;
} while( isserver && i<MAX_SOCKET);
#if 0
// unref the self_ref, for both socket and server
if(LUA_NOREF!=nud->self_ref){ // for a server self_ref is NOREF
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = LUA_NOREF; // now only var in lua is ref to the userdata
}
#endif
return 0;
}
// Lua: socket/udpserver:on( "method", function(s) )
static int net_on( lua_State* L, const char* mt )
{
NODE_DBG("net_on is called.\n");
bool isserver = false;
lnet_userdata *nud;
size_t sl;
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
luaL_argcheck(L, nud, 1, "Server/Socket expected");
if(nud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
} }
if (err != ERR_OK) {
if (mt!=NULL && c_strcmp(mt, "net.server")==0) switch (ud->type) {
isserver = true; case TYPE_TCP_SERVER:
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) tcp_close(ud->tcp_pcb);
isserver = false; ud->tcp_pcb = NULL;
else break;
{ case TYPE_UDP_SOCKET:
NODE_DBG("wrong metatable for net_on.\n"); udp_remove(ud->udp_pcb);
return 0; ud->udp_pcb = NULL;
break;
}
return lwip_lua_checkerr(L, err);
} }
if (ud->self_ref == LUA_NOREF) {
const char *method = luaL_checklstring( L, 2, &sl ); lua_pushvalue(L, 1);
if (method == NULL) ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
return luaL_error( L, "wrong arg type" );
luaL_checkanyfunction(L, 3);
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 10 && c_strcmp(method, "connection") == 0){
if(nud->cb_connect_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 12 && c_strcmp(method, "reconnection") == 0){
if(nud->cb_reconnect_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref);
nud->cb_reconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 13 && c_strcmp(method, "disconnection") == 0){
if(nud->cb_disconnect_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
nud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 7 && c_strcmp(method, "receive") == 0){
if(nud->cb_receive_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref);
nud->cb_receive_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 4 && c_strcmp(method, "sent") == 0){
if(nud->cb_send_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 3 && c_strcmp(method, "dns") == 0){
if(nud->cb_dns_found_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else{
lua_pop(L, 1);
return luaL_error( L, "method not supported" );
} }
return 0;
return 0;
} }
// Lua: server/socket:send( string, function(sent) ) // Lua: client:connect(port, addr)
static int net_send( lua_State* L, const char* mt ) int net_connect( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
// NODE_DBG("net_send is called.\n"); if (!ud || ud->type != TYPE_TCP_CLIENT)
bool isserver = false; return luaL_error(L, "invalid user data");
struct espconn *pesp_conn = NULL; if (ud->pcb)
lnet_userdata *nud; return luaL_error(L, "already connected");
size_t l; uint16_t port = luaL_checkinteger(L, 2);
if (port == 0)
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); return luaL_error(L, "specify port");
luaL_argcheck(L, nud, 1, "Server/Socket expected"); const char *domain = "127.0.0.1";
if(nud==NULL){ if (lua_isstring(L, 3)) {
NODE_DBG("userdata is nil.\n"); size_t dl = 0;
return 0; domain = luaL_checklstring(L, 3, &dl);
} }
if (lua_gettop(L) > 3) {
if(nud->pesp_conn == NULL){ luaL_argcheck(L, lua_isfunction(L, 4) || lua_islightfunction(L, 4), 4, "not a function");
NODE_DBG("nud->pesp_conn is NULL.\n"); lua_pushvalue(L, 4);
return 0; luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref);
} ud->client.cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
pesp_conn = nud->pesp_conn; }
ud->tcp_pcb = tcp_new();
if (mt!=NULL && c_strcmp(mt, "net.server")==0) if (!ud->tcp_pcb)
isserver = true; return luaL_error(L, "cannot allocate PCB");
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) tcp_arg(ud->tcp_pcb, ud);
isserver = false; tcp_err(ud->tcp_pcb, net_err_cb);
else tcp_recv(ud->tcp_pcb, net_tcp_recv_cb);
{ tcp_sent(ud->tcp_pcb, net_sent_cb);
NODE_DBG("wrong metatable for net_send.\n"); ud->tcp_pcb->remote_port = port;
return 0; ip_addr_t addr;
} ud->client.wait_dns ++;
int unref = 0;
if(isserver && nud->pesp_conn->type == ESPCONN_TCP){ if (ud->self_ref == LUA_NOREF) {
return luaL_error( L, "tcp server send not supported" ); unref = 1;
} lua_pushvalue(L, 1);
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
#if 0 }
char temp[20] = {0}; err_t err = dns_gethostbyname(domain, &addr, net_dns_cb, ud);
c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); if (err == ERR_OK) {
NODE_DBG("remote "); net_dns_cb(domain, &addr, ud);
NODE_DBG(temp); } else if (err != ERR_INPROGRESS) {
NODE_DBG(":"); ud->client.wait_dns --;
NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); if (unref) {
NODE_DBG(" sending data.\n"); luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
#endif ud->self_ref = LUA_NOREF;
}
const char *payload = luaL_checklstring( L, 2, &l ); tcp_abort(ud->tcp_pcb);
if (l>1460 || payload == NULL) ud->tcp_pcb = NULL;
return luaL_error( L, "need <1460 payload" ); return lwip_lua_checkerr(L, err);
if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if(nud->cb_send_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
// SDK 1.4.0 changed behaviour, for UDP server need to look up remote ip/port
if (isserver && pesp_conn->type == ESPCONN_UDP)
{
remot_info *pr = 0;
if (espconn_get_connection_info (pesp_conn, &pr, 0) != ESPCONN_OK)
return luaL_error (L, "remote ip/port unavailable");
pesp_conn->proto.udp->remote_port = pr->remote_port;
os_memmove (pesp_conn->proto.udp->remote_ip, pr->remote_ip, 4);
// The remot_info apparently should *not* be os_free()d, fyi
} }
#ifdef CLIENT_SSL_ENABLE return 0;
if(nud->secure)
espconn_secure_sent(pesp_conn, (unsigned char *)payload, l);
else
#endif
espconn_sent(pesp_conn, (unsigned char *)payload, l);
return 0;
} }
// Lua: socket:dns( string, function(socket, ip) ) // Lua: client/socket:on(name, callback)
static int net_dns( lua_State* L, const char* mt ) int net_on( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
NODE_DBG("net_dns is called.\n"); if (!ud || ud->type == TYPE_TCP_SERVER)
bool isserver = false; return luaL_error(L, "invalid user data");
struct espconn *pesp_conn = NULL; int *refptr = NULL;
lnet_userdata *nud; const char *name = luaL_checkstring(L, 2);
size_t l; if (!name) return luaL_error(L, "need callback name");
switch (ud->type) {
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); case TYPE_TCP_CLIENT:
luaL_argcheck(L, nud, 1, "Server/Socket expected"); if (strcmp("connection",name)==0)
if(nud==NULL){ { refptr = &ud->client.cb_connect_ref; break; }
NODE_DBG("userdata is nil.\n"); if (strcmp("disconnection",name)==0)
return 0; { refptr = &ud->client.cb_disconnect_ref; break; }
} if (strcmp("reconnection",name)==0)
{ refptr = &ud->client.cb_reconnect_ref; break; }
if (mt!=NULL && c_strcmp(mt, "net.server")==0) case TYPE_UDP_SOCKET:
isserver = true; if (strcmp("dns",name)==0)
else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) { refptr = &ud->client.cb_dns_ref; break; }
isserver = false; if (strcmp("receive",name)==0)
else { refptr = &ud->client.cb_receive_ref; break; }
{ if (strcmp("sent",name)==0)
NODE_DBG("wrong metatable for net_send.\n"); { refptr = &ud->client.cb_sent_ref; break; }
return 0; break;
} default: return luaL_error(L, "invalid user data");
}
if(nud->pesp_conn == NULL){ if (refptr == NULL)
NODE_DBG("nud->pesp_conn is NULL.\n"); return luaL_error(L, "invalid callback name");
return 0; if (lua_isfunction(L, 3) || lua_islightfunction(L, 3)) {
} lua_pushvalue(L, 3);
pesp_conn = nud->pesp_conn; luaL_unref(L, LUA_REGISTRYINDEX, *refptr);
*refptr = luaL_ref(L, LUA_REGISTRYINDEX);
if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server } else if (lua_isnil(L, 3)) {
lua_pushvalue(L, 1); // copy to the top of stack luaL_unref(L, LUA_REGISTRYINDEX, *refptr);
if(nud->self_ref != LUA_NOREF) *refptr = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); } else {
nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); return luaL_error(L, "invalid callback function");
}
const char *domain = luaL_checklstring( L, 2, &l );
if (l>128 || domain == NULL)
return luaL_error( L, "need <128 domain" );
if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if(nud->cb_dns_found_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} }
return 0;
host_ip.addr = 0;
if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, net_dns_found))
net_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip.
return 0;
} }
// Lua: net.dns.resolve( domain, function(ip) ) // Lua: client:send(data, function(c)), socket:send(port, ip, data, function(s))
static int net_dns_static( lua_State* L ) int net_send( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
const char *mt = "net.socket"; if (!ud || ud->type == TYPE_TCP_SERVER)
if (!lua_isstring( L, 1 )) return luaL_error(L, "invalid user data");
return luaL_error( L, "wrong parameter type (domain)" ); ip_addr_t addr;
uint16_t port;
int rfunc = LUA_NOREF; //save reference to func const char *data;
if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ size_t datalen = 0;
rfunc = luaL_ref(L, LUA_REGISTRYINDEX); int stack = 2;
} if (ud->type == TYPE_UDP_SOCKET) {
int rdom = luaL_ref(L, LUA_REGISTRYINDEX); //save reference to domain size_t dl = 0;
port = luaL_checkinteger(L, stack++);
lua_settop(L,0); //empty stack if (port == 0) return luaL_error(L, "need port");
lua_getfield(L, LUA_GLOBALSINDEX, "net"); const char *domain = luaL_checklstring(L, stack++, &dl);
lua_getfield(L, -1, "createConnection"); if (!domain) return luaL_error(L, "need IP address");
lua_remove(L, -2); //remove "net" from stack if (!ipaddr_aton(domain, &addr)) return luaL_error(L, "invalid IP address");
lua_pushinteger(L, UDP); // we are going to create a new dummy UDP socket }
lua_call(L,1,1);// after this the stack should have a socket data = luaL_checklstring(L, stack++, &datalen);
if (!data || datalen == 0) return luaL_error(L, "no data to send");
lua_rawgeti(L, LUA_REGISTRYINDEX, rdom); // load domain back to the stack if (lua_isfunction(L, stack) || lua_islightfunction(L, stack)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, rfunc); // load the callback function back to the stack lua_pushvalue(L, stack++);
luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
luaL_unref(L, LUA_REGISTRYINDEX, rdom); //free reference ud->client.cb_sent_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luaL_unref(L, LUA_REGISTRYINDEX, rfunc); //free reference }
if (ud->type == TYPE_UDP_SOCKET && !ud->pcb) {
bool isserver = false; ud->udp_pcb = udp_new();
struct espconn *pesp_conn = NULL; if (!ud->udp_pcb)
lnet_userdata *nud; return luaL_error(L, "cannot allocate PCB");
size_t l; udp_recv(ud->udp_pcb, net_udp_recv_cb, ud);
ip_addr_t laddr = {0};
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); err_t err = udp_bind(ud->udp_pcb, &laddr, 0);
luaL_argcheck(L, nud, 1, "Server/Socket expected"); if (err != ERR_OK) {
if(nud==NULL){ udp_remove(ud->udp_pcb);
NODE_DBG("userdata is nil.\n"); ud->udp_pcb = NULL;
return 0; return lwip_lua_checkerr(L, err);
} }
if(nud->pesp_conn == NULL){ if (ud->self_ref == LUA_NOREF) {
NODE_DBG("nud->pesp_conn is NULL.\n"); lua_pushvalue(L, 1);
return 0; ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} }
pesp_conn = nud->pesp_conn; }
if (!ud->pcb || ud->self_ref == LUA_NOREF)
lua_pushvalue(L, 1); // copy to the top of stack return luaL_error(L, "not connected");
if(nud->self_ref != LUA_NOREF) err_t err;
luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); if (ud->type == TYPE_UDP_SOCKET) {
nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
if (!pb)
const char *domain = luaL_checklstring( L, 2, &l ); return luaL_error(L, "cannot allocate message buffer");
if (l>128 || domain == NULL) pbuf_take(pb, data, datalen);
return luaL_error( L, "need <128 domain" ); err = udp_sendto(ud->udp_pcb, pb, &addr, port);
pbuf_free(pb);
if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){ if (ud->client.cb_sent_ref != LUA_NOREF) {
lua_pushvalue(L, 3); // copy argument (func) to the top of stack lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref);
if(nud->cb_dns_found_ref != LUA_NOREF) lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); lua_call(L, 1, 0);
nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX); }
} else if (ud->type == TYPE_TCP_CLIENT) {
err = tcp_write(ud->tcp_pcb, data, datalen, TCP_WRITE_FLAG_COPY);
}
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");
if (!ud->client.hold && ud->tcp_pcb) {
ud->client.hold = 1;
} }
host_ip.addr = 0;
if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, net_dns_found))
net_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip.
return 0; return 0;
} }
// Lua: s = net.createServer(type, function(server)) // Lua: client:unhold()
static int net_createServer( lua_State* L ) int net_unhold( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
const char *mt = "net.server"; if (!ud || ud->type != TYPE_TCP_CLIENT)
return net_create(L, mt); return luaL_error(L, "invalid user data");
} if (ud->client.hold && ud->tcp_pcb) {
ud->client.hold = 0;
// Lua: server:delete() ud->tcp_pcb->flags |= TF_ACK_NOW;
static int net_server_delete( lua_State* L ) tcp_recved(ud->tcp_pcb, TCP_WND);
{ }
const char *mt = "net.server"; return 0;
return net_delete(L, mt);
}
// Lua: server:listen( port, ip )
static int net_server_listen( lua_State* L )
{
const char *mt = "net.server";
return net_start(L, mt);
}
// Lua: server:close()
static int net_server_close( lua_State* L )
{
const char *mt = "net.server";
return net_close(L, mt);
}
// Lua: udpserver:on( "method", function(udpserver) )
static int net_udpserver_on( lua_State* L )
{
const char *mt = "net.server";
return net_on(L, mt);
}
// Lua: udpserver:send(string, function() )
static int net_udpserver_send( lua_State* L )
{
const char *mt = "net.server";
return net_send(L, mt);;
}
// Lua: s = net.createConnection(type, function(conn))
static int net_createConnection( lua_State* L )
{
const char *mt = "net.socket";
return net_create(L, mt);
}
// Lua: socket:delete()
static int net_socket_delete( lua_State* L )
{
const char *mt = "net.socket";
return net_delete(L, mt);
}
// Lua: socket:connect( port, ip )
static int net_socket_connect( lua_State* L )
{
const char *mt = "net.socket";
return net_start(L, mt);
}
// Lua: socket:close()
static int net_socket_close( lua_State* L )
{
const char *mt = "net.socket";
return net_close(L, mt);
} }
// Lua: socket:on( "method", function(socket) ) // Lua: client/socket:dns(domain, callback(socket, addr))
static int net_socket_on( lua_State* L ) int net_dns( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
const char *mt = "net.socket"; if (!ud || ud->type == TYPE_TCP_SERVER)
return net_on(L, mt); 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: socket:send( string, function() ) // Lua: client:getpeer()
static int net_socket_send( lua_State* L ) int net_getpeer( lua_State *L ) {
{ lnet_userdata *ud = net_get_udata(L);
const char *mt = "net.socket"; if (!ud || ud->type != TYPE_TCP_CLIENT)
return net_send(L, mt); 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[16];
bzero(addr_str, 16);
ets_sprintf(addr_str, IPSTR, IP2STR(&addr.addr));
lua_pushinteger(L, port);
lua_pushstring(L, addr_str);
return 2;
} }
static int net_socket_hold( lua_State* L ) // Lua: client/server/socket:getaddr()
{ int net_getaddr( lua_State *L ) {
const char *mt = "net.socket"; lnet_userdata *ud = net_get_udata(L);
struct espconn *pesp_conn = NULL; if (!ud) return luaL_error(L, "invalid user data");
lnet_userdata *nud; if (!ud->pcb) {
size_t l; lua_pushnil(L);
lua_pushnil(L);
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); return 2;
luaL_argcheck(L, nud, 1, "Server/Socket expected"); }
if(nud==NULL){ uint16_t port;
NODE_DBG("userdata is nil.\n"); ip_addr_t addr;
return 0; 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) {
if(nud->pesp_conn == NULL){ lua_pushnil(L);
NODE_DBG("nud->pesp_conn is NULL.\n"); lua_pushnil(L);
return 0; return 2;
} }
pesp_conn = nud->pesp_conn; char addr_str[16];
espconn_recv_hold(pesp_conn); bzero(addr_str, 16);
ets_sprintf(addr_str, IPSTR, IP2STR(&addr.addr));
return 0; lua_pushinteger(L, port);
lua_pushstring(L, addr_str);
return 2;
} }
static int net_socket_unhold( lua_State* L ) // Lua: client/server/socket:close()
{ int net_close( lua_State *L ) {
const char *mt = "net.socket"; lnet_userdata *ud = net_get_udata(L);
struct espconn *pesp_conn = NULL; if (!ud) return luaL_error(L, "invalid user data");
lnet_userdata *nud; if (ud->pcb) {
size_t l; switch (ud->type) {
case TYPE_TCP_CLIENT:
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); if (ERR_OK != tcp_close(ud->tcp_pcb)) {
luaL_argcheck(L, nud, 1, "Server/Socket expected"); tcp_arg(ud->tcp_pcb, NULL);
if(nud==NULL){ tcp_abort(ud->tcp_pcb);
NODE_DBG("userdata is nil.\n"); }
return 0; 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 ||
if(nud->pesp_conn == NULL){ (ud->pcb == NULL && ud->client.wait_dns == 0)) {
NODE_DBG("nud->pesp_conn is NULL.\n"); lua_gc(L, LUA_GCSTOP, 0);
return 0; luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref);
ud->self_ref = LUA_NOREF;
lua_gc(L, LUA_GCRESTART, 0);
} }
pesp_conn = nud->pesp_conn;
espconn_recv_unhold(pesp_conn);
return 0; return 0;
} }
// Lua: ip,port = sk:getpeer() int net_delete( lua_State *L ) {
static int net_socket_getpeer( lua_State* L ) lnet_userdata *ud = net_get_udata(L);
{ if (!ud) return luaL_error(L, "no user data");
lnet_userdata *nud; if (ud->pcb) {
const char *mt = "net.socket"; switch (ud->type) {
nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); case TYPE_TCP_CLIENT:
luaL_argcheck(L, nud, 1, "Server/Socket expected"); tcp_arg(ud->tcp_pcb, NULL);
tcp_abort(ud->tcp_pcb);
if(nud!=NULL && nud->pesp_conn!=NULL ){ ud->tcp_pcb = NULL;
char temp[20] = {0}; break;
c_sprintf(temp, IPSTR, IP2STR( &(nud->pesp_conn->proto.tcp->remote_ip) ) ); case TYPE_TCP_SERVER:
if ( nud->pesp_conn->proto.tcp->remote_port != 0 ) { tcp_close(ud->tcp_pcb);
lua_pushstring( L, temp ); ud->tcp_pcb = NULL;
lua_pushinteger( L, nud->pesp_conn->proto.tcp->remote_port ); break;
} else { case TYPE_UDP_SOCKET:
lua_pushnil( L ); udp_remove(ud->udp_pcb);
lua_pushnil( L ); ud->udp_pcb = NULL;
} break;
} else { }
lua_pushnil( L ); }
lua_pushnil( L ); 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;
} }
return 2; 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;
} }
// Lua: socket:dns( string, function(ip) ) #pragma mark - Multicast
static int net_socket_dns( lua_State* L )
{
const char *mt = "net.socket";
return net_dns(L, mt);
}
static int net_multicastJoinLeave( lua_State *L, int join) static int net_multicastJoinLeave( lua_State *L, int join) {
{
size_t il; size_t il;
ip_addr_t multicast_addr; ip_addr_t multicast_addr;
ip_addr_t if_addr; ip_addr_t if_addr;
...@@ -1391,227 +808,85 @@ static int net_multicastJoinLeave( lua_State *L, int join) ...@@ -1391,227 +808,85 @@ static int net_multicastJoinLeave( lua_State *L, int join)
multicast_addr.addr = ipaddr_addr(multicast_ip); multicast_addr.addr = ipaddr_addr(multicast_ip);
if (join) if (join)
{ {
espconn_igmp_join(&if_addr, &multicast_addr); igmp_joingroup(&if_addr, &multicast_addr);
} }
else else
{ {
espconn_igmp_leave(&if_addr, &multicast_addr); igmp_leavegroup(&if_addr, &multicast_addr);
} }
return 0; return 0;
} }
// Lua: net.multicastJoin(ifip, multicastip) // Lua: net.multicastJoin(ifip, multicastip)
// if ifip "" or "any" all interfaces are affected // if ifip "" or "any" all interfaces are affected
static int net_multicastJoin( lua_State* L ) static int net_multicastJoin( lua_State* L ) {
{
return net_multicastJoinLeave(L,1); return net_multicastJoinLeave(L,1);
} }
// Lua: net.multicastLeave(ifip, multicastip) // Lua: net.multicastLeave(ifip, multicastip)
// if ifip "" or "any" all interfaces are affected // if ifip "" or "any" all interfaces are affected
static int net_multicastLeave( lua_State* L ) static int net_multicastLeave( lua_State* L ) {
{
return net_multicastJoinLeave(L,0); return net_multicastJoinLeave(L,0);
} }
// Returns NULL on success, error message otherwise #pragma mark - DNS
static const char *append_pem_blob(const char *pem, const char *type, uint8_t **buffer_p, uint8_t *buffer_limit, const char *name) {
char unb64[256];
memset(unb64, 0xff, sizeof(unb64));
int i;
for (i = 0; i < 64; i++) {
unb64["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
}
if (!pem) {
return "No PEM blob";
}
// Scan for -----BEGIN CERT
pem = strstr(pem, "-----BEGIN ");
if (!pem) {
return "No PEM header";
}
if (strncmp(pem + 11, type, strlen(type))) {
return "Wrong PEM type";
}
pem = strchr(pem, '\n');
if (!pem) {
return "Incorrect PEM format";
}
//
// Base64 encoded data starts here
// Get all the base64 data into a single buffer....
// We will use the back end of the buffer....
//
uint8_t *buffer = *buffer_p;
uint8_t *dest = buffer + 32 + 2; // Leave space for name and length
int bitcount = 0;
int accumulator = 0;
for (; *pem && dest < buffer_limit; pem++) {
int val = unb64[*(uint8_t*) pem];
if (val & 0xC0) {
// not a base64 character
if (isspace(*(uint8_t*) pem)) {
continue;
}
if (*pem == '=') {
// just ignore -- at the end
bitcount = 0;
continue;
}
if (*pem == '-') {
break;
}
return "Invalid character in PEM";
} else {
bitcount += 6;
accumulator = (accumulator << 6) + val;
if (bitcount >= 8) {
bitcount -= 8;
*dest++ = accumulator >> bitcount;
}
}
}
if (dest >= buffer_limit || strncmp(pem, "-----END ", 9) || strncmp(pem + 9, type, strlen(type)) || bitcount) {
return "Invalid PEM format data";
}
size_t len = dest - (buffer + 32 + 2);
memset(buffer, 0, 32);
strcpy(buffer, name);
buffer[32] = len & 0xff;
buffer[33] = (len >> 8) & 0xff;
*buffer_p = dest;
return NULL;
}
static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_memory, int flash_offset, const char **types, const char **names)
{
uint8_t *buffer = luaM_malloc(L, INTERNAL_FLASH_SECTOR_SIZE);
uint8_t *buffer_base = buffer;
uint8_t *buffer_limit = buffer + INTERNAL_FLASH_SECTOR_SIZE;
int argno;
for (argno = 1; argno <= lua_gettop(L) && types[argno - 1]; argno++) { static void net_dns_static_cb(const char *name, ip_addr_t *ipaddr, void *callback_arg) {
const char *pem = lua_tostring(L, argno); ip_addr_t addr;
if (ipaddr != NULL)
const char *error = append_pem_blob(pem, types[argno - 1], &buffer, buffer_limit, names[argno - 1]); addr = *ipaddr;
if (error) { else addr.addr = 0xFFFFFFFF;
luaM_free(L, buffer_base); int cb_ref = ((int*)callback_arg)[0];
return error; c_free(callback_arg);
} lua_State *L = lua_getstate();
}
memset(buffer, 0xff, buffer_limit - buffer);
// Lets see if it matches what is already there....
if (c_memcmp(buffer_base, flash_memory, INTERNAL_FLASH_SECTOR_SIZE) != 0) {
// Starts being dangerous
if (platform_flash_erase_sector(flash_offset / INTERNAL_FLASH_SECTOR_SIZE) != PLATFORM_OK) {
luaM_free(L, buffer_base);
return "Failed to erase sector";
}
if (platform_s_flash_write(buffer_base, flash_offset, INTERNAL_FLASH_SECTOR_SIZE) != INTERNAL_FLASH_SECTOR_SIZE) {
luaM_free(L, buffer_base);
return "Failed to write sector";
}
// ends being dangerous
}
luaM_free(L, buffer_base);
return NULL;
}
// Lua: net.cert.auth(true / false | PEM data [, PEM data] )
static int net_cert_auth(lua_State *L)
{
int enable;
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_client_cert_area[0]);
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
// THis should never happen
return luaL_error( L, "bad offset" );
}
if (lua_type(L, 1) == LUA_TSTRING) {
const char *types[3] = { "CERTIFICATE", "RSA PRIVATE KEY", NULL };
const char *names[2] = { "certificate", "private_key" };
const char *error = fill_page_with_pem(L, &net_client_cert_area[0], flash_offset, types, names);
if (error) {
return luaL_error(L, error);
}
enable = 1;
} else {
enable = lua_toboolean(L, 1);
}
bool rc;
if (enable) { lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref);
// See if there is a cert there lua_pushnil(L);
if (net_client_cert_area[0] == 0x00 || net_client_cert_area[0] == 0xff) { if (addr.addr != 0xFFFFFFFF) {
return luaL_error( L, "no certificates found" ); char iptmp[20];
} size_t ipl = c_sprintf(iptmp, IPSTR, IP2STR(&addr.addr));
rc = espconn_secure_cert_req_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE); lua_pushlstring(L, iptmp, ipl);
} else { } else {
rc = espconn_secure_cert_req_disable(1); lua_pushnil(L);
} }
lua_call(L, 2, 0);
lua_pushboolean(L, rc); luaL_unref(L, LUA_REGISTRYINDEX, cb_ref);
return 1;
} }
// Lua: net.cert.verify(true / false | PEM data [, PEM data] ) // Lua: net.dns.resolve( domain, function(sk, ip) )
static int net_cert_verify(lua_State *L) static int net_dns_static( lua_State* L ) {
{ size_t dl;
int enable; const char* domain = luaL_checklstring(L, 1, &dl);
if (!domain && dl > 128) {
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_server_cert_area[0]); return luaL_error(L, "wrong domain");
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
// THis should never happen
return luaL_error( L, "bad offset" );
} }
if (lua_type(L, 1) == LUA_TSTRING) { luaL_checkanyfunction(L, 2);
const char *types[2] = { "CERTIFICATE", NULL }; lua_pushvalue(L, 2); // copy argument (func) to the top of stack
const char *names[1] = { "certificate" }; int cbref = luaL_ref(L, LUA_REGISTRYINDEX);
if (cbref == LUA_NOREF) {
const char *error = fill_page_with_pem(L, &net_server_cert_area[0], flash_offset, types, names); return luaL_error(L, "wrong callback");
if (error) {
return luaL_error(L, error);
}
enable = 1;
} else {
enable = lua_toboolean(L, 1);
} }
int *cbref_ptr = c_zalloc(sizeof(int));
bool rc; cbref_ptr[0] = cbref;
ip_addr_t addr;
if (enable) { err_t err = dns_gethostbyname(domain, &addr, net_dns_static_cb, cbref_ptr);
// See if there is a cert there if (err == ERR_OK) {
if (net_server_cert_area[0] == 0x00 || net_server_cert_area[0] == 0xff) { net_dns_static_cb(domain, &addr, cbref_ptr);
return luaL_error( L, "no certificates found" ); return 0;
} } else if (err == ERR_INPROGRESS) {
rc = espconn_secure_ca_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE); return 0;
} else { } else {
rc = espconn_secure_ca_disable(1); int e = lwip_lua_checkerr(L, err);
c_free(cbref_ptr);
return e;
} }
return 0;
lua_pushboolean(L, rc);
return 1;
} }
// Lua: s = net.dns.setdnsserver(ip_addr, [index]) // Lua: s = net.dns.setdnsserver(ip_addr, [index])
static int net_setdnsserver( lua_State* L ) static int net_setdnsserver( lua_State* L ) {
{
size_t l; size_t l;
u32_t ip32; u32_t ip32;
...@@ -1631,8 +906,7 @@ static int net_setdnsserver( lua_State* L ) ...@@ -1631,8 +906,7 @@ static int net_setdnsserver( lua_State* L )
} }
// Lua: s = net.dns.getdnsserver([index]) // Lua: s = net.dns.getdnsserver([index])
static int net_getdnsserver( lua_State* L ) static int net_getdnsserver( lua_State* L ) {
{
int numdns = luaL_optint(L, 1, 0); int numdns = luaL_optint(L, 1, 0);
if (numdns >= DNS_MAX_SERVERS) if (numdns >= DNS_MAX_SERVERS)
return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1); return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1);
...@@ -1653,110 +927,78 @@ static int net_getdnsserver( lua_State* L ) ...@@ -1653,110 +927,78 @@ static int net_getdnsserver( lua_State* L )
return 1; return 1;
} }
#if 0 #pragma mark - Tables
static int net_array_index( lua_State* L )
{
char** parray = luaL_checkudata(L, 1, "net.array");
int index = luaL_checkint(L, 2);
lua_pushnumber(L, (*parray)[index-1]);
return 1;
}
static int net_array_newindex( lua_State* L )
{
char** parray = luaL_checkudata(L, 1, "net.array");
int index = luaL_checkint(L, 2);
int value = luaL_checkint(L, 3);
(*parray)[index-1] = value;
return 0;
}
// expose an array to lua, by storing it in a userdata with the array metatable #ifdef TLS_MODULE_PRESENT
static int expose_array(lua_State* L, char *array, unsigned short len) { extern const LUA_REG_TYPE tls_cert_map[];
char** parray = lua_newuserdata(L, len);
*parray = array;
luaL_getmetatable(L, "net.array");
lua_setmetatable(L, -2);
return 1;
}
#endif #endif
// Module function map // Module function map
static const LUA_REG_TYPE net_server_map[] = { static const LUA_REG_TYPE net_tcpserver_map[] = {
{ LSTRKEY( "listen" ), LFUNCVAL( net_server_listen ) }, { LSTRKEY( "listen" ), LFUNCVAL( net_listen ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_server_close ) }, { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) },
{ LSTRKEY( "on" ), LFUNCVAL( net_udpserver_on ) }, { LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "send" ), LFUNCVAL( net_udpserver_send ) }, { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) },
//{ LSTRKEY( "delete" ), LFUNCVAL( net_server_delete ) }, { LSTRKEY( "__index" ), LROVAL( net_tcpserver_map ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_server_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_server_map ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
static const LUA_REG_TYPE net_socket_map[] = { static const LUA_REG_TYPE net_tcpsocket_map[] = {
{ LSTRKEY( "connect" ), LFUNCVAL( net_socket_connect ) }, { LSTRKEY( "connect" ), LFUNCVAL( net_connect ) },
{ LSTRKEY( "close" ), LFUNCVAL( net_socket_close ) }, { LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "on" ), LFUNCVAL( net_socket_on ) }, { LSTRKEY( "on" ), LFUNCVAL( net_on ) },
{ LSTRKEY( "send" ), LFUNCVAL( net_socket_send ) }, { LSTRKEY( "send" ), LFUNCVAL( net_send ) },
{ LSTRKEY( "hold" ), LFUNCVAL( net_socket_hold ) }, { LSTRKEY( "hold" ), LFUNCVAL( net_hold ) },
{ LSTRKEY( "unhold" ), LFUNCVAL( net_socket_unhold ) }, { LSTRKEY( "unhold" ), LFUNCVAL( net_unhold ) },
{ LSTRKEY( "dns" ), LFUNCVAL( net_socket_dns ) }, { LSTRKEY( "dns" ), LFUNCVAL( net_dns ) },
{ LSTRKEY( "getpeer" ), LFUNCVAL( net_socket_getpeer ) }, { LSTRKEY( "getpeer" ), LFUNCVAL( net_getpeer ) },
//{ LSTRKEY( "delete" ), LFUNCVAL( net_socket_delete ) }, { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( net_socket_delete ) }, { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) },
{ LSTRKEY( "__index" ), LROVAL( net_socket_map ) }, { LSTRKEY( "__index" ), LROVAL( net_tcpsocket_map ) },
{ LNILKEY, LNILVAL }
};
#if 0
static const LUA_REG_TYPE net_array_map[] = {
{ LSTRKEY( "__index" ), LFUNCVAL( net_array_index ) },
{ LSTRKEY( "__newindex" ), LFUNCVAL( net_array_newindex ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
#endif
static const LUA_REG_TYPE net_cert_map[] = { static const LUA_REG_TYPE net_udpsocket_map[] = {
{ LSTRKEY( "verify" ), LFUNCVAL( net_cert_verify ) }, { LSTRKEY( "listen" ), LFUNCVAL( net_listen ) },
#ifdef CLIENT_SSL_CERT_AUTH_ENABLE { LSTRKEY( "close" ), LFUNCVAL( net_close ) },
{ LSTRKEY( "auth" ), LFUNCVAL( net_cert_auth ) }, { LSTRKEY( "on" ), LFUNCVAL( net_on ) },
#endif { 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 } { LNILKEY, LNILVAL }
}; };
static const LUA_REG_TYPE net_dns_map[] = { static const LUA_REG_TYPE net_dns_map[] = {
{ LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) }, { LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) },
{ LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) }, { LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) },
{ LSTRKEY( "resolve" ), LFUNCVAL( net_dns_static ) }, { LSTRKEY( "resolve" ), LFUNCVAL( net_dns_static ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
static const LUA_REG_TYPE net_map[] = { static const LUA_REG_TYPE net_map[] = {
{ LSTRKEY( "createServer" ), LFUNCVAL( net_createServer ) }, { LSTRKEY( "createServer" ), LFUNCVAL( net_createServer ) },
{ LSTRKEY( "createConnection" ), LFUNCVAL( net_createConnection ) }, { LSTRKEY( "createConnection" ), LFUNCVAL( net_createConnection ) },
{ LSTRKEY( "createUDPSocket" ), LFUNCVAL( net_createUDPSocket ) },
{ LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) }, { LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) },
{ LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) }, { LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) },
{ LSTRKEY( "dns" ), LROVAL( net_dns_map ) }, { LSTRKEY( "dns" ), LROVAL( net_dns_map ) },
#ifdef CLIENT_SSL_ENABLE #ifdef TLS_MODULE_PRESENT
{ LSTRKEY( "cert" ), LROVAL(net_cert_map) }, { LSTRKEY( "cert" ), LROVAL( tls_cert_map ) },
#endif #endif
{ LSTRKEY( "TCP" ), LNUMVAL( TCP ) }, { LSTRKEY( "TCP" ), LNUMVAL( TYPE_TCP ) },
{ LSTRKEY( "UDP" ), LNUMVAL( UDP ) }, { LSTRKEY( "UDP" ), LNUMVAL( TYPE_UDP ) },
{ LSTRKEY( "__metatable" ), LROVAL( net_map ) }, { LSTRKEY( "__metatable" ), LROVAL( net_map ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
int luaopen_net( lua_State *L ) { int luaopen_net( lua_State *L ) {
int i; igmp_init();
for(i=0;i<MAX_SOCKET;i++)
{
socket[i] = LUA_NOREF;
}
luaL_rometatable(L, "net.server", (void *)net_server_map); // create metatable for net.server luaL_rometatable(L, NET_TABLE_TCP_SERVER, (void *)net_tcpserver_map);
luaL_rometatable(L, "net.socket", (void *)net_socket_map); // create metatable for net.socket luaL_rometatable(L, NET_TABLE_TCP_CLIENT, (void *)net_tcpsocket_map);
#if 0 luaL_rometatable(L, NET_TABLE_UDP_SOCKET, (void *)net_udpsocket_map);
luaL_rometatable(L, "net.array", (void *)net_array_map); // create metatable for net.array
#endif
return 0; return 0;
} }
......
// Module for TLS
#include "module.h"
#if defined(CLIENT_SSL_ENABLE) && defined(LUA_USE_MODULES_NET)
#include "lauxlib.h"
#include "platform.h"
#include "lmem.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "c_types.h"
#include "mem.h"
#include "lwip/ip_addr.h"
#include "espconn.h"
#include "lwip/err.h"
#include "lwip/dns.h"
#ifdef HAVE_SSL_SERVER_CRT
#include HAVE_SSL_SERVER_CRT
#else
__attribute__((section(".servercert.flash"))) unsigned char tls_server_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
#endif
__attribute__((section(".clientcert.flash"))) unsigned char tls_client_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
extern int tls_socket_create( lua_State *L );
extern const LUA_REG_TYPE tls_cert_map[];
typedef struct {
struct espconn *pesp_conn;
int self_ref;
int cb_connect_ref;
int cb_reconnect_ref;
int cb_disconnect_ref;
int cb_sent_ref;
int cb_receive_ref;
int cb_dns_ref;
} tls_socket_ud;
int tls_socket_create( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud*) lua_newuserdata(L, sizeof(tls_socket_ud));
ud->pesp_conn = NULL;
ud->self_ref =
ud->cb_connect_ref =
ud->cb_reconnect_ref =
ud->cb_disconnect_ref =
ud->cb_sent_ref =
ud->cb_receive_ref =
ud->cb_dns_ref = LUA_NOREF;
luaL_getmetatable(L, "tls.socket");
lua_setmetatable(L, -2);
return 1;
}
static void tls_socket_onconnect( struct espconn *pesp_conn ) {
tls_socket_ud *ud = (tls_socket_ud *)pesp_conn->reverse;
if (!ud || ud->self_ref == LUA_NOREF) return;
if (ud->cb_connect_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_connect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
}
}
static void tls_socket_cleanup(tls_socket_ud *ud) {
if (ud->pesp_conn) {
espconn_secure_disconnect(ud->pesp_conn);
if (ud->pesp_conn->proto.tcp) {
c_free(ud->pesp_conn->proto.tcp);
ud->pesp_conn->proto.tcp = NULL;
}
c_free(ud->pesp_conn);
ud->pesp_conn = NULL;
}
lua_State *L = lua_getstate();
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 tls_socket_ondisconnect( struct espconn *pesp_conn ) {
tls_socket_ud *ud = (tls_socket_ud *)pesp_conn->reverse;
if (!ud || ud->self_ref == LUA_NOREF) return;
tls_socket_cleanup(ud);
if (ud->cb_disconnect_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_disconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
tls_socket_cleanup(ud);
lua_call(L, 1, 0);
} else tls_socket_cleanup(ud);
}
static void tls_socket_onreconnect( struct espconn *pesp_conn, s8 err ) {
tls_socket_ud *ud = (tls_socket_ud *)pesp_conn->reverse;
if (!ud || ud->self_ref == LUA_NOREF) return;
if (ud->cb_reconnect_ref != LUA_NOREF) {
const char* reason = NULL;
switch (err) {
case(ESPCONN_MEM): reason = "Out of memory"; break;
case(ESPCONN_TIMEOUT): reason = "Timeout"; break;
case(ESPCONN_RTE): reason = "Routing problem"; break;
case(ESPCONN_ABRT): reason = "Connection aborted"; break;
case(ESPCONN_RST): reason = "Connection reset"; break;
case(ESPCONN_CLSD): reason = "Connection closed"; break;
case(ESPCONN_HANDSHAKE): reason = "SSL handshake failed"; break;
case(ESPCONN_SSL_INVALID_DATA): reason = "SSL application invalid"; break;
}
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_reconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
if (reason != NULL) {
lua_pushstring(L, reason);
} else {
lua_pushnil(L);
}
tls_socket_cleanup(ud);
lua_call(L, 2, 0);
} else tls_socket_cleanup(ud);
}
static void tls_socket_onrecv( struct espconn *pesp_conn, char *buf, u16 length ) {
tls_socket_ud *ud = (tls_socket_ud *)pesp_conn->reverse;
if (!ud || ud->self_ref == LUA_NOREF) return;
if (ud->cb_receive_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_receive_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_pushlstring(L, buf, length);
lua_call(L, 2, 0);
}
}
static void tls_socket_onsent( struct espconn *pesp_conn ) {
tls_socket_ud *ud = (tls_socket_ud *)pesp_conn->reverse;
if (!ud || ud->self_ref == LUA_NOREF) return;
if (ud->cb_sent_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_sent_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
lua_call(L, 1, 0);
}
}
static void tls_socket_dns_cb( const char* domain, const ip_addr_t *ip_addr, tls_socket_ud *ud ) {
if (ud->self_ref == LUA_NOREF) return;
ip_addr_t addr;
if (ip_addr) addr = *ip_addr;
else addr.addr = 0xFFFFFFFF;
lua_State *L = lua_getstate();
if (ud->cb_dns_ref != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_dns_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref);
if (addr.addr == 0xFFFFFFFF) {
lua_pushnil(L);
} else {
char tmp[20];
c_sprintf(tmp, IPSTR, IP2STR(&addr.addr));
lua_pushstring(L, tmp);
}
lua_call(L, 2, 0);
}
if (addr.addr == 0xFFFFFFFF) {
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);
} else {
os_memcpy(ud->pesp_conn->proto.tcp->remote_ip, &addr.addr, 4);
espconn_secure_connect(ud->pesp_conn);
}
}
static int tls_socket_connect( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if (ud->pesp_conn) {
return luaL_error(L, "already connected");
}
u16 port = luaL_checkinteger( L, 2 );
size_t il;
const char *domain = "127.0.0.1";
if( lua_isstring(L, 3) )
domain = luaL_checklstring( L, 3, &il );
if (port == 0)
return luaL_error(L, "invalid port");
if (domain == NULL)
return luaL_error(L, "invalid domain");
ud->pesp_conn = (struct espconn*)c_zalloc(sizeof(struct espconn));
if(!ud->pesp_conn)
return luaL_error(L, "not enough memory");
ud->pesp_conn->proto.udp = NULL;
ud->pesp_conn->proto.tcp = (esp_tcp *)c_zalloc(sizeof(esp_tcp));
if(!ud->pesp_conn->proto.tcp){
c_free(ud->pesp_conn);
ud->pesp_conn = NULL;
return luaL_error(L, "not enough memory");
}
ud->pesp_conn->type = ESPCONN_TCP;
ud->pesp_conn->state = ESPCONN_NONE;
ud->pesp_conn->reverse = ud;
ud->pesp_conn->proto.tcp->remote_port = port;
espconn_regist_connectcb(ud->pesp_conn, (espconn_connect_callback)tls_socket_onconnect);
espconn_regist_disconcb(ud->pesp_conn, (espconn_connect_callback)tls_socket_ondisconnect);
espconn_regist_reconcb(ud->pesp_conn, (espconn_reconnect_callback)tls_socket_onreconnect);
espconn_regist_recvcb(ud->pesp_conn, (espconn_recv_callback)tls_socket_onrecv);
espconn_regist_sentcb(ud->pesp_conn, (espconn_sent_callback)tls_socket_onsent);
if (ud->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1); // copy to the top of stack
ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
ip_addr_t addr;
err_t err = dns_gethostbyname(domain, &addr, (dns_found_callback)tls_socket_dns_cb, ud);
if (err == ERR_OK) {
tls_socket_dns_cb(domain, &addr, ud);
} else if (err != ERR_INPROGRESS) {
tls_socket_dns_cb(domain, NULL, ud);
}
return 0;
}
static int tls_socket_on( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
size_t sl;
const char *method = luaL_checklstring( L, 2, &sl );
if (method == NULL)
return luaL_error( L, "wrong arg type" );
luaL_checkanyfunction(L, 3);
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if (strcmp(method, "connection") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_connect_ref);
ud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (strcmp(method, "disconnection") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_disconnect_ref);
ud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (strcmp(method, "reconnection") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_reconnect_ref);
ud->cb_reconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (strcmp(method, "receive") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_receive_ref);
ud->cb_receive_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (strcmp(method, "sent") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_sent_ref);
ud->cb_sent_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else if (strcmp(method, "dns") == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_dns_ref);
ud->cb_dns_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
return luaL_error(L, "invalid method");
}
return 0;
}
static int tls_socket_send( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if(ud->pesp_conn == NULL) {
NODE_DBG("not connected");
return 0;
}
size_t sl;
const char* buf = luaL_checklstring(L, 2, &sl);
if (!buf) {
return luaL_error(L, "wrong arg type");
}
espconn_secure_send(ud->pesp_conn, (void*)buf, sl);
return 0;
}
static int tls_socket_hold( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if(ud->pesp_conn == NULL) {
NODE_DBG("not connected");
return 0;
}
espconn_recv_hold(ud->pesp_conn);
return 0;
}
static int tls_socket_unhold( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if(ud->pesp_conn == NULL) {
NODE_DBG("not connected");
return 0;
}
espconn_recv_unhold(ud->pesp_conn);
return 0;
}
static int tls_socket_dns( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
return 0;
}
static int tls_socket_getpeer( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if(ud->pesp_conn && ud->pesp_conn->proto.tcp->remote_port != 0){
char temp[20] = {0};
c_sprintf(temp, IPSTR, IP2STR( &(ud->pesp_conn->proto.tcp->remote_ip) ) );
lua_pushstring( L, temp );
lua_pushinteger( L, ud->pesp_conn->proto.tcp->remote_port );
} else {
lua_pushnil( L );
lua_pushnil( L );
}
return 2;
}
static int tls_socket_close( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if (ud->pesp_conn) {
espconn_secure_disconnect(ud->pesp_conn);
}
return 0;
}
static int tls_socket_delete( lua_State *L ) {
tls_socket_ud *ud = (tls_socket_ud *)luaL_checkudata(L, 1, "tls.socket");
luaL_argcheck(L, ud, 1, "TLS socket expected");
if(ud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
if (ud->pesp_conn) {
espconn_secure_disconnect(ud->pesp_conn);
if (ud->pesp_conn->proto.tcp) {
c_free(ud->pesp_conn->proto.tcp);
ud->pesp_conn->proto.tcp = NULL;
}
c_free(ud->pesp_conn);
ud->pesp_conn = NULL;
}
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_connect_ref);
ud->cb_connect_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_disconnect_ref);
ud->cb_disconnect_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_reconnect_ref);
ud->cb_reconnect_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_dns_ref);
ud->cb_dns_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_receive_ref);
ud->cb_receive_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_sent_ref);
ud->cb_sent_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;
}
// Returns NULL on success, error message otherwise
static const char *append_pem_blob(const char *pem, const char *type, uint8_t **buffer_p, uint8_t *buffer_limit, const char *name) {
char unb64[256];
memset(unb64, 0xff, sizeof(unb64));
int i;
for (i = 0; i < 64; i++) {
unb64["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
}
if (!pem) {
return "No PEM blob";
}
// Scan for -----BEGIN CERT
pem = strstr(pem, "-----BEGIN ");
if (!pem) {
return "No PEM header";
}
if (strncmp(pem + 11, type, strlen(type))) {
return "Wrong PEM type";
}
pem = strchr(pem, '\n');
if (!pem) {
return "Incorrect PEM format";
}
//
// Base64 encoded data starts here
// Get all the base64 data into a single buffer....
// We will use the back end of the buffer....
//
uint8_t *buffer = *buffer_p;
uint8_t *dest = buffer + 32 + 2; // Leave space for name and length
int bitcount = 0;
int accumulator = 0;
for (; *pem && dest < buffer_limit; pem++) {
int val = unb64[*(uint8_t*) pem];
if (val & 0xC0) {
// not a base64 character
if (isspace(*(uint8_t*) pem)) {
continue;
}
if (*pem == '=') {
// just ignore -- at the end
bitcount = 0;
continue;
}
if (*pem == '-') {
break;
}
return "Invalid character in PEM";
} else {
bitcount += 6;
accumulator = (accumulator << 6) + val;
if (bitcount >= 8) {
bitcount -= 8;
*dest++ = accumulator >> bitcount;
}
}
}
if (dest >= buffer_limit || strncmp(pem, "-----END ", 9) || strncmp(pem + 9, type, strlen(type)) || bitcount) {
return "Invalid PEM format data";
}
size_t len = dest - (buffer + 32 + 2);
memset(buffer, 0, 32);
strcpy(buffer, name);
buffer[32] = len & 0xff;
buffer[33] = (len >> 8) & 0xff;
*buffer_p = dest;
return NULL;
}
static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_memory, int flash_offset, const char **types, const char **names)
{
uint8_t *buffer = luaM_malloc(L, INTERNAL_FLASH_SECTOR_SIZE);
uint8_t *buffer_base = buffer;
uint8_t *buffer_limit = buffer + INTERNAL_FLASH_SECTOR_SIZE;
int argno;
for (argno = 1; argno <= lua_gettop(L) && types[argno - 1]; argno++) {
const char *pem = lua_tostring(L, argno);
const char *error = append_pem_blob(pem, types[argno - 1], &buffer, buffer_limit, names[argno - 1]);
if (error) {
luaM_free(L, buffer_base);
return error;
}
}
memset(buffer, 0xff, buffer_limit - buffer);
// Lets see if it matches what is already there....
if (c_memcmp(buffer_base, flash_memory, INTERNAL_FLASH_SECTOR_SIZE) != 0) {
// Starts being dangerous
if (platform_flash_erase_sector(flash_offset / INTERNAL_FLASH_SECTOR_SIZE) != PLATFORM_OK) {
luaM_free(L, buffer_base);
return "Failed to erase sector";
}
if (platform_s_flash_write(buffer_base, flash_offset, INTERNAL_FLASH_SECTOR_SIZE) != INTERNAL_FLASH_SECTOR_SIZE) {
luaM_free(L, buffer_base);
return "Failed to write sector";
}
// ends being dangerous
}
luaM_free(L, buffer_base);
return NULL;
}
// Lua: tls.cert.auth(true / false | PEM data [, PEM data] )
static int tls_cert_auth(lua_State *L)
{
int enable;
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &tls_client_cert_area[0]);
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
// THis should never happen
return luaL_error( L, "bad offset" );
}
if (lua_type(L, 1) == LUA_TSTRING) {
const char *types[3] = { "CERTIFICATE", "RSA PRIVATE KEY", NULL };
const char *names[2] = { "certificate", "private_key" };
const char *error = fill_page_with_pem(L, &tls_client_cert_area[0], flash_offset, types, names);
if (error) {
return luaL_error(L, error);
}
enable = 1;
} else {
enable = lua_toboolean(L, 1);
}
bool rc;
if (enable) {
// See if there is a cert there
if (tls_client_cert_area[0] == 0x00 || tls_client_cert_area[0] == 0xff) {
return luaL_error( L, "no certificates found" );
}
rc = espconn_secure_cert_req_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
} else {
rc = espconn_secure_cert_req_disable(1);
}
lua_pushboolean(L, rc);
return 1;
}
// Lua: tls.cert.verify(true / false | PEM data [, PEM data] )
static int tls_cert_verify(lua_State *L)
{
int enable;
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &tls_server_cert_area[0]);
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
// THis should never happen
return luaL_error( L, "bad offset" );
}
if (lua_type(L, 1) == LUA_TSTRING) {
const char *types[2] = { "CERTIFICATE", NULL };
const char *names[1] = { "certificate" };
const char *error = fill_page_with_pem(L, &tls_server_cert_area[0], flash_offset, types, names);
if (error) {
return luaL_error(L, error);
}
enable = 1;
} else {
enable = lua_toboolean(L, 1);
}
bool rc;
if (enable) {
// See if there is a cert there
if (tls_server_cert_area[0] == 0x00 || tls_server_cert_area[0] == 0xff) {
return luaL_error( L, "no certificates found" );
}
rc = espconn_secure_ca_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
} else {
rc = espconn_secure_ca_disable(1);
}
lua_pushboolean(L, rc);
return 1;
}
static const LUA_REG_TYPE tls_socket_map[] = {
{ LSTRKEY( "connect" ), LFUNCVAL( tls_socket_connect ) },
{ LSTRKEY( "close" ), LFUNCVAL( tls_socket_close ) },
{ LSTRKEY( "on" ), LFUNCVAL( tls_socket_on ) },
{ LSTRKEY( "send" ), LFUNCVAL( tls_socket_send ) },
{ LSTRKEY( "hold" ), LFUNCVAL( tls_socket_hold ) },
{ LSTRKEY( "unhold" ), LFUNCVAL( tls_socket_unhold ) },
{ LSTRKEY( "dns" ), LFUNCVAL( tls_socket_dns ) },
{ LSTRKEY( "getpeer" ), LFUNCVAL( tls_socket_getpeer ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( tls_socket_delete ) },
{ LSTRKEY( "__index" ), LROVAL( tls_socket_map ) },
{ LNILKEY, LNILVAL }
};
const LUA_REG_TYPE tls_cert_map[] = {
{ LSTRKEY( "verify" ), LFUNCVAL( tls_cert_verify ) },
{ LSTRKEY( "auth" ), LFUNCVAL( tls_cert_auth ) },
{ LSTRKEY( "__index" ), LROVAL( tls_cert_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE tls_map[] = {
{ LSTRKEY( "createConnection" ), LFUNCVAL( tls_socket_create ) },
{ LSTRKEY( "cert" ), LROVAL( tls_cert_map ) },
{ LSTRKEY( "__metatable" ), LROVAL( tls_map ) },
{ LNILKEY, LNILVAL }
};
int luaopen_tls( lua_State *L ) {
luaL_rometatable(L, "tls.socket", (void *)tls_socket_map); // create metatable for net.server
espconn_secure_set_size(ESPCONN_CLIENT, 4096);
return 0;
}
NODEMCU_MODULE(TLS, "tls", tls_map, luaopen_tls);
#endif
# net Module # net Module
| Since | Origin / Contributor | Maintainer | Source | | Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [net.c](../../../app/modules/net.c)| | 2014-12-22 | [Zeroday](https://github.com/funshine) | [PhoeniX](https://github.com/djphoenix) | [net.c](../../../app/modules/net.c)|
**SSL/TLS support** ** TLS operations was moved to the [TLS](tls.md) module **
NodeMCU includes [mbedTLS library](https://tls.mbed.org/). With default config it supports **TLS** 1.0 / 1.1 / 1.2 and most common cipher suites, including DH/ECDH key exchange methods.
Full list of features you may get on [mbedTLS page](https://tls.mbed.org/core-features).
## Constants ## Constants
Constants to be used in other functions: `net.TCP`, `net.UDP` Constants to be used in other functions: `net.TCP`, `net.UDP`
...@@ -20,20 +16,23 @@ Creates a client. ...@@ -20,20 +16,23 @@ Creates a client.
`net.createConnection(type, secure)` `net.createConnection(type, secure)`
#### Parameters #### Parameters
- `type` `net.TCP` or `net.UDP` - `type` `net.TCP` or `net.UDP`. UDP connections chained to [net.createUDPSocket()](#netcreateudpsocket)
- `secure` 1 for encrypted, 0 for plain - `secure` 1 for encrypted, 0 for plain. Secure connections chained to [tls.createConnection()](tls.md#tlscreateconnection)
#### Returns #### Returns
net.socket sub module
- for `net.TCP` - net.socket sub module
- for `net.UDP` - net.udpsocket sub module
- for `net.TCP` with `secure` - tls.socket sub module
#### Example #### Example
```lua ```lua
net.createConnection(net.UDP, 0) net.createConnection(net.TCP, 0)
``` ```
#### See also #### See also
[`net.createServer()`](#netcreateserver) [`net.createServer()`](#netcreateserver), [`net.createUDPSocket()`](#netcreateudpsocket), [`tls.createConnection()`](tls.md#tlscreateconnection)
## net.createServer() ## net.createServer()
...@@ -43,11 +42,13 @@ Creates a server. ...@@ -43,11 +42,13 @@ Creates a server.
`net.createServer(type, timeout)` `net.createServer(type, timeout)`
#### Parameters #### Parameters
- `type` `net.TCP` or `net.UDP` - `type` `net.TCP` or `net.UDP`. UDP connections chained to [net.createUDPSocket()](#netcreateudpsocket)
- `timeout` for a TCP server timeout is 1~28'800 seconds (for an inactive client to be disconnected) - `timeout` for a TCP server timeout is 1~28'800 seconds (for an inactive client to be disconnected)
#### Returns #### Returns
net.server sub module
- for `net.TCP` - net.server sub module
- for `net.UDP` - net.udpsocket sub module
#### Example #### Example
...@@ -55,6 +56,22 @@ net.server sub module ...@@ -55,6 +56,22 @@ net.server sub module
net.createServer(net.TCP, 30) -- 30s timeout net.createServer(net.TCP, 30) -- 30s timeout
``` ```
#### See also
[`net.createConnection()`](#netcreateconnection), [`net.createUDPSocket()`](#netcreateudpsocket)
## net.createUDPSocket()
Creates an UDP socket.
#### Syntax
`net.createUDPSocket()`
#### Parameters
none
#### Returns
net.udpsocket sub module
#### See also #### See also
[`net.createConnection()`](#netcreateconnection) [`net.createConnection()`](#netcreateconnection)
...@@ -117,10 +134,10 @@ sv:close() ...@@ -117,10 +134,10 @@ sv:close()
Listen on port from IP address. Listen on port from IP address.
#### Syntax #### Syntax
`net.server.listen(port,[ip],function(net.socket))` `net.server.listen([port],[ip],function(net.socket))`
#### Parameters #### Parameters
- `port` port number - `port` port number, can be omitted (random port will be chosen)
- `ip` IP address string, can be omitted - `ip` IP address string, can be omitted
- `function(net.socket)` callback function, pass to caller function as param if a connection is created successfully - `function(net.socket)` callback function, pass to caller function as param if a connection is created successfully
...@@ -149,19 +166,22 @@ end ...@@ -149,19 +166,22 @@ end
#### See also #### See also
[`net.createServer()`](#netcreateserver) [`net.createServer()`](#netcreateserver)
## net.server:on() ## net.server:getaddr()
UDP server only: Register callback functions for specific events. Returns server local address/port.
#### See also #### Syntax
[`net.socket:on()`](#netsocketon) `net.server.getaddr()`
## net.server:send() #### Parameters
none
UDP server only: Sends data to remote peer. #### Returns
`port`, `ip` (or `nil, nil` if not listening)
#### See also #### See also
[`net.socket:send()`](#netsocketsend) [`net.server:listen()`](#netserverlisten)
# net.socket Module # net.socket Module
## net.socket:close() ## net.socket:close()
...@@ -223,7 +243,7 @@ sk = nil ...@@ -223,7 +243,7 @@ sk = nil
## net.socket:getpeer() ## net.socket:getpeer()
Retrieve port and ip of peer. Retrieve port and ip of remote peer.
#### Syntax #### Syntax
`getpeer()` `getpeer()`
...@@ -232,8 +252,20 @@ Retrieve port and ip of peer. ...@@ -232,8 +252,20 @@ Retrieve port and ip of peer.
none none
#### Returns #### Returns
- `ip` of peer `port`, `ip` (or `nil, nil` if not connected)
- `port` of peer
## net.socket:getaddr()
Retrieve local port and ip of socket.
#### Syntax
`getaddr()`
#### Parameters
none
#### Returns
`port`, `ip` (or `nil, nil` if not connected)
## net.socket:hold() ## net.socket:hold()
...@@ -260,7 +292,16 @@ Register callback functions for specific events. ...@@ -260,7 +292,16 @@ Register callback functions for specific events.
#### Parameters #### Parameters
- `event` string, which can be "connection", "reconnection", "disconnection", "receive" or "sent" - `event` string, which can be "connection", "reconnection", "disconnection", "receive" or "sent"
- `function(net.socket[, string])` callback function. The first parameter is the socket. If event is "receive", the second parameter is the received data as string. - `function(net.socket[, string])` callback function. Can be `nil` to remove callback.
The first parameter of callback is the socket.
- If event is "receive", the second parameter is the received data as string.
- If event is "disconnection" or "reconnection", the second parameter is error code.
If reconnection event is specified, disconnection receives only "normal close" events.
Otherwise, all connection errors (with normal close) passed to disconnection event.
#### Returns #### Returns
`nil` `nil`
...@@ -269,11 +310,11 @@ Register callback functions for specific events. ...@@ -269,11 +310,11 @@ Register callback functions for specific events.
```lua ```lua
srv = net.createConnection(net.TCP, 0) srv = net.createConnection(net.TCP, 0)
srv:on("receive", function(sck, c) print(c) end) srv:on("receive", function(sck, c) print(c) end)
srv:connect(80,"192.168.0.66") srv:on("connection", function(sck)
srv:on("connection", function(sck, c)
-- Wait for connection before sending. -- Wait for connection before sending.
sck:send("GET / HTTP/1.1\r\nHost: 192.168.0.66\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") sck:send("GET / HTTP/1.1\r\nHost: 192.168.0.66\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
end) end)
srv:connect(80,"192.168.0.66")
``` ```
#### See also #### See also
...@@ -367,6 +408,44 @@ none ...@@ -367,6 +408,44 @@ none
#### See also #### See also
[`net.socket:hold()`](#netsockethold) [`net.socket:hold()`](#netsockethold)
# net.udpsocket Module
## net.udpsocket:close()
Closes UDP socket.
The syntax and functional identical to [`net.socket:close()`](#netsocketclose).
## net.udpsocket:listen()
Listen on port from IP address.
The syntax and functional similar to [`net.server:listen()`](#netserverlisten), but callback parameter is not provided.
## net.udpsocket:on()
Register callback functions for specific events.
The syntax and functional similar to [`net.socket:on()`](#netsocketon), only "received", "sent" and "dns" events are valid.
**`received` callback have `port` and `ip` after `data` argument.**
## net.udpsocket:send()
Sends data to specific remote peer.
## net.udpsocket:dns()
Provides DNS resolution for a hostname.
The syntax and functional identical to [`net.socket:dns()`](#netsocketdns).
## net.udpsocket:getaddr()
Retrieve local port and ip of socket.
The syntax and functional identical to [`net.socket:getaddr()`](#netsocketgetaddr).
# net.dns Module # net.dns Module
## net.dns.getdnsserver() ## net.dns.getdnsserver()
...@@ -401,11 +480,11 @@ print(net.dns.getdnsserver(1)) -- 192.168.1.252 ...@@ -401,11 +480,11 @@ print(net.dns.getdnsserver(1)) -- 192.168.1.252
Resolve a hostname to an IP address. Doesn't require a socket like [`net.socket.dns()`](#netsocketdns). Resolve a hostname to an IP address. Doesn't require a socket like [`net.socket.dns()`](#netsocketdns).
#### Syntax #### Syntax
`net.dns.resolve(host, function(ip))` `net.dns.resolve(host, function(sk, ip))`
#### Parameters #### Parameters
- `host` hostname to resolve - `host` hostname to resolve
- `function(sk, ip)` callback called when the name was resolved. Don't use `sk`, it's a socket used internally to resolve the hostname. - `function(sk, ip)` callback called when the name was resolved. `sk` is always `nil`
#### Returns #### Returns
`nil` `nil`
...@@ -438,98 +517,4 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open ...@@ -438,98 +517,4 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open
# net.cert Module # net.cert Module
This controls certificate verification when SSL is in use. This part gone to the [TLS](tls.md) module, link kept for backward compatibility.
## net.cert.verify()
Controls the vertificate verification process when the Nodemcu makes a secure connection.
#### Syntax
`net.cert.verify(enable)`
`net.cert.verify(pemdata)`
#### Parameters
- `enable` A boolean which indicates whether verification should be enabled or not. The default at boot is `false`.
- `pemdata` A string containing the CA certificate to use for verification.
#### Returns
`true` if it worked.
Can throw a number of errors if invalid data is supplied.
#### Example
Make a secure https connection and verify that the certificate chain is valid.
```
net.cert.verify(true)
http.get("https://example.com/info", nil, function (code, resp) print(code, resp) end)
```
Load a certificate into the flash chip and make a request. This is the [startssl](https://startssl.com) root certificate. They provide free
certificates.
```
net.cert.verify([[
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----
]])
http.get("https://pskreporter.info/gen404", nil, function (code, resp) print(code, resp) end)
```
#### Notes
The certificate needed for verification is stored in the flash chip. The `net.cert.verify` call with `true`
enables verification against the value stored in the flash.
The certificate can be loaded into the flash chip in two ways -- one at firmware build time, and the other at initial boot
of the firmware. In order to load the certificate at build time, just place a file containing the CA certificate (in PEM format)
at `server-ca.crt` in the root of the nodemcu-firmware build tree. The build scripts will incorporate this into the resulting
firmware image.
The alternative approach is easier for development, and that is to supply the PEM data as a string value to `net.cert.verify`. This
will store the certificate into the flash chip and turn on verification for that certificate. Subsequent boots of the nodemcu can then
use `net.cert.verify(true)` and use the stored certificate.
# TLS Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2016-12-15 | [PhoeniX](https://github.com/djphoenix) | [PhoeniX](https://github.com/djphoenix) | [tls.c](../../../app/modules/tls.c)|
**SSL/TLS support**
!!! important
TLS module requires [net](net.md) module.
NodeMCU includes [mbedTLS library](https://tls.mbed.org/). With default config it supports **TLS** 1.0 / 1.1 / 1.2 and most common cipher suites, including DH/ECDH key exchange methods.
Full list of features you may get on [mbedTLS page](https://tls.mbed.org/core-features).
This controls certificate verification when SSL is in use.
## tls.createConnection()
Creates TLS connection.
#### Syntax
`tls.createConnection()`
#### Parameters
none
#### Returns
tls.socket sub module
#### Example
```lua
tls.createConnection()
```
# tls.socket Module
## tls.socket:close()
Closes socket.
#### Syntax
`close()`
#### Parameters
none
#### Returns
`nil`
#### See also
[`tls.createConnection()`](#tlscreateconnection)
## tls.socket:connect()
Connect to a remote server.
#### Syntax
`connect(port, ip|domain)`
#### Parameters
- `port` port number
- `ip` IP address or domain name string
#### Returns
`nil`
#### See also
[`tls.socket:on()`](#tlssocketon)
## tls.socket:dns()
Provides DNS resolution for a hostname.
#### Syntax
`dns(domain, function(tls.socket, ip))`
#### Parameters
- `domain` domain name
- `function(tls.socket, ip)` callback function. The first parameter is the socket, the second parameter is the IP address as a string.
#### Returns
`nil`
#### Example
```lua
sk = tls.createConnection()
sk:dns("google.com", function(conn, ip) print(ip) end)
sk = nil
```
## tls.socket:getpeer()
Retrieve port and ip of peer.
#### Syntax
`getpeer()`
#### Parameters
none
#### Returns
- `ip` of peer
- `port` of peer
## tls.socket:hold()
Throttle data reception by placing a request to block the TCP receive function. This request is not effective immediately, Espressif recommends to call it while reserving 5*1460 bytes of memory.
#### Syntax
`hold()`
#### Parameters
none
#### Returns
`nil`
#### See also
[`tls.socket:unhold()`](#tlssocketunhold)
## tls.socket:on()
Register callback functions for specific events.
#### Syntax
`on(event, function())`
#### Parameters
- `event` string, which can be "connection", "reconnection", "disconnection", "receive" or "sent"
- `function(tls.socket[, string])` callback function. The first parameter is the socket.
If event is "receive", the second parameter is the received data as string.
If event is "reconnection", the second parameter is the reason of connection error (string).
#### Returns
`nil`
#### Example
```lua
srv = tls.createConnection()
srv:on("receive", function(sck, c) print(c) end)
srv:on("connection", function(sck, c)
-- Wait for connection before sending.
sck:send("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
end)
srv:connect(443,"google.com")
```
#### See also
- [`tls.createConnection()`](#tlscreateconnection)
- [`tls.socket:hold()`](#tlssockethold)
## tls.socket:send()
Sends data to remote peer.
#### Syntax
`send(string)`
#### Parameters
- `string` data in string which will be sent to server
#### Returns
`nil`
#### Note
Multiple consecutive `send()` calls aren't guaranteed to work (and often don't) as network requests are treated as separate tasks by the SDK. Instead, subscribe to the "sent" event on the socket and send additional data (or close) in that callback. See [#730](https://github.com/nodemcu/nodemcu-firmware/issues/730#issuecomment-154241161) for details.
#### See also
[`tls.socket:on()`](#tlssocketon)
## tls.socket:unhold()
Unblock TCP receiving data by revocation of a preceding `hold()`.
#### Syntax
`unhold()`
#### Parameters
none
#### Returns
`nil`
#### See also
[`tls.socket:hold()`](#tlssockethold)
# tls.cert Module
## tls.cert.verify()
Controls the vertificate verification process when the Nodemcu makes a secure connection.
#### Syntax
`tls.cert.verify(enable)`
`tls.cert.verify(pemdata)`
#### Parameters
- `enable` A boolean which indicates whether verification should be enabled or not. The default at boot is `false`.
- `pemdata` A string containing the CA certificate to use for verification.
#### Returns
`true` if it worked.
Can throw a number of errors if invalid data is supplied.
#### Example
Make a secure https connection and verify that the certificate chain is valid.
```
tls.cert.verify(true)
http.get("https://example.com/info", nil, function (code, resp) print(code, resp) end)
```
Load a certificate into the flash chip and make a request. This is the [startssl](https://startssl.com) root certificate. They provide free
certificates.
```
tls.cert.verify([[
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----
]])
http.get("https://pskreporter.info/gen404", nil, function (code, resp) print(code, resp) end)
```
#### Notes
The certificate needed for verification is stored in the flash chip. The `tls.cert.verify` call with `true`
enables verification against the value stored in the flash.
The certificate can be loaded into the flash chip in two ways -- one at firmware build time, and the other at initial boot
of the firmware. In order to load the certificate at build time, just place a file containing the CA certificate (in PEM format)
at `server-ca.crt` in the root of the nodemcu-firmware build tree. The build scripts will incorporate this into the resulting
firmware image.
The alternative approach is easier for development, and that is to supply the PEM data as a string value to `tls.cert.verify`. This
will store the certificate into the flash chip and turn on verification for that certificate. Subsequent boots of the nodemcu can then
use `tls.cert.verify(true)` and use the stored certificate.
...@@ -76,6 +76,7 @@ pages: ...@@ -76,6 +76,7 @@ pages:
- 'spi': 'en/modules/spi.md' - 'spi': 'en/modules/spi.md'
- 'struct': 'en/modules/struct.md' - 'struct': 'en/modules/struct.md'
- 'switec': 'en/modules/switec.md' - 'switec': 'en/modules/switec.md'
- 'tls': 'en/modules/tls.md'
- 'tm1829': 'en/modules/tm1829.md' - 'tm1829': 'en/modules/tm1829.md'
- 'tmr': 'en/modules/tmr.md' - 'tmr': 'en/modules/tmr.md'
- 'tsl2561': 'en/modules/tsl2561.md' - 'tsl2561': 'en/modules/tsl2561.md'
......
...@@ -31,8 +31,8 @@ def main(): ...@@ -31,8 +31,8 @@ def main():
help='specify the section for the data (default is .servercert.flash)') help='specify the section for the data (default is .servercert.flash)')
parser.add_argument('--name', parser.add_argument('--name',
default='net_server_cert_area', default='tls_server_cert_area',
help='specify the variable name for the data (default is net_server_cert_area)') help='specify the variable name for the data (default is tls_server_cert_area)')
parser.add_argument('file', nargs='+', parser.add_argument('file', nargs='+',
help='One or more PEM files') help='One or more PEM files')
......
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