From 857997842677c45ea952e1b58b848c40ea7148fc Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Sun, 3 Jul 2016 17:50:11 +0300 Subject: [PATCH] UDP socket in net module (LWIP) --- app/modules/net.c | 600 ++++++++++++++++++++++++++++------------- docs/en/modules/net.md | 162 +++++++++-- 2 files changed, 558 insertions(+), 204 deletions(-) diff --git a/app/modules/net.c b/app/modules/net.c index a93bb1dee8..6a3cb9fa1d 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -14,6 +14,7 @@ #include "espconn.h" #include "lwip/dns.h" #include "lwip/igmp.h" +#include "lwip/udp.h" #define TCP ESPCONN_TCP #define UDP ESPCONN_UDP @@ -28,7 +29,6 @@ static int tcpserver_cb_connect_ref = LUA_NOREF; // for tcp server connected ca static uint16_t tcp_server_timeover = 30; static struct espconn *pTcpServer = NULL; -static struct espconn *pUdpServer = NULL; typedef struct lnet_userdata { @@ -199,8 +199,7 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) lua_call(gL, 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(pesp_conn->proto.tcp->remote_port == 0){ lua_gc(gL, LUA_GCSTOP, 0); if(nud->self_ref != LUA_NOREF){ luaL_unref(gL, LUA_REGISTRYINDEX, nud->self_ref); @@ -301,6 +300,7 @@ static void net_socket_connected(void *arg) } extern int tls_socket_create( lua_State *L ); +static int net_createUDPSocket( lua_State* L ); // Lua: s = net.create(type, secure/timeout, function(conn)) static int net_create( lua_State* L, const char* mt ) @@ -312,6 +312,15 @@ static int net_create( lua_State* L, const char* mt ) uint8_t stack = 1; bool isserver = false; + { + int type = luaL_checkinteger(L, stack); + if (type == UDP) { + return net_createUDPSocket(L); + } else if (type != TCP) { + return luaL_error(L, "invalid type"); + } + stack++; + } if (mt!=NULL && c_strcmp(mt, "net.server")==0) isserver = true; else if (mt!=NULL && c_strcmp(mt, "net.socket")==0) @@ -322,10 +331,6 @@ static int net_create( lua_State* L, const char* mt ) 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) ) @@ -342,7 +347,7 @@ static int net_create( lua_State* L, const char* mt ) } #endif - if(isserver && type == ESPCONN_TCP){ + if(isserver){ if ( lua_isnumber(L, stack) ) { unsigned to = lua_tointeger(L, stack); @@ -373,57 +378,34 @@ static int net_create( lua_State* L, const char* mt ) lua_setmetatable(L, -2); // create the espconn struct - if(isserver && type==ESPCONN_TCP && pTcpServer){ + if(isserver && 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->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"); } - pesp_conn->type = type; + pesp_conn->type = ESPCONN_TCP; pesp_conn->state = ESPCONN_NONE; // reverse is for the callback function pesp_conn->reverse = nud; - if(isserver && type==ESPCONN_TCP && pTcpServer==NULL){ + if(isserver && pTcpServer==NULL){ pTcpServer = pesp_conn; - } else if(isserver && type==ESPCONN_UDP && pUdpServer==NULL){ - pUdpServer = pesp_conn; } gL = L; // global L for net module. @@ -470,15 +452,9 @@ static int net_delete( lua_State* L, const char* mt ) 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; - } + 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 @@ -526,13 +502,8 @@ static void socket_connect(struct espconn *pesp_conn) if(nud == NULL) return; - if( pesp_conn->type == ESPCONN_TCP ) { - espconn_connect(pesp_conn); - } - else if (pesp_conn->type == ESPCONN_UDP) - { - espconn_create(pesp_conn); + espconn_connect(pesp_conn); } NODE_DBG("socket_connect is called.\n"); } @@ -575,20 +546,10 @@ static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) 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"); - } + 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"); socket_connect(pesp_conn); } } @@ -632,26 +593,13 @@ static int net_start( lua_State* L, const char* mt ) 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(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); if( lua_isstring(L,stack) ) // deal with the domain string { @@ -665,55 +613,39 @@ static int net_start( lua_State* L, const char* mt ) 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"); - } + 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"); } - // 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); - } + // call back function when a connection is obtained + 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 + if(!isserver){ // self_ref is only needed by socket userdata 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); @@ -730,15 +662,6 @@ static int net_start( lua_State* L, const char* mt ) } } } - 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)) @@ -785,7 +708,7 @@ static int net_close( lua_State* L, const char* mt ) return 0; } - if(isserver && nud->pesp_conn->type == ESPCONN_TCP && tcpserver_cb_connect_ref != LUA_NOREF){ + if(isserver && tcpserver_cb_connect_ref != LUA_NOREF){ luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); tcpserver_cb_connect_ref = LUA_NOREF; } @@ -818,22 +741,9 @@ static int net_close( lua_State* L, const char* mt ) if(skt->pesp_conn) // disconnect the connection { - if(skt->pesp_conn->type == ESPCONN_TCP) - { - { - 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(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port) + espconn_disconnect(skt->pesp_conn); } } lua_settop(L, n); // reset the stack top @@ -843,7 +753,7 @@ static int net_close( lua_State* L, const char* mt ) return 0; } -// Lua: socket/udpserver:on( "method", function(s) ) +// Lua: socket:on( "method", function(s) ) static int net_on( lua_State* L, const char* mt ) { NODE_DBG("net_on is called.\n"); @@ -875,27 +785,27 @@ static int net_on( lua_State* L, const char* mt ) 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(!isserver && 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){ + }else if(!isserver && 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){ + }else if(!isserver && 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){ + }else if(!isserver && 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){ + }else if(!isserver && 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){ + }else if(!isserver && 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); @@ -939,7 +849,7 @@ static int net_send( lua_State* L, const char* mt ) return 0; } - if(isserver && nud->pesp_conn->type == ESPCONN_TCP){ + if(isserver){ return luaL_error( L, "tcp server send not supported" ); } @@ -952,16 +862,6 @@ static int net_send( lua_State* L, const char* mt ) 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 } espconn_sent(pesp_conn, (unsigned char *)payload, l); @@ -1000,7 +900,7 @@ static int net_dns( lua_State* L, const char* mt ) } pesp_conn = nud->pesp_conn; - if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server + if(!isserver){ // self_ref is only needed by socket userdata lua_pushvalue(L, 1); // copy to the top of stack if(nud->self_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); @@ -1110,20 +1010,6 @@ static int net_server_close( lua_State* L ) 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 ) { @@ -1338,14 +1224,340 @@ static int net_getdnsserver( lua_State* L ) return 1; } +typedef struct { + struct udp_pcb *pcb; + int cb_recv_ref; + int self_ref; +} net_udp_ud; + +static void net_udp_recv( net_udp_ud *ud, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port ) { + if ( !ud || !pcb || + ud->pcb != pcb || + ud->self_ref == LUA_NOREF || + ud->cb_recv_ref == LUA_NOREF) + { + pbuf_free(p); + return; + } + + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->cb_recv_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ud->self_ref); + lua_pushinteger(L, port); + char iptmp[20]; + size_t ipl = c_sprintf(iptmp, IPSTR, IP2STR(&addr->addr)); + lua_pushlstring(L, iptmp, ipl); + lua_pushlstring(L, p->payload, p->len); + lua_call(L, 4, 0); + + pbuf_free(p); +} + +static int net_createUDPSocket( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud*) lua_newuserdata(L, sizeof(net_udp_ud)); + + ud->pcb = udp_new(); + udp_recv(ud->pcb, (udp_recv_fn)net_udp_recv, ud); + ud->cb_recv_ref = LUA_NOREF; + ud->self_ref = LUA_NOREF; + + luaL_getmetatable(L, "net.udp"); + lua_setmetatable(L, -2); + + return 1; +} + +static int luaL_lwip_checkerr(err_t res, lua_State *L) { + switch (res) { + case ERR_OK: return 0; \ + case ERR_MEM: return luaL_error(L, "out of memory"); + case ERR_BUF: return luaL_error(L, "buffer error"); + case ERR_TIMEOUT: return luaL_error(L, "timeout"); + case ERR_RTE: return luaL_error(L, "routing error"); + case ERR_INPROGRESS: return luaL_error(L, "operation in progress"); + case ERR_VAL: return luaL_error(L, "illegal value"); + case ERR_WOULDBLOCK: return luaL_error(L, "operation would block"); + case ERR_ABRT: return luaL_error(L, "connection aborted"); + case ERR_RST: return luaL_error(L, "connection reset"); + case ERR_CLSD: return luaL_error(L, "connection closed"); + case ERR_CONN: return luaL_error(L, "not connected"); + case ERR_ARG: return luaL_error(L, "illegal argument"); + case ERR_USE: return luaL_error(L, "address in use"); + case ERR_IF: return luaL_error(L, "low-level netif error"); + case ERR_ISCONN: return luaL_error(L, "already connected"); + default: return luaL_error(L, "invalid LWIP state"); + } +} + +static int net_udp_getaddr( lua_State* L ); + +static int net_udp_bind( lua_State* L ) { + net_udp_ud *ud; + ip_addr_t addr; + u16_t port; + + ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + port = luaL_checkinteger(L, 2); + if (lua_isstring(L, 3)) { + size_t sl; + const char* domain = luaL_checklstring(L, 3, &sl); + addr.addr = ipaddr_addr(domain); + if (addr.addr == 0xFFFFFFFF) { + return luaL_error(L, "Invalid IP address"); + } + } else { + addr.addr = 0; + } + + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + udp_disconnect(ud->pcb); + + int err; + if ((err = luaL_lwip_checkerr(udp_bind(ud->pcb, &addr, port), L)) != 0) { + return err; + } + + lua_pushvalue(L, 1); // copy to the top of stack + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + return net_udp_getaddr(L); +} + +static int net_udp_connect( lua_State* L ) { + net_udp_ud *ud; + ip_addr_t addr; + u16_t port; + size_t sl; + + ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + port = luaL_checkinteger(L, 2); + const char* domain = luaL_checklstring(L, 3, &sl); + + if (port == 0) { + return luaL_error(L, "Invalid port"); + } + addr.addr = ipaddr_addr(domain); + if (domain == 0 || addr.addr == 0 || addr.addr == 0xFFFFFFFF) { + return luaL_error(L, "Invalid IP address"); + } + + int err; + if ((err = luaL_lwip_checkerr(udp_connect(ud->pcb, &addr, port), L)) != 0) { + return err; + } + + if (ud->self_ref == LUA_NOREF) { + lua_pushvalue(L, 1); // copy to the top of stack + ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + return net_udp_getaddr(L); +} + +static int net_udp_send( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + size_t sl; + const char* payload = luaL_checklstring(L, 2, &sl); + if (payload == 0) { + return luaL_error(L, "invalid payload"); + } + + if (ud->pcb->remote_port == 0 || ud->pcb->remote_ip.addr == 0) { + return luaL_lwip_checkerr(ERR_CONN, L); + } + + struct pbuf *buf = pbuf_alloc(PBUF_TRANSPORT, sl, PBUF_RAM); + os_memmove(buf->payload, payload, sl); + int err; + if ((err = luaL_lwip_checkerr(udp_send(ud->pcb, buf), L)) != 0) { + pbuf_free(buf); + return err; + } + pbuf_free(buf); + + return 0; +} + +static int net_udp_sendto( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + size_t sl, il; + const char* payload = luaL_checklstring(L, 2, &sl); + u16_t port = luaL_checkinteger(L, 3); + const char* domain = luaL_checklstring(L, 4, &il); + ip_addr_t addr; + + if (payload == 0) { + return luaL_error(L, "invalid payload"); + } + if (port == 0) { + return luaL_error(L, "Invalid port"); + } + addr.addr = ipaddr_addr(domain); + if (domain == 0 || addr.addr == 0 || addr.addr == 0xFFFFFFFF) { + return luaL_error(L, "Invalid IP address"); + } + + struct pbuf *buf = pbuf_alloc(PBUF_TRANSPORT, sl, PBUF_RAM); + os_memmove(buf->payload, payload, sl); + int err; + if ((err = luaL_lwip_checkerr(udp_sendto(ud->pcb, buf, &addr, port), L)) != 0) { + pbuf_free(buf); + return err; + } + pbuf_free(buf); + + return 0; +} + +static int net_udp_getaddr( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if (ud->self_ref == LUA_NOREF) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + + char ipaddr[20]; + c_sprintf(ipaddr, IPSTR, IP2STR(&ud->pcb->local_ip.addr)); + + lua_pushinteger(L, ud->pcb->local_port); + lua_pushstring(L, ipaddr); + + return 2; +} + +static int net_udp_getpeer( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if (ud->self_ref == LUA_NOREF) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + + if (ud->pcb->remote_port == 0 || ud->pcb->remote_ip.addr == 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + + char ipaddr[20]; + c_sprintf(ipaddr, IPSTR, IP2STR(&ud->pcb->remote_ip.addr)); + + lua_pushinteger(L, ud->pcb->remote_port); + lua_pushstring(L, ipaddr); + + return 2; +} + +static int net_udp_close( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + udp_disconnect(ud->pcb); + + lua_gc(L, LUA_GCSTOP, 0); + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + lua_gc(L, LUA_GCRESTART, 0); + + return 0; +} + +static int net_udp_on( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + size_t sl; + const char* event = luaL_checklstring(L, 2, &sl); + + luaL_checkanyfunction(L, 3); + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + + if(c_strcmp(event, "receive") == 0){ + luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_recv_ref); + ud->cb_recv_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + return luaL_error(L, "invalid method"); + } + + return 0; +} + +static int net_udp_delete( lua_State* L ) { + net_udp_ud *ud = (net_udp_ud *)luaL_checkudata(L, 1, "net.udp"); + luaL_argcheck(L, ud, 1, "UDP socket expected"); + if(ud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if (ud->pcb) { + udp_disconnect(ud->pcb); + udp_remove(ud->pcb); + ud->pcb = 0; + } + luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_recv_ref); + ud->cb_recv_ref = LUA_NOREF; + + lua_gc(L, LUA_GCSTOP, 0); + luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); + ud->self_ref = LUA_NOREF; + lua_gc(L, LUA_GCRESTART, 0); + + return 0; +} + extern const LUA_REG_TYPE tls_cert_map[]; // 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 ) }, @@ -1374,9 +1586,24 @@ static const LUA_REG_TYPE net_dns_map[] = { { LNILKEY, LNILVAL } }; +static const LUA_REG_TYPE net_udp_map[] = { + { LSTRKEY( "bind" ), LFUNCVAL( net_udp_bind ) }, + { LSTRKEY( "on" ), LFUNCVAL( net_udp_on ) }, + { LSTRKEY( "connect" ), LFUNCVAL( net_udp_connect ) }, + { LSTRKEY( "sendto" ), LFUNCVAL( net_udp_sendto ) }, + { LSTRKEY( "send" ), LFUNCVAL( net_udp_send ) }, + { LSTRKEY( "getaddr" ), LFUNCVAL( net_udp_getaddr ) }, + { LSTRKEY( "getpeer" ), LFUNCVAL( net_udp_getpeer ) }, + { LSTRKEY( "close" ), LFUNCVAL( net_udp_close ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( net_udp_delete ) }, + { LSTRKEY( "__index" ), LROVAL( net_udp_map ) }, + { LNILKEY, LNILVAL } +}; + static const LUA_REG_TYPE net_map[] = { { LSTRKEY( "createServer" ), LFUNCVAL( net_createServer ) }, { LSTRKEY( "createConnection" ), LFUNCVAL( net_createConnection ) }, + { LSTRKEY( "createUDPSocket" ), LFUNCVAL( net_createUDPSocket ) }, { LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) }, { LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) }, { LSTRKEY( "dns" ), LROVAL( net_dns_map ) }, @@ -1399,6 +1626,7 @@ int luaopen_net( lua_State *L ) { luaL_rometatable(L, "net.server", (void *)net_server_map); // create metatable for net.server luaL_rometatable(L, "net.socket", (void *)net_socket_map); // create metatable for net.socket + luaL_rometatable(L, "net.udp", (void *)net_udp_map); // create metatable for net.udp return 0; } diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 424ae56416..6418bde74a 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -16,7 +16,7 @@ Creates a client. `net.createConnection(type, secure)` #### Parameters -- `type` `net.TCP` or `net.UDP` +- `type` `net.TCP` or `net.UDP`. `net.UDP` chains to `net.createUDPSocket()` - `secure` 1 for encrypted, 0 for plain. Secure connections chained to [tls.createConnection()](tls#tlscreateconnection) #### Returns @@ -29,7 +29,7 @@ net.createConnection(net.TCP, 0) ``` #### See also -[`net.createServer()`](#netcreateserver), [`tls.createConnection()`](tls#tlscreateconnection) +[`net.createServer()`](#netcreateserver), [`net.createUDPSocket()`](#netcreateudpsocket), [`tls.createConnection()`](tls#tlscreateconnection) ## net.createServer() @@ -39,7 +39,7 @@ Creates a server. `net.createServer(type, timeout)` #### Parameters -- `type` `net.TCP` or `net.UDP` +- `type` `net.TCP` or `net.UDP`. `net.UDP` chains to `net.createUDPSocket()` - `timeout` for a TCP server timeout is 1~28'800 seconds (for an inactive client to be disconnected) #### Returns @@ -52,7 +52,27 @@ net.createServer(net.TCP, 30) -- 30s timeout ``` #### See also -[`net.createConnection()`](#netcreateconnection) +[`net.createConnection()`](#netcreateconnection), [`net.createUDPSocket()`](#netcreateudpsocket) + +## net.createUDPSocket() + +Creates a UDP socket. + +#### Syntax +`net.createUDPSocket()` + +#### Parameters +none + +#### Returns +net.udp sub module + +#### Example + +```lua +net.createUDPSocket() +``` + ## net.multicastJoin() @@ -139,20 +159,6 @@ end) #### See also [`net.createServer()`](#netcreateserver) -## net.server:on() - -UDP server only: Register callback functions for specific events. - -#### See also -[`net.socket:on()`](#netsocketon) - -## net.server:send() - -UDP server only: Sends data to remote peer. - -#### See also -[`net.socket:send()`](#netsocketsend) - # net.socket Module ## net.socket:close() @@ -423,6 +429,126 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open #### See also [`net.dns:getdnsserver()`](#netdnsgetdnsserver) +# net.udp Module + +## net.udp:bind() + +Bounds socket to specified ip/port. + +#### Syntax +`bind(port[, ip])` + +#### Parameters +- `port` Port to bind socket. If 0, uses random of free ports. +- `ip` Bind to specific IP. Default binds to `0.0.0.0` (any address). + +#### Returns +`nil` + +## net.udp:connect() + +Connects socket to ip/port. + +#### Syntax +`connect(port, ip)` + +#### Parameters +- `port` Port to connect. +- `ip` Address to connect. + +#### Returns +`nil` + +## net.udp:close() + +Closes UDP socket. Closed socket can be bound or connected again. + +#### Syntax +`close()` + +#### Parameters +none + +#### Returns +`nil` + +## net.udp:send() + +Sends data to previously `connect`ed peer. + +#### Syntax +`send(data)` + +#### Parameters +- `data` Data (string) to send + +#### Returns +`nil` + +#### See also +[`net.udp:connect()`](#netudpconnect) + +## net.udp:sendto() + +Sends data to specific peer. + +#### Syntax +`sendto(data, port, ip)` + +#### Parameters +- `data` Data (string) to send +- `port` Port to send data +- `ip` IP address of peer to send data + +#### Returns +`nil` + +## net.udp:on() + +Registers callback for specific socket event. + +#### Syntax +`on(event, callback)` + +#### Parameters +- `event` Event to call back +- `callback` Callback function + +#### Events +- `receive`: `function(sock, port, ip, data)` Emits when received data chunk from peer. + +#### Returns +`nil` + +## net.udp:getaddr() + +Gets local ip/port of socket. + +#### Syntax +`getaddr()` + +#### Parameters +none + +#### Returns +`port, ip` which socket currently bound + +## net.udp:getpeer() + +Gets remote peer's ip/port. + +#### Syntax +`getpeer()` + +#### Parameters +none + +#### Returns +`port, ip` which socket currently connected to + +#### See also +[`net.udp:connect()`](#netudpconnect) + # net.cert Module This part gone to [tls](tls) module, link kept for backward compatibility.