From 04cff01975be7e75df771a7bb7a6c4770cf66d72 Mon Sep 17 00:00:00 2001
From: vsky <blue205@centrum.cz>
Date: Sat, 2 Sep 2017 00:00:26 +0200
Subject: [PATCH] Net_info module exposing ping function initial commit

---
 app/include/lwip/app/ping.h |   1 +
 app/include/user_modules.h  |   1 +
 app/lwip/app/ping.c         |   1 +
 app/modules/hx711.c         |   1 +
 app/modules/net_info.c      | 228 ++++++++++++++++++++++++++++++++++++
 docs/modules/net_info.md    |  79 +++++++++++++
 mkdocs.yml                  |   1 +
 7 files changed, 312 insertions(+)
 create mode 100644 app/modules/net_info.c
 create mode 100644 docs/modules/net_info.md

diff --git a/app/include/lwip/app/ping.h b/app/include/lwip/app/ping.h
index 21e26e9101..161b7aba6b 100644
--- a/app/include/lwip/app/ping.h
+++ b/app/include/lwip/app/ping.h
@@ -75,6 +75,7 @@ struct ping_resp{
 	uint32 bytes;
 	uint32 total_bytes;
 	uint32 total_time;
+        uint8  ttl;
 	sint8  ping_err;
 };
 
diff --git a/app/include/user_modules.h b/app/include/user_modules.h
index 0b28dbe757..c3d7affa0d 100644
--- a/app/include/user_modules.h
+++ b/app/include/user_modules.h
@@ -38,6 +38,7 @@
 //#define LUA_USE_MODULES_MDNS
 #define LUA_USE_MODULES_MQTT
 #define LUA_USE_MODULES_NET
+#define LUA_USE_MODULES_NET_INFO
 #define LUA_USE_MODULES_NODE
 #define LUA_USE_MODULES_OW
 //#define LUA_USE_MODULES_PCM
diff --git a/app/lwip/app/ping.c b/app/lwip/app/ping.c
index 38045c5b69..25ac72c5b5 100644
--- a/app/lwip/app/ping.c
+++ b/app/lwip/app/ping.c
@@ -165,6 +165,7 @@ ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr)
 				  pingresp.seqno = ntohs(iecho->seqno);
 				  pingresp.ping_err = 0;
 				  pingmsg->ping_opt->recv_function(pingmsg->ping_opt,(void*) &pingresp);
+				  pingresp.ttl = iphdr->_ttl;
 			  }
 		  }
 		  seqno = iecho->seqno;
diff --git a/app/modules/hx711.c b/app/modules/hx711.c
index cd871504c4..a517a288d2 100644
--- a/app/modules/hx711.c
+++ b/app/modules/hx711.c
@@ -8,6 +8,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "user_interface.h"
+#include "task/task.h"
 static uint8_t data_pin;
 static uint8_t clk_pin;
 // The fields below are after the pin_num conversion
