Преглед на файлове

auto update script executed

Toby Chui преди 1 година
родител
ревизия
fe603243e0
променени са 8 файла, в които са добавени 406 реда и са изтрити 17 реда
  1. 1 1
      api.go
  2. 0 1
      go.mod
  3. 0 2
      go.sum
  4. 1 1
      main.go
  5. 68 0
      mod/wakeonlan/wakeonlan.go
  6. 3 0
      start.go
  7. 234 1
      web/components/networktools.html
  8. 99 11
      wrappers.go

+ 1 - 1
api.go

@@ -81,12 +81,12 @@ func initAPIs() {
 	//mDNS APIs
 	authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
 	authRouter.HandleFunc("/api/mdns/discover", HandleMdnsScanning)
-	authRouter.HandleFunc("/api/ssdp/discover", HandleSsdpDiscovery)
 
 	//Network utilities
 	authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
 	authRouter.HandleFunc("/api/tools/webssh", HandleCreateProxySession)
 	authRouter.HandleFunc("/api/tools/websshSupported", HandleWebSshSupportCheck)
+	authRouter.HandleFunc("/api/tools/wol", HandleWakeOnLan)
 
 	//If you got APIs to add, append them here
 }

+ 0 - 1
go.mod

@@ -3,7 +3,6 @@ module imuslab.com/zoraxy
 go 1.16
 
 require (
-	github.com/bcurren/go-ssdp v0.0.0-20130927201504-ae8e7a0ef8a8 // indirect
 	github.com/boltdb/bolt v1.3.1
 	github.com/go-ping/ping v1.1.0
 	github.com/google/uuid v1.3.0

+ 0 - 2
go.sum

@@ -1,5 +1,3 @@
-github.com/bcurren/go-ssdp v0.0.0-20130927201504-ae8e7a0ef8a8 h1:a5CrzovDwGx3NE8+FXDkx7vyBo0EWeQ/JOKP1Q8VD6I=
-github.com/bcurren/go-ssdp v0.0.0-20130927201504-ae8e7a0ef8a8/go.mod h1:XW7y5ILmTgyAH5CEVZ7fPldvuDl1bYqQsqZrFM/LzKQ=
 github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=

+ 1 - 1
main.go

@@ -34,7 +34,7 @@ var (
 	name        = "Zoraxy"
 	version     = "2.11"
 	nodeUUID    = "generic"
-	development = false //Set this to false to use embedded web fs
+	development = true //Set this to false to use embedded web fs
 
 	/*
 		Binary Embedding File System

+ 68 - 0
mod/wakeonlan/wakeonlan.go

@@ -0,0 +1,68 @@
+package wakeonlan
+
+import (
+	"errors"
+	"net"
+	"time"
+)
+
+/*
+	Wake On Lan
+	Author: tobychui
+
+	This module send wake on LAN signal to a given MAC address
+	and do nothing else
+*/
+
+type magicPacket [102]byte
+
+func WakeTarget(macAddr string) error {
+	packet := magicPacket{}
+	mac, err := net.ParseMAC(macAddr)
+	if err != nil {
+		return err
+	}
+
+	if len(mac) != 6 {
+		return errors.New("invalid MAC address")
+	}
+
+	//Initialize the packet with all F
+	copy(packet[0:], []byte{255, 255, 255, 255, 255, 255})
+	offset := 6
+
+	for i := 0; i < 16; i++ {
+		copy(packet[offset:], mac)
+		offset += 6
+	}
+
+	//Most devices listen to either port 7 or 9, send to both of them
+	err = sendPacket("255.255.255.255:7", packet)
+	if err != nil {
+		return err
+	}
+
+	time.Sleep(30 * time.Millisecond)
+
+	err = sendPacket("255.255.255.255:9", packet)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func sendPacket(addr string, packet magicPacket) error {
+	conn, err := net.Dial("udp", addr)
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	_, err = conn.Write(packet[:])
+	return err
+}
+
+func IsValidMacAddress(macaddr string) bool {
+	_, err := net.ParseMAC(macaddr)
+	return err == nil
+}

+ 3 - 0
start.go

@@ -130,4 +130,7 @@ func startupSequence() {
 
 	//Create WebSSH Manager
 	webSshManager = sshprox.NewSSHProxyManager()
+
+	//Create WoL MAC storage table
+	sysdb.NewTable("wolmac")
 }

+ 234 - 1
web/components/networktools.html

@@ -51,6 +51,54 @@
         <div class="ui divider"></div>
         <h2>Wake On LAN</h2>
         <p>Wake up a remote server by WOL Magic Packet or an IoT device</p>
+        <div class="ui form">
+            <div class="two fields">
+                <div class="field">
+                  <input type="text" id="wol_servername" placeholder="Server Name">
+                </div>
+                <div class="field">
+                  <input type="text" id="wol_mac" placeholder="MAC Address">
+                </div>
+                <div class="field">
+                    <button class="ui basic right floated button" onclick="setWoLAddress();"><i class="ui blue add icon"></i> Add Address</button>
+                </div>
+            </div>
+        </div>
+        <div class="ui accordion">
+            <div class="title">
+                <i class="dropdown icon"></i>
+                Pick from mDNS scan results
+            </div>
+            <div class="content">
+                <div class="ui basic segment">
+                    <div class="ui selection fluid dropdown" id="mdnsWoL">
+                        <input type="hidden">
+                        <i class="dropdown icon"></i>
+                        <div class="default text">Select a server</div>
+                        <div class="menu" id="mdnsResultForWoL">
+                        </div>
+                    </div>
+                    <br>
+                    <button class="ui basic button" onclick="updateMDNSListForWoL();"><i class="ui green refresh icon"></i> Refresh mDNS Results</button>
+                    <button class="ui basic button" onclick="selectMdnsResultForWol();"><i class="ui blue add icon"></i> Add from mDNS</button>
+                </div>
+            </div>
+        </div>
+       
+        
+        <table class="ui celled unstackable table">
+            <thead>
+                <tr><th>Server Name</th>
+                <th>MAC Address</th>
+                <th>Action</th>
+            </tr></thead>
+            <tbody id="wolAddressList">
+                <tr>
+                    <td colspan="3"><i class="ui green circle checkmark"></i> No stored MAC address</td>
+                </tr>
+            </tbody>
+        </table>
+        <button class="ui basic button" onclick="listWoL();"><i class="ui green refresh icon"></i> Refresh</button>
     </div>
 
     <div class="ui bottom attached tab segment" data-tab="tab3">
@@ -71,7 +119,7 @@
 
 <script>
     // Activate the default tab
-
+    $('.ui.accordion').accordion();
     $('.menu .nettools.item').tab();
     $('.menu .nettools.item').addClass("activated");
 
@@ -198,6 +246,191 @@ function renderNICInfo(){
     });
 }
 renderNICInfo();
+
+/*
+    Wake On Lan functions
+*/
+
+const wake_on_lan_API = "/api/tools/wol";
+
+function selectMdnsResultForWol(){
+    let selectedText = $("#mdnsWoL").dropdown("get value");
+    let selectedWoL = JSON.parse(decodeURIComponent(selectedText));
+    $("#wol_servername").val(selectedWoL[0]);
+    $("#wol_mac").val(selectedWoL[1]);
+    setWoLAddress();
+    $("#mdnsWoL").dropdown("clear");
+}
+
+//Set Wake On Lan address
+function setWoLAddress() {
+    var name = $("#wol_servername").val().trim();
+    var mac = $("#wol_mac").val().trim();
+
+    if (name.length == 0){
+        $("#wol_servername").parent().addClass("error");
+    }else{
+        $("#wol_servername").parent().removeClass("error");
+    }
+
+    if (!isValidMacAddress(mac)){
+        $("#wol_mac").parent().addClass("error");
+        msgbox("Invalid MAC address given", false, 5000);
+        return
+    }else{
+        $("#wol_mac").parent().removeClass("error");
+    }
+
+    $.ajax({
+        url: wake_on_lan_API,
+        type: "POST",
+        data: {
+            set: mac,
+            name: name
+        },
+        success: function(result) {
+            msgbox(result.error || "WoL MAC Added", (result.error == undefined), (result.error == undefined)?3000:5000);
+            listWoL();
+
+            if (result.error == undefined){
+                $("#wol_servername").val("");
+                $("#wol_mac").val("");
+            }
+        },
+        error: function(error) {
+            console.error(error);
+        }
+    });
+}
+
+function delWoLAddr(mac, name) {
+    if (confirm(`Confirm remove WoL record for ${name} (${mac}) ?`)){
+        $.ajax({
+            url: wake_on_lan_API,
+            type: "POST",
+            data: {
+                del: mac.trim()
+            },
+            success: function(result) {
+                msgbox(result.error || "WoL MAC Removed", (result.error == undefined), (result.error == undefined)?3000:5000);
+                listWoL();
+            },
+            error: function(error) {
+                console.error(error);
+            }
+        });
+    }
+    
+}
+
+function wakeWoL(mac, object=undefined) {
+    if (object != undefined){
+        $(object).addClass("loading").addClass("disabled");
+    }
+    $.ajax({
+        url: wake_on_lan_API,
+        type: "POST",
+        data: {
+        wake: mac
+        },
+        success: function(result) {
+            if (result.error != undefined){
+                msgbox(result.error, false, 5000);
+            }else{
+                //Success?
+                setTimeout(function(){
+                    if (object != undefined){
+                        $(object).removeClass("loading").removeClass("disabled");
+                    }
+                }, 5000);
+            }
+            console.log(result);
+        },
+        error: function(error) {
+            console.error(error);
+        }
+    });
+}
+
+function listWoL(callback) {
+  $.ajax({
+    url: wake_on_lan_API,
+    type: "GET",
+    success: function(data) {
+        // Clear existing rows from the table
+        $("#wolAddressList").empty();
+
+        // Loop through data and create a new row for each object
+        for (var i = 0; i < data.length; i++) {
+            let thisServerName = data[i].ServerName;
+            let thisMacAddr = data[i].MacAddr
+            $("#wolAddressList").append(`
+                <tr class="wolmacentry" mac="${thisMacAddr}">
+                    <td>${thisServerName}</td>
+                    <td>${thisMacAddr}</td>
+                    <td>
+                        <button onclick="wakeWoL('${thisMacAddr}', this);" class="ui tiny basic button"><i class="green power icon"></i>Wake on LAN</button>
+                        <button onclick="delWoLAddr('${thisMacAddr}', '${thisServerName}');" class="ui tiny red basic icon button"><i class="red trash icon"></i></button>
+                    </td>
+
+                </tr>
+            `);
+        }
+
+        if (data.length == 0){
+            $("#wolAddressList").append(`<tr>
+                <td colspan="3"><i class="ui green circle check icon"></i> No stored MAC address for Wake On Lan (WoL)</td>
+            </tr>`);
+        }   
+
+        //Also update the MDNS list
+        updateMDNSListForWoL();
+
+        if (callback) {
+            callback(data);
+        }
+    },
+    error: function(error) {
+      console.error(error);
+    }
+  });
+}
+listWoL();
+
+function isValidMacAddress(macAddress) {
+  const macRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
+  return macRegex.test(macAddress);
+}
+
+function updateMDNSListForWoL(){
+    let alreadyAddedEntries = [];
+    $(".wolmacentry").each(function(){
+        let thisMac = $(this).attr("mac").trim();
+        if (isValidMacAddress(thisMac)){
+            console.log(thisMac);
+            alreadyAddedEntries.push(thisMac);
+        }
+    });
+    $("#mdnsResultForWoL").html("");
+    $.get("/api/mdns/list", function(data){
+        data.forEach(thisServer => {
+            if (thisServer.MacAddr.length > 0){
+                for (var i = 0; i < thisServer.MacAddr.length; i++){
+                    let thisMacAddr = thisServer.MacAddr[i];
+                    if (!isValidMacAddress(thisMacAddr) || alreadyAddedEntries.includes(thisMacAddr)){
+                        continue;
+                    }
+                    let encodedObject = encodeURIComponent(JSON.stringify([thisServer.HostName, thisMacAddr]));
+                    $("#mdnsResultForWoL").append(`<div class="item" data-value="${encodedObject}"><i class="server icon"></i> ${thisServer.HostName} (${thisMacAddr})</div>`);
+                }
+            }
+        });
+        $("#mdnsResultForWoL").parent().dropdown();
+    });
+}
+updateMDNSListForWoL();
+
+
 </script>
 
 

+ 99 - 11
helpers.go → wrappers.go

@@ -1,18 +1,35 @@
 package main
 
+/*
+	Wrappers.go
+
+	This script provide wrapping functions
+	for modules that do not provide
+	handler interface within the modules
+
+	--- NOTES ---
+	If your module have more than one layer
+	or require state keeping, please move
+	the abstraction up one layer into
+	your own module. Do not keep state on
+	the global scope other than single
+	Manager instance
+*/
+
 import (
 	"encoding/json"
 	"fmt"
 	"log"
 	"net/http"
+	"strings"
 	"time"
 
-	"github.com/bcurren/go-ssdp"
 	"imuslab.com/zoraxy/mod/dynamicproxy"
 	"imuslab.com/zoraxy/mod/ipscan"
 	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/uptime"
 	"imuslab.com/zoraxy/mod/utils"
+	"imuslab.com/zoraxy/mod/wakeonlan"
 )
 
 /*
@@ -196,16 +213,87 @@ func HandleIpScan(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-//Handle SSDP discovery
-func HandleSsdpDiscovery(w http.ResponseWriter, r *http.Request) {
-	responses, err := ssdp.Search("ssdp:discover", 10*time.Second)
-	log.Println(responses, err)
-	if err != nil {
-		return
-	}
+/*
+	Handle wake on LAN
+	Support following methods
+	/?set=xxx&name=xxx Record a new MAC address into the database
+	/?wake=xxx Wake a server given its MAC address
+	/?del=xxx Delete a server given its MAC address
+	/ Default: list all recorded WoL MAC address
+
+*/
+func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
+	set, _ := utils.PostPara(r, "set")
+	del, _ := utils.PostPara(r, "del")
+	wake, _ := utils.PostPara(r, "wake")
+	if set != "" {
+		//Get the name of the describing server
+		servername, err := utils.PostPara(r, "name")
+		if err != nil {
+			utils.SendErrorResponse(w, "invalid server name given")
+			return
+		}
+
+		//Check if the given mac address is a valid mac address
+		set = strings.TrimSpace(set)
+		if !wakeonlan.IsValidMacAddress(set) {
+			utils.SendErrorResponse(w, "invalid mac address given")
+			return
+		}
+
+		//Store this into the database
+		sysdb.Write("wolmac", set, servername)
+
+		utils.SendOK(w)
+	} else if wake != "" {
+		//Wake the target up by MAC address
+		if !wakeonlan.IsValidMacAddress(wake) {
+			utils.SendErrorResponse(w, "invalid mac address given")
+			return
+		}
+
+		log.Println("[WoL] Sending Wake on LAN magic packet to " + wake)
+		err := wakeonlan.WakeTarget(wake)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		utils.SendOK(w)
+	} else if del != "" {
+		if !wakeonlan.IsValidMacAddress(del) {
+			utils.SendErrorResponse(w, "invalid mac address given")
+			return
+		}
+
+		sysdb.Delete("wolmac", del)
+		utils.SendOK(w)
+	} else {
+		//List all the saved WoL MAC Address
+		entries, err := sysdb.ListTable("wolmac")
+		if err != nil {
+			utils.SendErrorResponse(w, "unknown error occured")
+			return
+		}
+
+		type MacAddrRecord struct {
+			ServerName string
+			MacAddr    string
+		}
+
+		results := []*MacAddrRecord{}
+		for _, keypairs := range entries {
+			macAddr := string(keypairs[0])
+			serverName := ""
+			json.Unmarshal(keypairs[1], &serverName)
 
-	for _, response := range responses {
-		// Do something with the response you discover
-		fmt.Println(response)
+			results = append(results, &MacAddrRecord{
+				ServerName: serverName,
+				MacAddr:    macAddr,
+			})
+		}
+
+		js, _ := json.Marshal(results)
+		utils.SendJSONResponse(w, string(js))
 	}
 }