From 3adba91b15cf568ad7680817b4e4a0e1434b5c43 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Sat, 31 Dec 2016 15:14:03 +0300 Subject: [PATCH] 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 --- app/include/user_modules.h | 1 + app/modules/net.c | 2358 ++++++++++++------------------------ app/modules/tls.c | 649 ++++++++++ docs/en/modules/net.md | 239 ++-- docs/en/modules/tls.md | 288 +++++ mkdocs.yml | 1 + tools/make_server_cert.py | 4 +- 7 files changed, 1853 insertions(+), 1687 deletions(-) create mode 100644 app/modules/tls.c create mode 100644 docs/en/modules/tls.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index ffcad680..0dad1e1e 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -60,6 +60,7 @@ //#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_SWITEC //#define LUA_USE_MODULES_TM1829 +#define LUA_USE_MODULES_TLS #define LUA_USE_MODULES_TMR //#define LUA_USE_MODULES_TSL2561 //#define LUA_USE_MODULES_U8G diff --git a/app/modules/net.c b/app/modules/net.c index 744a4122..4d92479a 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -10,1363 +10,780 @@ #include "c_types.h" #include "mem.h" +#include "osapi.h" +#include "lwip/err.h" #include "lwip/ip_addr.h" -#include "espconn.h" #include "lwip/dns.h" +#include "lwip/igmp.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" -#define TCP ESPCONN_TCP -#define UDP ESPCONN_UDP - -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]; +#if defined(CLIENT_SSL_ENABLE) && defined(LUA_USE_MODULES_NET) && defined(LUA_USE_MODULES_TLS) +#define TLS_MODULE_PRESENT #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 -static int expose_array(lua_State* L, char *array, unsigned short len); -#endif +typedef const char net_table_name[14]; -#define MAX_SOCKET 5 -static int socket_num = 0; -static int socket[MAX_SOCKET]; -static int tcpserver_cb_connect_ref = LUA_NOREF; // for tcp server connected callback -static uint16_t tcp_server_timeover = 30; +static const net_table_name NET_TABLES[] = { + "net.tcpserver", + "net.tcpsocket", + "net.udpsocket" +}; +#define NET_TABLE_TCP_SERVER NET_TABLES[0] +#define NET_TABLE_TCP_CLIENT NET_TABLES[1] +#define NET_TABLE_UDP_SOCKET NET_TABLES[2] -static struct espconn *pTcpServer = NULL; -static struct espconn *pUdpServer = NULL; +#define TYPE_TCP TYPE_TCP_CLIENT +#define TYPE_UDP TYPE_UDP_SOCKET -typedef struct lnet_userdata -{ - struct espconn *pesp_conn; +typedef struct lnet_userdata { + enum net_type type; int self_ref; - int cb_connect_ref; - int cb_reconnect_ref; - int cb_disconnect_ref; - int cb_receive_ref; - int cb_send_ref; - int cb_dns_found_ref; -#ifdef CLIENT_SSL_ENABLE - uint8_t secure; -#endif -}lnet_userdata; + union { + struct tcp_pcb *tcp_pcb; + struct udp_pcb *udp_pcb; + void *pcb; + }; + union { + struct { + int cb_accept_ref; + int timeout; + } 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; -static void net_server_disconnected(void *arg) // for tcp server only -{ - NODE_DBG("net_server_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 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(" disconnected.\n"); -#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); +#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"); } - int i; - lua_gc(L, LUA_GCSTOP, 0); - for(i=0;iself_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--; +} + +#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); + + ud->type = type; + ud->self_ref = LUA_NOREF; + ud->pcb = NULL; + + switch (type) { + case TYPE_TCP_CLIENT: + ud->client.cb_connect_ref = LUA_NOREF; + ud->client.cb_reconnect_ref = LUA_NOREF; + ud->client.cb_disconnect_ref = LUA_NOREF; + ud->client.hold = 0; + case TYPE_UDP_SOCKET: + ud->client.wait_dns = 0; + ud->client.cb_dns_ref = LUA_NOREF; + ud->client.cb_receive_ref = LUA_NOREF; + ud->client.cb_sent_ref = LUA_NOREF; + break; + case TYPE_TCP_SERVER: + ud->server.cb_accept_ref = LUA_NOREF; break; - } } - lua_gc(L, LUA_GCRESTART, 0); + return ud; } -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; +#pragma mark - LWIP callbacks + +static void net_err_cb(void *arg, err_t err) { + lnet_userdata *ud = (lnet_userdata*)arg; + if (!ud || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return; + ud->pcb = NULL; // Will be freed at LWIP level 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); + int ref; + if (err != ERR_OK && ud->client.cb_reconnect_ref != LUA_NOREF) + ref = ud->client.cb_reconnect_ref; + else ref = ud->client.cb_disconnect_ref; + if (ref != LUA_NOREF) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + lua_pushinteger(L, err); + lua_call(L, 2, 0); } - - if(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); -} - -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) -{ - 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) -{ - // NODE_DBG("net_socket_sent 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_send_ref == LUA_NOREF) - return; - if(nud->self_ref == LUA_NOREF) - return; - lua_State *L = lua_getstate(); - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_send_ref); - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua - lua_call(L, 1, 0); -} - -static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) -{ - NODE_DBG("net_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){ - NODE_DBG("nud null.\n"); - return; - } - if(nud->cb_dns_found_ref == LUA_NOREF){ - NODE_DBG("cb_dns_found_ref null.\n"); - return; - } - - if(nud->self_ref == LUA_NOREF){ - NODE_DBG("self_ref null.\n"); - return; - } - - lua_State *L = lua_getstate(); - - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua - - if(ipaddr == NULL) - { - NODE_DBG( "DNS Fail!\n" ); - lua_pushnil(L); - }else{ - // ipaddr->addr is a uint32_t ip - 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 - } - // "enhanced" end - - lua_call(L, 2, 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) ){ + if (ud->client.wait_dns == 0) { 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 - } + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; lua_gc(L, LUA_GCRESTART, 0); } } -static void net_server_connected(void *arg) // for tcp only -{ - NODE_DBG("net_server_connected is called.\n"); - struct espconn *pesp_conn = arg; - int i = 0; - lnet_userdata *skt = NULL; - if(pesp_conn == NULL) - 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;ipcb != tpcb) return ERR_ABRT; + if (err != ERR_OK) { + net_err_cb(arg, err); + return ERR_ABRT; } - 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_rawgeti(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); // get function - // create a new client object - skt = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata)); - - if(!skt){ - NODE_ERR("can't newudata\n"); - lua_pop(L, 1); - return; - } - // set its metatable - 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) -{ - NODE_DBG("net_socket_connected 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; - // can receive and send data, even if there is no connected callback in lua. - espconn_regist_recvcb(pesp_conn, net_socket_received); - espconn_regist_sentcb(pesp_conn, net_socket_sent); - espconn_regist_disconcb(pesp_conn, net_socket_disconnected); - - if(nud->cb_connect_ref == LUA_NOREF) - return; - if(nud->self_ref == LUA_NOREF) - return; - lua_State *L = lua_getstate(); - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->cb_connect_ref); - lua_rawgeti(L, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua - lua_call(L, 1, 0); -} - -// Lua: s = net.create(type, secure/timeout, function(conn)) -static int net_create( lua_State* L, const char* mt ) -{ - NODE_DBG("net_create is called.\n"); - 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 - luaL_getmetatable(L, mt); - 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 + if (ud->self_ref != LUA_NOREF && ud->client.cb_connect_ref != LUA_NOREF) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); lua_call(L, 1, 0); } - - return 1; + return ERR_OK; } -// 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 = (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(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 - 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) -{ - if(pesp_conn == NULL) - return; - lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; - if(nud == NULL) - return; - - if( pesp_conn->type == ESPCONN_TCP ) - { -#ifdef CLIENT_SSL_ENABLE - if(nud->secure){ - espconn_secure_connect(pesp_conn); - } - else -#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); -static int dns_reconn_count = 0; -static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) -{ - 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; +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(); - 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; + if (ud->self_ref != LUA_NOREF && ud->client.cb_dns_ref != LUA_NOREF) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + if (addr.addr != 0xFFFFFFFF) { + char iptmp[16]; + bzero(iptmp, 16); + ets_sprintf(iptmp, IPSTR, IP2STR(&addr.addr)); + lua_pushstring(L, iptmp); + } else { + lua_pushnil(L); } - NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); - host_ip.addr = 0; - espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_found); + lua_call(L, 2, 0); + } + ud->client.wait_dns --; + if (ud->pcb && ud->type == TYPE_TCP_CLIENT && ud->tcp_pcb->state == CLOSED) { + tcp_connect(ud->tcp_pcb, &addr, ud->tcp_pcb->remote_port, net_connected_cb); + } else if (!ud->pcb && ud->client.wait_dns == 0) { + lua_gc(L, LUA_GCSTOP, 0); + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + lua_gc(L, LUA_GCRESTART, 0); + } +} + +static void net_recv_cb(lnet_userdata *ud, struct pbuf *p, ip_addr_t *addr, u16_t port) { + if (ud->client.cb_receive_ref == LUA_NOREF) { + pbuf_free(p); 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_State *L = lua_getstate(); + int num_args = 2; + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + lua_pushlstring(L, p->payload, p->len); + if (ud->type == TYPE_UDP_SOCKET) { + num_args += 2; + char iptmp[16]; + 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); } -// Lua: server:listen( port, ip, function(con) ) -// Lua: socket:connect( port, ip, function(con) ) -static int net_start( lua_State* L, const char* mt ) -{ - 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; +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; } - nud = (lnet_userdata *)luaL_checkudata(L, stack, mt); - luaL_argcheck(L, nud, stack, "Server/Socket expected"); - stack++; - - if(nud==NULL){ - NODE_DBG("userdata is nil.\n"); - return 0; - } - - if(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - 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 - { - domain = luaL_checklstring( L, stack, &il ); - 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 - if ( pesp_conn->type == ESPCONN_TCP ) { - 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 - 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 - lua_pushvalue(L, 1); // copy to the top of stack - 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(isserver){ // no secure server support for now - espconn_regist_connectcb(pesp_conn, net_server_connected); - // tcp server, SSL is not supported -#ifdef CLIENT_SSL_ENABLE - // if(nud->secure) - // 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 - { - 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. - } - - if(!isserver){ - 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; + net_recv_cb(ud, p, addr, port); } -// Lua: server/socket:close() -// 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) - isserver = true; - else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) - isserver = false; - else - { - NODE_DBG("wrong metatable for net_close.\n"); - return 0; +static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + lnet_userdata *ud = (lnet_userdata*)arg; + if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) + return ERR_ABRT; + if (!p) { + net_err_cb(arg, err); + return tcp_close(tpcb); } - - if(isserver && nud->pesp_conn->type == ESPCONN_TCP && tcpserver_cb_connect_ref != LUA_NOREF){ - luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); - tcpserver_cb_connect_ref = LUA_NOREF; - } - - int n = lua_gettop(L); - skt = nud; - - do{ - if(isserver && skt == NULL){ - if(socket[i] != LUA_NOREF){ // there is client socket exists - lua_rawgeti(L, LUA_REGISTRYINDEX, socket[i]); // get the referenced user_data to stack top -#if 0 - socket[i] = LUA_NOREF; - socket_num--; -#endif // do this in net_server_disconnected - i++; - if(lua_isuserdata(L,-1)){ - skt = lua_touserdata(L,-1); - } else { - lua_pop(L, 1); - continue; - } - }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 - } - } - } -#if 0 - // unref the self_ref - if(LUA_NOREF!=skt->self_ref){ // for a server 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 - } -#endif - lua_settop(L, n); // reset the stack top - skt = NULL; - } while( isserver && iself_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; + net_recv_cb(ud, p, 0, 0); + tcp_recved(tpcb, ud->client.hold ? 0 : TCP_WND); + return ERR_OK; } -// 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 (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_on.\n"); - return 0; - } - - 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(!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; +static err_t net_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) { + lnet_userdata *ud = (lnet_userdata*)arg; + if (!ud || !ud->pcb || ud->type != TYPE_TCP_CLIENT || ud->self_ref == LUA_NOREF) return ERR_ABRT; + if (ud->client.cb_sent_ref == LUA_NOREF) return ERR_OK; + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + lua_call(L, 1, 0); + return ERR_OK; } -// Lua: server/socket:send( string, function(sent) ) -static int net_send( lua_State* L, const char* mt ) -{ - // NODE_DBG("net_send is called.\n"); - bool isserver = false; - struct espconn *pesp_conn = NULL; - lnet_userdata *nud; - size_t l; - - 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; - } +static err_t net_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) { + lnet_userdata *ud = (lnet_userdata*)arg; + if (!ud || ud->type != TYPE_TCP_SERVER || !ud->pcb) return ERR_ABRT; + if (ud->self_ref == LUA_NOREF || ud->server.cb_accept_ref == LUA_NOREF) return ERR_ABRT; - if(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - return 0; - } - pesp_conn = nud->pesp_conn; + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref); - 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_send.\n"); - return 0; - } - - if(isserver && nud->pesp_conn->type == ESPCONN_TCP){ - return luaL_error( L, "tcp server send not supported" ); - } - -#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(" sending data.\n"); -#endif - - const char *payload = luaL_checklstring( L, 2, &l ); - if (l>1460 || payload == NULL) - return luaL_error( L, "need <1460 payload" ); - - 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 - 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) ) -static int net_dns( lua_State* L, const char* mt ) -{ - NODE_DBG("net_dns is called.\n"); - bool isserver = false; - struct espconn *pesp_conn = NULL; - lnet_userdata *nud; - size_t l; - - 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 (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_send.\n"); - return 0; - } - - if(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - return 0; - } - pesp_conn = nud->pesp_conn; - - if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server - lua_pushvalue(L, 1); // copy to the top of stack - if(nud->self_ref != LUA_NOREF) - luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); - nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); - } - - 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); - } - - 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) ) -static int net_dns_static( lua_State* L ) -{ - const char *mt = "net.socket"; - if (!lua_isstring( L, 1 )) - return luaL_error( L, "wrong parameter type (domain)" ); - - int rfunc = LUA_NOREF; //save reference to func - if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ - rfunc = luaL_ref(L, LUA_REGISTRYINDEX); - } - int rdom = luaL_ref(L, LUA_REGISTRYINDEX); //save reference to domain - - lua_settop(L,0); //empty stack - lua_getfield(L, LUA_GLOBALSINDEX, "net"); - lua_getfield(L, -1, "createConnection"); - lua_remove(L, -2); //remove "net" from stack - 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 - - lua_rawgeti(L, LUA_REGISTRYINDEX, rdom); // load domain back to the stack - lua_rawgeti(L, LUA_REGISTRYINDEX, rfunc); // load the callback function back to the stack - - luaL_unref(L, LUA_REGISTRYINDEX, rdom); //free reference - luaL_unref(L, LUA_REGISTRYINDEX, rfunc); //free reference - - bool isserver = false; - struct espconn *pesp_conn = NULL; - lnet_userdata *nud; - size_t l; - - 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(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - return 0; - } - pesp_conn = nud->pesp_conn; - - lua_pushvalue(L, 1); // copy to the top of stack - if(nud->self_ref != LUA_NOREF) - luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + lnet_userdata *nud = net_create(L, TYPE_TCP_CLIENT); + lua_pushvalue(L, 2); nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + nud->tcp_pcb = newpcb; + tcp_arg(nud->tcp_pcb, nud); + tcp_err(nud->tcp_pcb, net_err_cb); + tcp_recv(nud->tcp_pcb, net_tcp_recv_cb); + tcp_sent(nud->tcp_pcb, net_sent_cb); + 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); - const char *domain = luaL_checklstring( L, 2, &l ); - if (l>128 || domain == NULL) - return luaL_error( L, "need <128 domain" ); + lua_call(L, 1, 0); - 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 net_connected_cb(nud, nud->tcp_pcb, ERR_OK); +} + +#pragma mark - Lua API - create + +#ifdef TLS_MODULE_PRESENT +extern int tls_socket_create( lua_State *L ); +#endif + +// Lua: net.createUDPSocket() +int net_createUDPSocket( lua_State *L ) { + net_create(L, TYPE_UDP_SOCKET); + return 1; +} + +// Lua: net.createServer(type, timeout) +int net_createServer( lua_State *L ) { + int type, timeout; + + type = luaL_optlong(L, 1, TYPE_TCP); + timeout = luaL_optlong(L, 2, 30); + + if (type == TYPE_UDP) return net_createUDPSocket( L ); + if (type != TYPE_TCP) return luaL_error(L, "invalid type"); + + lnet_userdata *u = net_create(L, TYPE_TCP_SERVER); + u->server.timeout = timeout; + return 1; +} + +// Lua: net.createConnection(type, secure) +int net_createConnection( lua_State *L ) { + int type, secure; + + type = luaL_optlong(L, 1, TYPE_TCP); + secure = luaL_optlong(L, 2, 0); + + if (type == TYPE_UDP) return net_createUDPSocket( L ); + if (type != TYPE_TCP) return luaL_error(L, "invalid type"); + if (secure) { +#ifdef TLS_MODULE_PRESENT + return tls_socket_create( L ); +#else + return luaL_error(L, "secure connections not enabled"); +#endif } - - 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; + net_create(L, TYPE_TCP_CLIENT); + return 1; } -// Lua: s = net.createServer(type, function(server)) -static int net_createServer( lua_State* L ) -{ - const char *mt = "net.server"; - return net_create(L, mt); -} +#pragma mark - Get & check userdata -// Lua: server:delete() -static int net_server_delete( lua_State* L ) -{ - const char *mt = "net.server"; - 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) ) -static int net_socket_on( lua_State* L ) -{ - const char *mt = "net.socket"; - return net_on(L, mt); -} - -// Lua: socket:send( string, function() ) -static int net_socket_send( lua_State* L ) -{ - const char *mt = "net.socket"; - return net_send(L, mt); -} - -static int net_socket_hold( lua_State* L ) -{ - const char *mt = "net.socket"; - struct espconn *pesp_conn = NULL; - lnet_userdata *nud; - size_t l; - - 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; +lnet_userdata *net_get_udata_s( lua_State *L, int stack ) { + if (!lua_isuserdata(L, stack)) return NULL; + lnet_userdata *ud = (lnet_userdata *)lua_touserdata(L, stack); + switch (ud->type) { + case TYPE_TCP_CLIENT: + case TYPE_TCP_SERVER: + case TYPE_UDP_SOCKET: + break; + default: return NULL; } - - if(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - return 0; - } - pesp_conn = nud->pesp_conn; - espconn_recv_hold(pesp_conn); - - return 0; + const char *mt = NET_TABLES[ud->type]; + ud = luaL_checkudata(L, stack, mt); + return ud; } +#define net_get_udata(L) net_get_udata_s(L, 1) -static int net_socket_unhold( lua_State* L ) -{ - const char *mt = "net.socket"; - struct espconn *pesp_conn = NULL; - lnet_userdata *nud; - size_t l; +#pragma mark - Lua API - 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; +// Lua: server:listen(port, addr, function(c)), socket:listen(port, addr) +int net_listen( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type == TYPE_TCP_CLIENT) + return luaL_error(L, "invalid user data"); + if (ud->pcb) + return luaL_error(L, "already listening"); + int stack = 2; + uint16_t port = 0; + const char *domain = "0.0.0.0"; + if (lua_isnumber(L, stack)) + port = lua_tointeger(L, stack++); + if (lua_isstring(L, stack)) { + size_t dl = 0; + domain = luaL_checklstring(L, stack++, &dl); } - - if(nud->pesp_conn == NULL){ - NODE_DBG("nud->pesp_conn is NULL.\n"); - return 0; + 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"); + } } - pesp_conn = nud->pesp_conn; - espconn_recv_unhold(pesp_conn); - - return 0; -} - -// Lua: ip,port = sk:getpeer() -static int net_socket_getpeer( lua_State* L ) -{ - lnet_userdata *nud; - const char *mt = "net.socket"; - nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); - luaL_argcheck(L, nud, 1, "Server/Socket expected"); - - if(nud!=NULL && nud->pesp_conn!=NULL ){ - char temp[20] = {0}; - c_sprintf(temp, IPSTR, IP2STR( &(nud->pesp_conn->proto.tcp->remote_ip) ) ); - if ( nud->pesp_conn->proto.tcp->remote_port != 0 ) { - lua_pushstring( L, temp ); - lua_pushinteger( L, nud->pesp_conn->proto.tcp->remote_port ); - } else { - lua_pushnil( L ); - lua_pushnil( L ); + err_t err = ERR_OK; + switch (ud->type) { + case TYPE_TCP_SERVER: + ud->tcp_pcb = tcp_new(); + if (!ud->tcp_pcb) + return luaL_error(L, "cannot allocate PCB"); + err = tcp_bind(ud->tcp_pcb, &addr, port); + if (err == ERR_OK) { + tcp_arg(ud->tcp_pcb, ud); + struct tcp_pcb *pcb = tcp_listen(ud->tcp_pcb); + if (!pcb) { + err = ERR_MEM; + } else { + ud->tcp_pcb = pcb; + tcp_accept(ud->tcp_pcb, net_accept_cb); + } } - } else { - lua_pushnil( L ); - lua_pushnil( L ); + break; + case TYPE_UDP_SOCKET: + ud->udp_pcb = udp_new(); + if (!ud->udp_pcb) + return luaL_error(L, "cannot allocate PCB"); + udp_recv(ud->udp_pcb, net_udp_recv_cb, ud); + err = udp_bind(ud->udp_pcb, &addr, port); + break; } + if (err != ERR_OK) { + switch (ud->type) { + case TYPE_TCP_SERVER: + tcp_close(ud->tcp_pcb); + ud->tcp_pcb = NULL; + break; + case TYPE_UDP_SOCKET: + udp_remove(ud->udp_pcb); + ud->udp_pcb = NULL; + break; + } + return lwip_lua_checkerr(L, err); + } + if (ud->self_ref == LUA_NOREF) { + lua_pushvalue(L, 1); + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + return 0; +} + +// Lua: client:connect(port, addr) +int net_connect( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type != TYPE_TCP_CLIENT) + return luaL_error(L, "invalid user data"); + if (ud->pcb) + return luaL_error(L, "already connected"); + uint16_t port = luaL_checkinteger(L, 2); + if (port == 0) + return luaL_error(L, "specify port"); + const char *domain = "127.0.0.1"; + if (lua_isstring(L, 3)) { + size_t dl = 0; + domain = luaL_checklstring(L, 3, &dl); + } + if (lua_gettop(L) > 3) { + luaL_argcheck(L, lua_isfunction(L, 4) || lua_islightfunction(L, 4), 4, "not a function"); + lua_pushvalue(L, 4); + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref); + ud->client.cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + ud->tcp_pcb = tcp_new(); + if (!ud->tcp_pcb) + return luaL_error(L, "cannot allocate PCB"); + tcp_arg(ud->tcp_pcb, ud); + tcp_err(ud->tcp_pcb, net_err_cb); + tcp_recv(ud->tcp_pcb, net_tcp_recv_cb); + tcp_sent(ud->tcp_pcb, net_sent_cb); + ud->tcp_pcb->remote_port = port; + ip_addr_t addr; + ud->client.wait_dns ++; + int unref = 0; + if (ud->self_ref == LUA_NOREF) { + unref = 1; + lua_pushvalue(L, 1); + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + err_t err = dns_gethostbyname(domain, &addr, net_dns_cb, ud); + if (err == ERR_OK) { + net_dns_cb(domain, &addr, ud); + } else if (err != ERR_INPROGRESS) { + ud->client.wait_dns --; + if (unref) { + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + } + tcp_abort(ud->tcp_pcb); + ud->tcp_pcb = NULL; + return lwip_lua_checkerr(L, err); + } + return 0; +} + +// Lua: client/socket:on(name, callback) +int net_on( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type == TYPE_TCP_SERVER) + return luaL_error(L, "invalid user data"); + int *refptr = NULL; + const char *name = luaL_checkstring(L, 2); + if (!name) return luaL_error(L, "need callback name"); + switch (ud->type) { + case TYPE_TCP_CLIENT: + if (strcmp("connection",name)==0) + { refptr = &ud->client.cb_connect_ref; break; } + if (strcmp("disconnection",name)==0) + { refptr = &ud->client.cb_disconnect_ref; break; } + if (strcmp("reconnection",name)==0) + { refptr = &ud->client.cb_reconnect_ref; break; } + case TYPE_UDP_SOCKET: + if (strcmp("dns",name)==0) + { refptr = &ud->client.cb_dns_ref; break; } + if (strcmp("receive",name)==0) + { refptr = &ud->client.cb_receive_ref; break; } + if (strcmp("sent",name)==0) + { refptr = &ud->client.cb_sent_ref; break; } + break; + default: return luaL_error(L, "invalid user data"); + } + if (refptr == NULL) + return luaL_error(L, "invalid callback name"); + if (lua_isfunction(L, 3) || lua_islightfunction(L, 3)) { + lua_pushvalue(L, 3); + luaL_unref(L, LUA_REGISTRYINDEX, *refptr); + *refptr = luaL_ref(L, LUA_REGISTRYINDEX); + } else if (lua_isnil(L, 3)) { + luaL_unref(L, LUA_REGISTRYINDEX, *refptr); + *refptr = LUA_NOREF; + } else { + return luaL_error(L, "invalid callback function"); + } + return 0; +} + +// Lua: client:send(data, function(c)), socket:send(port, ip, data, function(s)) +int net_send( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type == TYPE_TCP_SERVER) + return luaL_error(L, "invalid user data"); + ip_addr_t addr; + uint16_t port; + const char *data; + size_t datalen = 0; + int stack = 2; + if (ud->type == TYPE_UDP_SOCKET) { + size_t dl = 0; + port = luaL_checkinteger(L, stack++); + if (port == 0) return luaL_error(L, "need port"); + const char *domain = luaL_checklstring(L, stack++, &dl); + if (!domain) return luaL_error(L, "need IP address"); + if (!ipaddr_aton(domain, &addr)) return luaL_error(L, "invalid IP address"); + } + data = luaL_checklstring(L, stack++, &datalen); + if (!data || datalen == 0) return luaL_error(L, "no data to send"); + if (lua_isfunction(L, stack) || lua_islightfunction(L, stack)) { + lua_pushvalue(L, stack++); + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref); + ud->client.cb_sent_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + if (ud->type == TYPE_UDP_SOCKET && !ud->pcb) { + ud->udp_pcb = udp_new(); + if (!ud->udp_pcb) + return luaL_error(L, "cannot allocate PCB"); + udp_recv(ud->udp_pcb, net_udp_recv_cb, ud); + ip_addr_t laddr = {0}; + err_t err = udp_bind(ud->udp_pcb, &laddr, 0); + if (err != ERR_OK) { + udp_remove(ud->udp_pcb); + ud->udp_pcb = NULL; + return lwip_lua_checkerr(L, err); + } + if (ud->self_ref == LUA_NOREF) { + lua_pushvalue(L, 1); + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + } + if (!ud->pcb || ud->self_ref == LUA_NOREF) + return luaL_error(L, "not connected"); + err_t err; + if (ud->type == TYPE_UDP_SOCKET) { + struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + if (!pb) + return luaL_error(L, "cannot allocate message buffer"); + pbuf_take(pb, data, datalen); + err = udp_sendto(ud->udp_pcb, pb, &addr, port); + pbuf_free(pb); + if (ud->client.cb_sent_ref != LUA_NOREF) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + lua_call(L, 1, 0); + } + } else if (ud->type == TYPE_TCP_CLIENT) { + err = tcp_write(ud->tcp_pcb, data, datalen, TCP_WRITE_FLAG_COPY); + } + 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; + } + return 0; +} + +// Lua: client:unhold() +int net_unhold( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type != TYPE_TCP_CLIENT) + return luaL_error(L, "invalid user data"); + if (ud->client.hold && ud->tcp_pcb) { + ud->client.hold = 0; + ud->tcp_pcb->flags |= TF_ACK_NOW; + tcp_recved(ud->tcp_pcb, TCP_WND); + } + return 0; +} + +// Lua: client/socket:dns(domain, callback(socket, addr)) +int net_dns( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type == TYPE_TCP_SERVER) + return luaL_error(L, "invalid user data"); + size_t dl = 0; + const char *domain = luaL_checklstring(L, 2, &dl); + if (!domain) + return luaL_error(L, "no domain specified"); + if (lua_isfunction(L, 3) || lua_islightfunction(L, 3)) { + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref); + lua_pushvalue(L, 3); + ud->client.cb_dns_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + if (ud->client.cb_dns_ref == LUA_NOREF) + return luaL_error(L, "no callback specified"); + ud->client.wait_dns ++; + int unref = 0; + if (ud->self_ref == LUA_NOREF) { + unref = 1; + lua_pushvalue(L, 1); + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + ip_addr_t addr; + err_t err = dns_gethostbyname(domain, &addr, net_dns_cb, ud); + if (err == ERR_OK) { + net_dns_cb(domain, &addr, ud); + } else if (err != ERR_INPROGRESS) { + ud->client.wait_dns --; + if (unref) { + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + } + return lwip_lua_checkerr(L, err); + } + return 0; +} + +// Lua: client:getpeer() +int net_getpeer( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type != TYPE_TCP_CLIENT) + return luaL_error(L, "invalid user data"); + if (!ud->pcb) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + uint16_t port = ud->tcp_pcb->remote_port; + ip_addr_t addr = ud->tcp_pcb->remote_ip; + if (port == 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + char addr_str[16]; + bzero(addr_str, 16); + ets_sprintf(addr_str, IPSTR, IP2STR(&addr.addr)); + lua_pushinteger(L, port); + lua_pushstring(L, addr_str); return 2; } -// Lua: socket:dns( string, function(ip) ) -static int net_socket_dns( lua_State* L ) -{ - const char *mt = "net.socket"; - return net_dns(L, mt); +// Lua: client/server/socket:getaddr() +int net_getaddr( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud) return luaL_error(L, "invalid user data"); + if (!ud->pcb) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + uint16_t port; + ip_addr_t addr; + switch (ud->type) { + case TYPE_TCP_CLIENT: + case TYPE_TCP_SERVER: + addr = ud->tcp_pcb->local_ip; + port = ud->tcp_pcb->local_port; + break; + case TYPE_UDP_SOCKET: + addr = ud->udp_pcb->local_ip; + port = ud->udp_pcb->local_port; + break; + } + if (port == 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + char addr_str[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_multicastJoinLeave( lua_State *L, int join) -{ +// Lua: client/server/socket:close() +int net_close( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud) return luaL_error(L, "invalid user data"); + if (ud->pcb) { + switch (ud->type) { + case TYPE_TCP_CLIENT: + if (ERR_OK != tcp_close(ud->tcp_pcb)) { + tcp_arg(ud->tcp_pcb, NULL); + tcp_abort(ud->tcp_pcb); + } + ud->tcp_pcb = NULL; + break; + case TYPE_TCP_SERVER: + tcp_close(ud->tcp_pcb); + ud->tcp_pcb = NULL; + break; + case TYPE_UDP_SOCKET: + udp_remove(ud->udp_pcb); + ud->udp_pcb = NULL; + break; + } + } else { + return luaL_error(L, "not connected"); + } + if (ud->type == TYPE_TCP_SERVER || + (ud->pcb == NULL && ud->client.wait_dns == 0)) { + lua_gc(L, LUA_GCSTOP, 0); + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + lua_gc(L, LUA_GCRESTART, 0); + } + return 0; +} + +int net_delete( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud) return luaL_error(L, "no user data"); + if (ud->pcb) { + switch (ud->type) { + case TYPE_TCP_CLIENT: + tcp_arg(ud->tcp_pcb, NULL); + tcp_abort(ud->tcp_pcb); + ud->tcp_pcb = NULL; + break; + case TYPE_TCP_SERVER: + tcp_close(ud->tcp_pcb); + ud->tcp_pcb = NULL; + break; + case TYPE_UDP_SOCKET: + udp_remove(ud->udp_pcb); + ud->udp_pcb = NULL; + break; + } + } + switch (ud->type) { + case TYPE_TCP_CLIENT: + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_connect_ref); + ud->client.cb_connect_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_disconnect_ref); + ud->client.cb_disconnect_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_reconnect_ref); + ud->client.cb_reconnect_ref = LUA_NOREF; + case TYPE_UDP_SOCKET: + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_dns_ref); + ud->client.cb_dns_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_receive_ref); + ud->client.cb_receive_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, ud->client.cb_sent_ref); + ud->client.cb_sent_ref = LUA_NOREF; + break; + case TYPE_TCP_SERVER: + luaL_unref(L, LUA_REGISTRYINDEX, ud->server.cb_accept_ref); + ud->server.cb_accept_ref = LUA_NOREF; + break; + } + lua_gc(L, LUA_GCSTOP, 0); + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + lua_gc(L, LUA_GCRESTART, 0); + return 0; +} + +#pragma mark - Multicast + +static int net_multicastJoinLeave( lua_State *L, int join) { size_t il; ip_addr_t multicast_addr; ip_addr_t if_addr; @@ -1391,227 +808,85 @@ static int net_multicastJoinLeave( lua_State *L, int join) multicast_addr.addr = ipaddr_addr(multicast_ip); if (join) { - espconn_igmp_join(&if_addr, &multicast_addr); + igmp_joingroup(&if_addr, &multicast_addr); } else { - espconn_igmp_leave(&if_addr, &multicast_addr); + igmp_leavegroup(&if_addr, &multicast_addr); } return 0; } + // Lua: net.multicastJoin(ifip, multicastip) // if ifip "" or "any" all interfaces are affected -static int net_multicastJoin( lua_State* L ) -{ +static int net_multicastJoin( lua_State* L ) { return net_multicastJoinLeave(L,1); } // Lua: net.multicastLeave(ifip, multicastip) // if ifip "" or "any" all interfaces are affected -static int net_multicastLeave( lua_State* L ) -{ +static int net_multicastLeave( lua_State* L ) { return net_multicastJoinLeave(L,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; - } +#pragma mark - DNS - if (!pem) { - return "No PEM blob"; - } +static void net_dns_static_cb(const char *name, ip_addr_t *ipaddr, void *callback_arg) { + ip_addr_t addr; + if (ipaddr != NULL) + addr = *ipaddr; + else addr.addr = 0xFFFFFFFF; + int cb_ref = ((int*)callback_arg)[0]; + c_free(callback_arg); + lua_State *L = lua_getstate(); - // Scan for -----BEGIN CERT - pem = strstr(pem, "-----BEGIN "); - if (!pem) { - return "No PEM header"; + lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref); + lua_pushnil(L); + if (addr.addr != 0xFFFFFFFF) { + char iptmp[20]; + size_t ipl = c_sprintf(iptmp, IPSTR, IP2STR(&addr.addr)); + lua_pushlstring(L, iptmp, ipl); + } else { + lua_pushnil(L); } + lua_call(L, 2, 0); - 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; + luaL_unref(L, LUA_REGISTRYINDEX, cb_ref); } -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; - } +// Lua: net.dns.resolve( domain, function(sk, ip) ) +static int net_dns_static( lua_State* L ) { + size_t dl; + const char* domain = luaL_checklstring(L, 1, &dl); + if (!domain && dl > 128) { + return luaL_error(L, "wrong domain"); } - 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 + luaL_checkanyfunction(L, 2); + lua_pushvalue(L, 2); // copy argument (func) to the top of stack + int cbref = luaL_ref(L, LUA_REGISTRYINDEX); + if (cbref == LUA_NOREF) { + return luaL_error(L, "wrong callback"); } - - 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; + int *cbref_ptr = c_zalloc(sizeof(int)); + cbref_ptr[0] = cbref; + ip_addr_t addr; + err_t err = dns_gethostbyname(domain, &addr, net_dns_static_cb, cbref_ptr); + if (err == ERR_OK) { + net_dns_static_cb(domain, &addr, cbref_ptr); + return 0; + } else if (err == ERR_INPROGRESS) { + return 0; } else { - enable = lua_toboolean(L, 1); + int e = lwip_lua_checkerr(L, err); + c_free(cbref_ptr); + return e; } - - bool rc; - - if (enable) { - // See if there is a cert there - if (net_client_cert_area[0] == 0x00 || net_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: net.cert.verify(true / false | PEM data [, PEM data] ) -static int net_cert_verify(lua_State *L) -{ - int enable; - - uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_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, &net_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 (net_server_cert_area[0] == 0x00 || net_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; + return 0; } // 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; u32_t ip32; @@ -1631,8 +906,7 @@ static int net_setdnsserver( lua_State* L ) } // 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); if (numdns >= DNS_MAX_SERVERS) 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 ) return 1; } -#if 0 -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; -} +#pragma mark - Tables -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 -static int expose_array(lua_State* L, char *array, unsigned short len) { - char** parray = lua_newuserdata(L, len); - *parray = array; - luaL_getmetatable(L, "net.array"); - lua_setmetatable(L, -2); - return 1; -} +#ifdef TLS_MODULE_PRESENT +extern const LUA_REG_TYPE tls_cert_map[]; #endif // Module function map -static const LUA_REG_TYPE net_server_map[] = { - { LSTRKEY( "listen" ), LFUNCVAL( net_server_listen ) }, - { LSTRKEY( "close" ), LFUNCVAL( net_server_close ) }, - { LSTRKEY( "on" ), LFUNCVAL( net_udpserver_on ) }, - { LSTRKEY( "send" ), LFUNCVAL( net_udpserver_send ) }, -//{ LSTRKEY( "delete" ), LFUNCVAL( net_server_delete ) }, - { LSTRKEY( "__gc" ), LFUNCVAL( net_server_delete ) }, - { LSTRKEY( "__index" ), LROVAL( net_server_map ) }, +static const LUA_REG_TYPE net_tcpserver_map[] = { + { LSTRKEY( "listen" ), LFUNCVAL( net_listen ) }, + { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) }, + { LSTRKEY( "close" ), LFUNCVAL( net_close ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) }, + { LSTRKEY( "__index" ), LROVAL( net_tcpserver_map ) }, { LNILKEY, LNILVAL } }; -static const LUA_REG_TYPE net_socket_map[] = { - { LSTRKEY( "connect" ), LFUNCVAL( net_socket_connect ) }, - { LSTRKEY( "close" ), LFUNCVAL( net_socket_close ) }, - { LSTRKEY( "on" ), LFUNCVAL( net_socket_on ) }, - { LSTRKEY( "send" ), LFUNCVAL( net_socket_send ) }, - { LSTRKEY( "hold" ), LFUNCVAL( net_socket_hold ) }, - { LSTRKEY( "unhold" ), LFUNCVAL( net_socket_unhold ) }, - { LSTRKEY( "dns" ), LFUNCVAL( net_socket_dns ) }, - { LSTRKEY( "getpeer" ), LFUNCVAL( net_socket_getpeer ) }, -//{ LSTRKEY( "delete" ), LFUNCVAL( net_socket_delete ) }, - { LSTRKEY( "__gc" ), LFUNCVAL( net_socket_delete ) }, - { LSTRKEY( "__index" ), LROVAL( net_socket_map ) }, +static const LUA_REG_TYPE net_tcpsocket_map[] = { + { LSTRKEY( "connect" ), LFUNCVAL( net_connect ) }, + { LSTRKEY( "close" ), LFUNCVAL( net_close ) }, + { LSTRKEY( "on" ), LFUNCVAL( net_on ) }, + { LSTRKEY( "send" ), LFUNCVAL( net_send ) }, + { LSTRKEY( "hold" ), LFUNCVAL( net_hold ) }, + { LSTRKEY( "unhold" ), LFUNCVAL( net_unhold ) }, + { LSTRKEY( "dns" ), LFUNCVAL( net_dns ) }, + { LSTRKEY( "getpeer" ), LFUNCVAL( net_getpeer ) }, + { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) }, + { LSTRKEY( "__index" ), LROVAL( net_tcpsocket_map ) }, { LNILKEY, LNILVAL } }; -#if 0 -static const LUA_REG_TYPE net_array_map[] = { - { LSTRKEY( "__index" ), LFUNCVAL( net_array_index ) }, - { LSTRKEY( "__newindex" ), LFUNCVAL( net_array_newindex ) }, - { LNILKEY, LNILVAL } -}; -#endif -static const LUA_REG_TYPE net_cert_map[] = { - { LSTRKEY( "verify" ), LFUNCVAL( net_cert_verify ) }, -#ifdef CLIENT_SSL_CERT_AUTH_ENABLE - { LSTRKEY( "auth" ), LFUNCVAL( net_cert_auth ) }, -#endif +static const LUA_REG_TYPE net_udpsocket_map[] = { + { LSTRKEY( "listen" ), LFUNCVAL( net_listen ) }, + { LSTRKEY( "close" ), LFUNCVAL( net_close ) }, + { LSTRKEY( "on" ), LFUNCVAL( net_on ) }, + { LSTRKEY( "send" ), LFUNCVAL( net_send ) }, + { LSTRKEY( "dns" ), LFUNCVAL( net_dns ) }, + { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) }, + { LSTRKEY( "__index" ), LROVAL( net_udpsocket_map ) }, { LNILKEY, LNILVAL } }; static const LUA_REG_TYPE net_dns_map[] = { - { LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) }, - { LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) }, - { LSTRKEY( "resolve" ), LFUNCVAL( net_dns_static ) }, + { LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) }, + { LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) }, + { LSTRKEY( "resolve" ), LFUNCVAL( net_dns_static ) }, { LNILKEY, LNILVAL } }; static const LUA_REG_TYPE net_map[] = { { LSTRKEY( "createServer" ), LFUNCVAL( net_createServer ) }, { LSTRKEY( "createConnection" ), LFUNCVAL( net_createConnection ) }, + { LSTRKEY( "createUDPSocket" ), LFUNCVAL( net_createUDPSocket ) }, { LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) }, { LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) }, { LSTRKEY( "dns" ), LROVAL( net_dns_map ) }, -#ifdef CLIENT_SSL_ENABLE - { LSTRKEY( "cert" ), LROVAL(net_cert_map) }, +#ifdef TLS_MODULE_PRESENT + { LSTRKEY( "cert" ), LROVAL( tls_cert_map ) }, #endif - { LSTRKEY( "TCP" ), LNUMVAL( TCP ) }, - { LSTRKEY( "UDP" ), LNUMVAL( UDP ) }, + { LSTRKEY( "TCP" ), LNUMVAL( TYPE_TCP ) }, + { LSTRKEY( "UDP" ), LNUMVAL( TYPE_UDP ) }, { LSTRKEY( "__metatable" ), LROVAL( net_map ) }, { LNILKEY, LNILVAL } }; int luaopen_net( lua_State *L ) { - int i; - for(i=0;ipesp_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 diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index a7e3eed1..fadcaefc 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -1,13 +1,9 @@ # net Module | 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** - -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). +** TLS operations was moved to the [TLS](tls.md) module ** ## Constants Constants to be used in other functions: `net.TCP`, `net.UDP` @@ -20,20 +16,23 @@ Creates a client. `net.createConnection(type, secure)` #### Parameters -- `type` `net.TCP` or `net.UDP` -- `secure` 1 for encrypted, 0 for plain +- `type` `net.TCP` or `net.UDP`. UDP connections chained to [net.createUDPSocket()](#netcreateudpsocket) +- `secure` 1 for encrypted, 0 for plain. Secure connections chained to [tls.createConnection()](tls.md#tlscreateconnection) #### 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 ```lua -net.createConnection(net.UDP, 0) +net.createConnection(net.TCP, 0) ``` #### See also -[`net.createServer()`](#netcreateserver) +[`net.createServer()`](#netcreateserver), [`net.createUDPSocket()`](#netcreateudpsocket), [`tls.createConnection()`](tls.md#tlscreateconnection) ## net.createServer() @@ -43,11 +42,13 @@ Creates a server. `net.createServer(type, timeout)` #### 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) #### Returns -net.server sub module + +- for `net.TCP` - net.server sub module +- for `net.UDP` - net.udpsocket sub module #### Example @@ -55,6 +56,22 @@ net.server sub module 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 [`net.createConnection()`](#netcreateconnection) @@ -117,10 +134,10 @@ sv:close() Listen on port from IP address. #### Syntax -`net.server.listen(port,[ip],function(net.socket))` +`net.server.listen([port],[ip],function(net.socket))` #### Parameters -- `port` port number +- `port` port number, can be omitted (random port will be chosen) - `ip` IP address string, can be omitted - `function(net.socket)` callback function, pass to caller function as param if a connection is created successfully @@ -149,19 +166,22 @@ end #### See also [`net.createServer()`](#netcreateserver) -## net.server:on() +## net.server:getaddr() -UDP server only: Register callback functions for specific events. +Returns server local address/port. + +#### Syntax +`net.server.getaddr()` + +#### Parameters +none + +#### Returns +`port`, `ip` (or `nil, nil` if not listening) #### See also -[`net.socket:on()`](#netsocketon) +[`net.server:listen()`](#netserverlisten) -## net.server:send() - -UDP server only: Sends data to remote peer. - -#### See also -[`net.socket:send()`](#netsocketsend) # net.socket Module ## net.socket:close() @@ -223,7 +243,7 @@ sk = nil ## net.socket:getpeer() -Retrieve port and ip of peer. +Retrieve port and ip of remote peer. #### Syntax `getpeer()` @@ -232,8 +252,20 @@ Retrieve port and ip of peer. none #### Returns -- `ip` of peer -- `port` of peer +`port`, `ip` (or `nil, nil` if not connected) + +## 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() @@ -260,7 +292,16 @@ Register callback functions for specific events. #### Parameters - `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 `nil` @@ -269,11 +310,11 @@ Register callback functions for specific events. ```lua srv = net.createConnection(net.TCP, 0) srv:on("receive", function(sck, c) print(c) end) -srv:connect(80,"192.168.0.66") -srv:on("connection", function(sck, c) +srv:on("connection", function(sck) -- 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") end) +srv:connect(80,"192.168.0.66") ``` #### See also @@ -367,6 +408,44 @@ none #### See also [`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.getdnsserver() @@ -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). #### Syntax -`net.dns.resolve(host, function(ip))` +`net.dns.resolve(host, function(sk, ip))` #### Parameters - `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 `nil` @@ -438,98 +517,4 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open # net.cert Module -This controls certificate verification when SSL is in use. - -## 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. - +This part gone to the [TLS](tls.md) module, link kept for backward compatibility. diff --git a/docs/en/modules/tls.md b/docs/en/modules/tls.md new file mode 100644 index 00000000..d472e3e6 --- /dev/null +++ b/docs/en/modules/tls.md @@ -0,0 +1,288 @@ +# 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. diff --git a/mkdocs.yml b/mkdocs.yml index 5b33e436..a030cd36 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -76,6 +76,7 @@ pages: - 'spi': 'en/modules/spi.md' - 'struct': 'en/modules/struct.md' - 'switec': 'en/modules/switec.md' + - 'tls': 'en/modules/tls.md' - 'tm1829': 'en/modules/tm1829.md' - 'tmr': 'en/modules/tmr.md' - 'tsl2561': 'en/modules/tsl2561.md' diff --git a/tools/make_server_cert.py b/tools/make_server_cert.py index 89cec10e..01206595 100644 --- a/tools/make_server_cert.py +++ b/tools/make_server_cert.py @@ -31,8 +31,8 @@ def main(): help='specify the section for the data (default is .servercert.flash)') parser.add_argument('--name', - default='net_server_cert_area', - help='specify the variable name for the data (default is net_server_cert_area)') + default='tls_server_cert_area', + help='specify the variable name for the data (default is tls_server_cert_area)') parser.add_argument('file', nargs='+', help='One or more PEM files')