diff --git a/app/modules/net_info.c b/app/modules/net_info.c
new file mode 100644
index 0000000000..18442eb7e7
--- /dev/null
+++ b/app/modules/net_info.c
@@ -0,0 +1,228 @@
+// ***************************************************************************
+// net_info module for ESP8266 with nodeMCU
+//
+// adopted jan 2017 for commit to nodeMCU by Wolfgang Rosner
+// re-worked by Lukas Voborsky, @voborsky
+// ***************************************************************************
+ 
+/**
+  * test.ping()
+  * Description:
+  *     Send ICMP ping request to address, optionally call callback when response received
+  *
+  * Syntax:
+  *     wifi.sta.getconfig(ssid, password) --Set STATION configuration, Auto-connect by default, Connects to any BSSID
+  *     test.ping(address)              -- send 4 ping requests to target address
+  *     test.ping(address, n)           -- send n ping requests to target address
+  *     test.ping(address, n, callback) -- send n ping requests to target address
+  * Parameters:
+  *     address: string 
+  *     n: number of requests to send
+  *     callback: 
+  * Returns:
+  *     Nothing.
+  *
+  * Example:
+  *     test.ping("192.168.0.1")               -- send 4 pings to 192.168.0.1
+  *     test.ping("192.168.0.1", 10)           -- send 10 pings to 192.168.0.1
+  *     test.ping("192.168.0.1", 10, got_ping) -- send 10 pings to 192.168.0.1, call got_ping() with the
+  *                                           --     ping results
+  */
+
+#define NODE_DEBUG
+ 
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+
+// #include <string.h>
+// #include <strings.h>
+// #include <stddef.h>
+// #include <stdint.h>
+#include "mem.h"
+#include "task/task.h"
+
+#include "lwip/ip_addr.h"
+#include "espconn.h"
+#include "lwip/dns.h" 
+#include "lwip/app/ping.h"
+#include "lwip/raw.h"
+
+typedef struct {
+  struct ping_option ping_opt; // ping_opt needs to be the first element of the structure
+  sint32_t ping_callback_ref; 
+  sint32_t self_ref;  /* Reference to this structure as userdata */
+  uint8_t ping_host_count;
+  ip_addr_t ping_host_ip;
+ } net_info_ping_t;
+typedef net_info_ping_t* ping_t;  
+
+static ping_t nip_get( lua_State *L, int stack ) {
+	ping_t nip = (ping_t)luaL_checkudata(L, stack, "net_info.ping");
+	if (nip == NULL)
+		return (ping_t)luaL_error(L, "ping object expected");
+	return nip;
+}
+  
+void ping_received(void *arg, void *data) {
+    NODE_DBG("[ping_received] ");
+    // struct ping_option *pingopt = (struct ping_option*)arg;
+    ping_t nip  = (ping_t)arg;
+    struct ping_resp *pingresp = (struct ping_resp*)data;
+
+    char ipaddrstr[16];
+    ip_addr_t source_ip;
+    
+    source_ip.addr = nip->ping_opt.ip;
+    ipaddr_ntoa_r(&source_ip, ipaddrstr, sizeof(ipaddrstr));
+
+    if (nip->ping_callback_ref != LUA_NOREF) {
+      lua_State *L = lua_getstate();
+      lua_rawgeti(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
+      lua_pushinteger(L, pingresp->bytes);
+      lua_pushstring(L, ipaddrstr);
+      lua_pushinteger(L, pingresp->seqno);
+      lua_pushinteger(L, pingresp->ttl);
+      lua_pushinteger(L, pingresp->resp_time);
+      lua_call(L, 5, 0);
+    }
+}
+
+// static void ping_by_hostname(const char *name, ip_addr_t *ipaddr, void *arg) {
+    // if (!ping_opt) {
+      // ping_opt = (struct ping_option *)os_zalloc(sizeof(struct ping_option));
+    // } else {
+      // os_memset (ping_opt, 0, sizeof(struct ping_option));
+    // }
+
+    // if (ipaddr == NULL) {
+      // lua_State *L = lua_getstate();
+      // luaL_error(L, "SEVERE problem resolving hostname - network and DNS accessible?\n");
+      // return;
+    // }
+    // if (ipaddr->addr == IPADDR_NONE) {
+      // lua_State *L = lua_getstate();
+      // luaL_error(L, "problem resolving hostname - maybe nonexistent host?\n");
+      // return;
+    // }
+    
+    // ping_opt->count = ping_host_count;
+    // ping_opt->ip = ipaddr->addr;
+    // ping_opt->coarse_time = 0;
+    // ping_opt->recv_function = &ping_received;
+    
+    // ping_start(ping_opt);
+// }
+
+// struct ping_option{
+  // uint32 count;
+  // uint32 ip;
+  // uint32 coarse_time;
+  // ping_recv_function recv_function;
+  // ping_sent_function sent_function;
+  // void* reverse;
+  // struct ping_msg *parent_msg;
+// };    
+  
+static int net_info_ping(lua_State *L)
+{
+    NODE_DBG("[net_info_ping]");
+    ping_t nip = nip_get(L, 1);
+
+    if (nip->self_ref != LUA_REFNIL) {
+      luaL_unref(L, LUA_REGISTRYINDEX, nip->self_ref);
+      nip->self_ref = LUA_NOREF;
+    }
+    
+    const char *ping_target;
+    unsigned count = 4;
+     
+    // retrieve address arg (mandatory)
+    if (lua_isstring(L, 2)) {
+        ping_target = luaL_checkstring(L, 1);
+    } else {
+        return luaL_error(L, "no address specified");
+    }
+
+    // retrieve count arg (optional)
+    if (lua_isnumber(L, 3)) {
+        count = luaL_checkinteger(L, 2);
+    }
+   
+    // retrieve callback arg (optional)
+    if (nip->ping_callback_ref != LUA_NOREF) 
+      luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
+    nip->ping_callback_ref = LUA_NOREF;
+       
+    if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION)
+      nip->ping_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+    
+    // attempt to parse ping target as IP
+    uint32 ip = ipaddr_addr(ping_target);
+
+    if (ip != IPADDR_NONE) {
+        nip->ping_opt.count = count;
+        nip->ping_opt.ip = ip;
+        nip->ping_opt.coarse_time = 0;
+        nip->ping_opt.recv_function = &ping_received;
+        
+        NODE_DBG("[net_info_ping] calling ping_start");
+        ping_start(&(nip->ping_opt));
+    } else {
+        // ping_host_count = count;
+
+        // struct espconn *ping_dns_lookup;
+        // espconn_create(ping_dns_lookup);
+        // espconn_gethostbyname(ping_dns_lookup, ping_target, &ping_host_ip, ping_by_hostname);   
+    }
+
+    return 0;
+}
+
+
+static int net_info_create( lua_State *L ) {
+  NODE_DBG("[net_info_create]");
+  ping_t nip = (ping_t)lua_newuserdata(L, sizeof(net_info_ping_t));
+  if (!nip) return luaL_error(L, "not enough memory");
+  luaL_getmetatable(L, "net_info.ping");
+  lua_setmetatable(L, -2);
+  nip->ping_callback_ref = LUA_NOREF;
+  nip->self_ref = LUA_NOREF;
+  return 1;
+}
+
+static int net_info_unregister(lua_State* L){
+  NODE_DBG("[net_info_unregister]");
+	ping_t nip = nip_get(L, 1);
+
+	if (nip->self_ref != LUA_REFNIL) {
+		luaL_unref(L, LUA_REGISTRYINDEX, nip->self_ref);
+		nip->self_ref = LUA_NOREF;
+	}
+
+	if(nip->ping_callback_ref != LUA_NOREF)
+		luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
+	nip->ping_callback_ref = LUA_NOREF;
+	return 0;
+}
+
+
+// Module function map
+LROT_BEGIN(net_info_dyn)
+  LROT_FUNCENTRY( ping, net_info_ping )
+  LROT_FUNCENTRY( __gc, net_info_unregister )
+  LROT_TABENTRY( __index, net_info_dyn )
+LROT_END( net_info_dyn, net_info_dyn, LROT_MASK_GC_INDEX )
+
+
+LROT_BEGIN(net_info)
+  LROT_FUNCENTRY( create, net_info_create )
+LROT_END( net_ifo, NULL, 0 )
+
+int luaopen_net_info( lua_State *L ){
+  NODE_DBG("[luaopen_net_info]");
+  luaL_rometatable(L, "net_info.ping", LROT_TABLEREF(net_info));
+  return 0;
+}
+
+NODEMCU_MODULE(NET_INFO, "net_info", net_info, NULL);
diff --git a/docs/modules/net_info.md b/docs/modules/net_info.md
new file mode 100644
index 0000000000..d095dcc446
--- /dev/null
+++ b/docs/modules/net_info.md
@@ -0,0 +1,79 @@
+# net_info Module
+| Since  | Origin / Contributor  | Maintainer  | Source  |
+| :----- | :-------------------- | :---------- | :------ |
+| 2017-01-21 | [smcl](http://blog.mclemon.io/esp8266-contributing-to-the-nodemcu-ecosystem) | [wolfgangr](https://github.com/wolfgangr/nodemcu-firmware) | [net_info.c](../../../app/modules/net_info.c)
+
+
+This module is a stub to collect common network diagnostic and analysis tools.
+
+##net_info.ping()
+ send ICMP ECHO_REQUEST to network hosts 
+ 
+**Synopsis:**:
+```
+net_info.ping(host [, count [, callback]]) 
+```
+
+**Usage example:**
+```
+=net_info.ping("www.google.de",2)
+> 32 bytes from 172.217.20.195, icmp_seq=25 ttl=53 time=37ms
+32 bytes from 172.217.20.195, icmp_seq=26 ttl=53 time=38ms
+ping 2, timeout 0, total payload 64 bytes, 1946 ms
+```
+
+
+**Description:** (from *linux man 8 ping*)
+
+> `ping` uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. ECHO_REQUEST datagrams (''pings'') have an IP and ICMP header, followed by a struct timeval and then an arbitrary number of ''pad'' bytes used to fill out the packet. 
+
+
+**Usage variants**
+
+ping host **by IP-Adress:**  
+```
+net_info.ping("1.2.3.4")
+```
+Enter IP-Adress in commonly known dotted quad-decimal notation, enclosed in string quotes.
+
+!!! Note
+    There is only limited error checking on the validity of IP adresses in lwIP. Check twice!
+
+
+Use **DNS resolver** to get IP adress for ping:
+```
+net_info.ping("hostname")
+```
+
+!!! Note
+    This only works with Network access and DNS resolution properly configured.
+
+    
+Custom **ping count**:
+```
+net_info.ping(host, count)
+```
+* `host` can be given by IP or hostname as above.
+* `count` number of repetitive pings - default is 4 if omitted.
+
+
+Ping **callback function**:
+```
+net_info.ping(host, count, callback)
+```
+Instead of printing ping results on the console, the given callback function ist called each time a ECHO_RESPONSE is received.
+
+The callback receives the following arguments:  
+```
+function ping_recv(bytes, ipaddr, seqno, ttl, ping)
+```
+* length of message
+* ip-address of pinged host
+* icmp_seq number
+* time-to-live-value
+* ping time in ms
+
+!!! Caution
+    The callback functionality is still untested. Use at even more your own risk!
+
+For further reference to callback functionality, see smcl origin link provided on top of this page.
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index 4d704d1751..fc5dc2a934 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -83,6 +83,7 @@ pages:
     - 'mdns': 'modules/mdns.md'
     - 'mqtt': 'modules/mqtt.md'
     - 'net': 'modules/net.md'
+    - 'net_info': 'modules/net_info.md'
     - 'node': 'modules/node.md'
     - 'ow (1-Wire)': 'modules/ow.md'
     - 'pcm' : 'modules/pcm.md'