"
+ },
+ {
+ "equals": "watched-changed",
+ "replacement": "
"
+ },
+ {
+ "equals": "new",
+ "replacement": "
"
+ },
+ {
+ "equals": "missing-in-last-scan",
+ "replacement": "
"
+ }
+ ],
+ "localized": ["name"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Status"
+ }
+ ]
+ }
+ ]
+}
diff --git a/front/plugins/dig_scan/digscan.py b/front/plugins/dig_scan/digscan.py
new file mode 100755
index 00000000..46266282
--- /dev/null
+++ b/front/plugins/dig_scan/digscan.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+import os
+import pathlib
+import sys
+import json
+import sqlite3
+import subprocess
+
+# Define the installation path and extend the system path for plugin imports
+INSTALL_PATH = "/app"
+sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
+
+from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
+from plugin_utils import get_plugins_configs
+from logger import mylog, Logger
+from const import pluginsPath, fullDbPath, logPath
+from helper import timeNowTZ, get_setting_value
+from messaging.in_app import write_notification
+from database import DB
+from models.device_instance import DeviceInstance
+import conf
+from pytz import timezone
+
+# Make sure the TIMEZONE for logging is correct
+conf.tz = timezone(get_setting_value('TIMEZONE'))
+
+# Make sure log level is initialized correctly
+Logger(get_setting_value('LOG_LEVEL'))
+
+pluginName = 'DIGSCAN'
+
+# Define the current path and log file paths
+LOG_PATH = logPath + '/plugins'
+LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
+RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
+
+# Initialize the Plugin obj output file
+plugin_objects = Plugin_Objects(RESULT_FILE)
+
+
+def main():
+ mylog('verbose', [f'[{pluginName}] In script'])
+
+ timeout = get_setting_value('DIGSCAN_RUN_TIMEOUT')
+
+ # Create a database connection
+ db = DB() # instance of class DB
+ db.open()
+
+ # Initialize the Plugin obj output file
+ plugin_objects = Plugin_Objects(RESULT_FILE)
+
+ # Create a DeviceInstance instance
+ device_handler = DeviceInstance(db)
+
+ # Retrieve devices
+ if get_setting_value("REFRESH_FQDN"):
+ devices = device_handler.getUnknown()
+ else:
+ devices = device_handler.getAll()
+
+ mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
+
+ # TEST - below is a WINDOWS host IP
+ # execute_name_lookup('192.168.1.121', timeout)
+
+ for device in devices:
+ domain_name, dns_server = execute_name_lookup(device['devLastIP'], timeout)
+
+ if domain_name != '':
+ plugin_objects.add_object(
+ # "MAC", "IP", "Server", "Name"
+ primaryId = device['devMac'],
+ secondaryId = device['devLastIP'],
+ watched1 = dns_server,
+ watched2 = domain_name,
+ watched3 = '',
+ watched4 = '',
+ extra = '',
+ foreignKey = device['devMac'])
+
+ plugin_objects.write_result_file()
+
+
+ mylog('verbose', [f'[{pluginName}] Script finished'])
+
+ return 0
+
+#===============================================================================
+# Execute scan
+#===============================================================================
+def execute_name_lookup (ip, timeout):
+ """
+ Execute the DIG command on IP.
+ """
+
+ args = ['dig', '+short', '-x', ip]
+
+ # Execute command
+ output = ""
+
+ try:
+ mylog('verbose', [f'[{pluginName}] DEBUG CMD :', args])
+
+ # try runnning a subprocess with a forced (timeout) in case the subprocess hangs
+ output = subprocess.check_output (args, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeout), text=True).strip()
+
+ mylog('verbose', [f'[{pluginName}] DEBUG OUTPUT : {output}'])
+
+ domain_name = output
+ dns_server = ''
+
+ mylog('verbose', [f'[{pluginName}] Domain Name: {domain_name}'])
+
+ return domain_name, dns_server
+
+ except subprocess.CalledProcessError as e:
+ mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}'])
+
+ except subprocess.TimeoutExpired as timeErr:
+ mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
+
+ if output == "": # check if the subprocess failed
+ mylog('verbose', [f'[{pluginName}] Scan: FAIL - check logs'])
+ else:
+ mylog('verbose', [f'[{pluginName}] Scan: SUCCESS'])
+
+ return '', ''
+
+if __name__ == '__main__':
+ main()
+
diff --git a/front/plugins/nbtscan_scan/config.json b/front/plugins/nbtscan_scan/config.json
index 51fd3a13..50691f12 100755
--- a/front/plugins/nbtscan_scan/config.json
+++ b/front/plugins/nbtscan_scan/config.json
@@ -52,7 +52,7 @@
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
- "default_value": "disabled",
+ "default_value": "before_name_updates",
"options": [
"disabled",
"before_name_updates",
diff --git a/front/plugins/nbtscan_scan/nbtscan.py b/front/plugins/nbtscan_scan/nbtscan.py
index 60261a46..003aad33 100755
--- a/front/plugins/nbtscan_scan/nbtscan.py
+++ b/front/plugins/nbtscan_scan/nbtscan.py
@@ -57,14 +57,17 @@ def main():
device_handler = DeviceInstance(db)
# Retrieve devices
- unknown_devices = device_handler.getUnknown()
+ if get_setting_value("REFRESH_FQDN"):
+ devices = device_handler.getUnknown()
+ else:
+ devices = device_handler.getAll()
- mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}'])
+ mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
# TEST - below is a WINDOWS host IP
# execute_name_lookup('192.168.1.121', timeout)
- for device in unknown_devices:
+ for device in devices:
domain_name, dns_server = execute_name_lookup(device['devLastIP'], timeout)
if domain_name != '':
diff --git a/front/plugins/newdev_template/config.json b/front/plugins/newdev_template/config.json
index c0a2f609..93162224 100755
--- a/front/plugins/newdev_template/config.json
+++ b/front/plugins/newdev_template/config.json
@@ -1629,6 +1629,42 @@
"string": "Custom device properties to store additional data or to perform an action on the device. Check the
documentation on Custom Properties for additional details."
}
]
+ },
+ {
+ "function": "devFQDN",
+ "type": {
+ "dataType": "string",
+ "elements": [
+ {
+ "elementType": "input",
+ "elementOptions": [
+ {
+ "readonly": "true"
+ }
+ ],
+ "transformers": []
+ }
+ ]
+ },
+ "maxLength": 50,
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name",
+ "description"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "FQDN"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Fully Qualified Domain Name - Autodetected and Uneditable. Can be auto-refreshed by enabling the
REFRESH_FQDN setting."
+ }
+ ]
}
],
"required": [],
diff --git a/front/plugins/nslookup_scan/nslookup.py b/front/plugins/nslookup_scan/nslookup.py
index 3ec2fe2e..dc403250 100755
--- a/front/plugins/nslookup_scan/nslookup.py
+++ b/front/plugins/nslookup_scan/nslookup.py
@@ -59,11 +59,17 @@ def main():
device_handler = DeviceInstance(db)
# Retrieve devices
- unknown_devices = device_handler.getUnknown()
+ if get_setting_value("REFRESH_FQDN"):
+ devices = device_handler.getUnknown()
+ else:
+ devices = device_handler.getAll()
- mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}'])
+ mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
+
+ # TEST - below is a WINDOWS host IP
+ # execute_name_lookup('192.168.1.121', timeout)
- for device in unknown_devices:
+ for device in devices:
domain_name, dns_server = execute_nslookup(device['devLastIP'], timeout)
if domain_name != '':
diff --git a/front/plugins/ui_settings/config.json b/front/plugins/ui_settings/config.json
index c46fa376..ce5e21c9 100755
--- a/front/plugins/ui_settings/config.json
+++ b/front/plugins/ui_settings/config.json
@@ -377,7 +377,8 @@
"Device_TableHead_SourcePlugin",
"Device_TableHead_PresentLastScan",
"Device_TableHead_AlertDown",
- "Device_TableHead_CustomProps"
+ "Device_TableHead_CustomProps",
+ "Device_TableHead_FQDN"
],
"localized": ["name", "description"],
"name": [
diff --git a/front/workflowsCore.php b/front/workflowsCore.php
index b7aeed26..5cba7801 100755
--- a/front/workflowsCore.php
+++ b/front/workflowsCore.php
@@ -44,7 +44,7 @@ let fieldOptions = [
"devLastIP", "devStaticIP", "devScan", "devLogEvents", "devAlertEvents",
"devAlertDown", "devSkipRepeated", "devLastNotification", "devPresentLastScan",
"devIsNew", "devLocation", "devIsArchived", "devParentMAC", "devParentPort",
- "devIcon", "devSite", "devSSID", "devSyncHubNode", "devSourcePlugin"
+ "devIcon", "devSite", "devSSID", "devSyncHubNode", "devSourcePlugin", "devFQDN"
];
let triggerTypes = [
diff --git a/scripts/db_empty/README.md b/scripts/db_empty/README.md
new file mode 100755
index 00000000..4656d22b
--- /dev/null
+++ b/scripts/db_empty/README.md
@@ -0,0 +1,19 @@
+# Overview
+
+A script for deleting all data from the database.
+
+# Usage
+
+1. **Run the Script**
+
+`python ./db_empty.py`
+
+### Other info
+
+- Version: 1.0
+- Release Date: 01-Jun-2025
+- Author: [jokob-sk](https://github.com/jokob-sk)
+
+
+> [!NOTE]
+> This is a community supplied script and not maintained.
\ No newline at end of file
diff --git a/scripts/db_empty/db_empty.py b/scripts/db_empty/db_empty.py
new file mode 100755
index 00000000..95ef6da0
--- /dev/null
+++ b/scripts/db_empty/db_empty.py
@@ -0,0 +1,26 @@
+import sqlite3
+
+# Connect to the database
+conn = sqlite3.connect("/app/db/app.db")
+cursor = conn.cursor()
+
+# Get the names of all tables (excluding SQLite internal tables)
+cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
+tables = cursor.fetchall()
+
+# Disable foreign key constraints temporarily
+cursor.execute("PRAGMA foreign_keys = OFF;")
+
+# Delete all rows from each table
+for (table_name,) in tables:
+ cursor.execute(f"DELETE FROM {table_name};")
+
+# Commit changes and re-enable foreign keys
+conn.commit()
+cursor.execute("PRAGMA foreign_keys = ON;")
+
+# Vacuum to shrink database file
+cursor.execute("VACUUM;")
+
+# Close connection
+conn.close()
diff --git a/server/__main__.py b/server/__main__.py
index 653e155b..74d10761 100755
--- a/server/__main__.py
+++ b/server/__main__.py
@@ -153,7 +153,7 @@ def main ():
# Resolve devices names
mylog('debug','[Main] Resolve devices names')
- update_devices_names(db)
+ update_devices_names(db)
# Check if new devices found
sql.execute (sql_new_devices)
diff --git a/server/const.py b/server/const.py
index 9663c7ec..8ad0ab11 100755
--- a/server/const.py
+++ b/server/const.py
@@ -60,6 +60,7 @@ sql_devices_all = """
IFNULL(devSyncHubNode, '') AS devSyncHubNode,
IFNULL(devSourcePlugin, '') AS devSourcePlugin,
IFNULL(devCustomProps, '') AS devCustomProps,
+ IFNULL(devFQDN, '') AS devFQDN,
CASE
WHEN devIsNew = 1 THEN 'New'
WHEN devPresentLastScan = 1 THEN 'On-line'
diff --git a/server/database.py b/server/database.py
index 0db5a05b..36576bbe 100755
--- a/server/database.py
+++ b/server/database.py
@@ -80,370 +80,10 @@ class DB():
"""
Check the current tables in the DB and upgrade them if neccessary
"""
-
- self.sql.execute("""
- CREATE TABLE IF NOT EXISTS "Online_History" (
- "Index" INTEGER,
- "Scan_Date" TEXT,
- "Online_Devices" INTEGER,
- "Down_Devices" INTEGER,
- "All_Devices" INTEGER,
- "Archived_Devices" INTEGER,
- "Offline_Devices" INTEGER,
- PRIMARY KEY("Index" AUTOINCREMENT)
- );
- """)
- # -------------------------------------------------------------------
- # DevicesNew - cleanup after 6/6/2025 - need to update also DB in the source code!
-
- # check if migration already done based on devMac
- devMac_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devMac'
- """).fetchone()[0] == 0
-
- if devMac_missing:
-
- # -------------------------------------------------------------------------
- # Alter Devices table
- # -------------------------------------------------------------------------
- # dev_Network_Node_MAC_ADDR column
- dev_Network_Node_MAC_ADDR_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_MAC_ADDR'
- """).fetchone()[0] == 0
-
- if dev_Network_Node_MAC_ADDR_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_Network_Node_MAC_ADDR to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_Network_Node_MAC_ADDR" TEXT
- """)
-
- # dev_Network_Node_port column
- dev_Network_Node_port_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_port'
- """).fetchone()[0] == 0
-
- if dev_Network_Node_port_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_Network_Node_port to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_Network_Node_port" INTEGER
- """)
-
- # dev_Icon column
- dev_Icon_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Icon'
- """).fetchone()[0] == 0
-
- if dev_Icon_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_Icon to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_Icon" TEXT
- """)
-
- # dev_GUID column
- dev_GUID_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_GUID'
- """).fetchone()[0] == 0
-
- if dev_GUID_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_GUID to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_GUID" TEXT
- """)
-
- # dev_NetworkSite column
- dev_NetworkSite_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_NetworkSite'
- """).fetchone()[0] == 0
-
- if dev_NetworkSite_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_NetworkSite to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_NetworkSite" TEXT
- """)
-
- # dev_SSID column
- dev_SSID_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SSID'
- """).fetchone()[0] == 0
-
- if dev_SSID_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_SSID to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_SSID" TEXT
- """)
-
- # SQL query to update missing dev_GUID
- self.sql.execute(f'''
- UPDATE Devices
- SET dev_GUID = {sql_generateGuid}
- WHERE dev_GUID IS NULL
- ''')
-
- # dev_SyncHubNodeName column
- dev_SyncHubNodeName_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SyncHubNodeName'
- """).fetchone()[0] == 0
-
- if dev_SyncHubNodeName_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_SyncHubNodeName to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_SyncHubNodeName" TEXT
- """)
-
- # dev_SourcePlugin column
- dev_SourcePlugin_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SourcePlugin'
- """).fetchone()[0] == 0
-
- if dev_SourcePlugin_missing :
- mylog('verbose', ["[upgradeDB] Adding dev_SourcePlugin to the Devices table"])
- self.sql.execute("""
- ALTER TABLE "Devices" ADD "dev_SourcePlugin" TEXT
- """)
-
- # SQL to create Devices table with indexes
- sql_create_devices_new_tmp = """
- CREATE TABLE IF NOT EXISTS Devices_tmp (
- devMac STRING (50) PRIMARY KEY NOT NULL COLLATE NOCASE,
- devName STRING (50) NOT NULL DEFAULT "(unknown)",
- devOwner STRING (30) DEFAULT "(unknown)" NOT NULL,
- devType STRING (30),
- devVendor STRING (250),
- devFavorite BOOLEAN CHECK (devFavorite IN (0, 1)) DEFAULT (0) NOT NULL,
- devGroup STRING (10),
- devComments TEXT,
- devFirstConnection DATETIME NOT NULL,
- devLastConnection DATETIME NOT NULL,
- devLastIP STRING (50) NOT NULL COLLATE NOCASE,
- devStaticIP BOOLEAN DEFAULT (0) NOT NULL CHECK (devStaticIP IN (0, 1)),
- devScan INTEGER DEFAULT (1) NOT NULL,
- devLogEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devLogEvents IN (0, 1)),
- devAlertEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devAlertEvents IN (0, 1)),
- devAlertDown BOOLEAN NOT NULL DEFAULT (0) CHECK (devAlertDown IN (0, 1)),
- devSkipRepeated INTEGER DEFAULT 0 NOT NULL,
- devLastNotification DATETIME,
- devPresentLastScan BOOLEAN NOT NULL DEFAULT (0) CHECK (devPresentLastScan IN (0, 1)),
- devIsNew BOOLEAN NOT NULL DEFAULT (1) CHECK (devIsNew IN (0, 1)),
- devLocation STRING (250) COLLATE NOCASE,
- devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
- devParentMAC TEXT,
- devParentPort INTEGER,
- devIcon TEXT,
- devGUID TEXT,
- devSite TEXT,
- devSSID TEXT,
- devSyncHubNode TEXT,
- devSourcePlugin TEXT
- );
-
- CREATE INDEX IF NOT EXISTS IDX_dev_PresentLastScan ON Devices_tmp (devPresentLastScan);
- CREATE INDEX IF NOT EXISTS IDX_dev_FirstConnection ON Devices_tmp (devFirstConnection);
- CREATE INDEX IF NOT EXISTS IDX_dev_AlertDeviceDown ON Devices_tmp (devAlertDown);
- CREATE INDEX IF NOT EXISTS IDX_dev_StaticIP ON Devices_tmp (devStaticIP);
- CREATE INDEX IF NOT EXISTS IDX_dev_ScanCycle ON Devices_tmp (devScan);
- CREATE INDEX IF NOT EXISTS IDX_dev_Favorite ON Devices_tmp (devFavorite);
- CREATE INDEX IF NOT EXISTS IDX_dev_LastIP ON Devices_tmp (devLastIP);
- CREATE INDEX IF NOT EXISTS IDX_dev_NewDevice ON Devices_tmp (devIsNew);
- CREATE INDEX IF NOT EXISTS IDX_dev_Archived ON Devices_tmp (devIsArchived);
- """
-
- # Execute the creation of the Devices table and indexes
- self.sql.executescript(sql_create_devices_new_tmp)
-
-
- # copy over data
- sql_copy_from_devices = """
- INSERT OR IGNORE INTO Devices_tmp (
- devMac,
- devName,
- devOwner,
- devType,
- devVendor,
- devFavorite,
- devGroup,
- devComments,
- devFirstConnection,
- devLastConnection,
- devLastIP,
- devStaticIP,
- devScan,
- devLogEvents,
- devAlertEvents,
- devAlertDown,
- devSkipRepeated,
- devLastNotification,
- devPresentLastScan,
- devIsNew,
- devLocation,
- devIsArchived,
- devParentMAC,
- devParentPort,
- devIcon,
- devGUID,
- devSite,
- devSSID,
- devSyncHubNode,
- devSourcePlugin
- )
- SELECT
- dev_MAC AS devMac,
- dev_Name AS devName,
- dev_Owner AS devOwner,
- dev_DeviceType AS devType,
- dev_Vendor AS devVendor,
- dev_Favorite AS devFavorite,
- dev_Group AS devGroup,
- dev_Comments AS devComments,
- dev_FirstConnection AS devFirstConnection,
- dev_LastConnection AS devLastConnection,
- dev_LastIP AS devLastIP,
- dev_StaticIP AS devStaticIP,
- dev_ScanCycle AS devScan,
- dev_LogEvents AS devLogEvents,
- dev_AlertEvents AS devAlertEvents,
- dev_AlertDeviceDown AS devAlertDown,
- dev_SkipRepeated AS devSkipRepeated,
- dev_LastNotification AS devLastNotification,
- dev_PresentLastScan AS devPresentLastScan,
- dev_NewDevice AS devIsNew,
- dev_Location AS devLocation,
- dev_Archived AS devIsArchived,
- dev_Network_Node_MAC_ADDR AS devParentMAC,
- dev_Network_Node_port AS devParentPort,
- dev_Icon AS devIcon,
- dev_GUID AS devGUID,
- dev_NetworkSite AS devSite,
- dev_SSID AS devSSID,
- dev_SyncHubNodeName AS devSyncHubNode,
- dev_SourcePlugin AS devSourcePlugin
- FROM Devices;
- """
-
- self.sql.execute(sql_copy_from_devices)
-
-
- self.sql.execute(""" DROP TABLE Devices;""")
- # SQL to create Devices table with indexes
- sql_create_devices_new = """
- CREATE TABLE IF NOT EXISTS Devices (
- devMac STRING (50) PRIMARY KEY NOT NULL COLLATE NOCASE,
- devName STRING (50) NOT NULL DEFAULT "(unknown)",
- devOwner STRING (30) DEFAULT "(unknown)" NOT NULL,
- devType STRING (30),
- devVendor STRING (250),
- devFavorite BOOLEAN CHECK (devFavorite IN (0, 1)) DEFAULT (0) NOT NULL,
- devGroup STRING (10),
- devComments TEXT,
- devFirstConnection DATETIME NOT NULL,
- devLastConnection DATETIME NOT NULL,
- devLastIP STRING (50) NOT NULL COLLATE NOCASE,
- devStaticIP BOOLEAN DEFAULT (0) NOT NULL CHECK (devStaticIP IN (0, 1)),
- devScan INTEGER DEFAULT (1) NOT NULL,
- devLogEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devLogEvents IN (0, 1)),
- devAlertEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devAlertEvents IN (0, 1)),
- devAlertDown BOOLEAN NOT NULL DEFAULT (0) CHECK (devAlertDown IN (0, 1)),
- devSkipRepeated INTEGER DEFAULT 0 NOT NULL,
- devLastNotification DATETIME,
- devPresentLastScan BOOLEAN NOT NULL DEFAULT (0) CHECK (devPresentLastScan IN (0, 1)),
- devIsNew BOOLEAN NOT NULL DEFAULT (1) CHECK (devIsNew IN (0, 1)),
- devLocation STRING (250) COLLATE NOCASE,
- devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
- devParentMAC TEXT,
- devParentPort INTEGER,
- devIcon TEXT,
- devGUID TEXT,
- devSite TEXT,
- devSSID TEXT,
- devSyncHubNode TEXT,
- devSourcePlugin TEXT
- );
-
- CREATE INDEX IF NOT EXISTS IDX_dev_PresentLastScan ON Devices (devPresentLastScan);
- CREATE INDEX IF NOT EXISTS IDX_dev_FirstConnection ON Devices (devFirstConnection);
- CREATE INDEX IF NOT EXISTS IDX_dev_AlertDeviceDown ON Devices (devAlertDown);
- CREATE INDEX IF NOT EXISTS IDX_dev_StaticIP ON Devices (devStaticIP);
- CREATE INDEX IF NOT EXISTS IDX_dev_ScanCycle ON Devices (devScan);
- CREATE INDEX IF NOT EXISTS IDX_dev_Favorite ON Devices (devFavorite);
- CREATE INDEX IF NOT EXISTS IDX_dev_LastIP ON Devices (devLastIP);
- CREATE INDEX IF NOT EXISTS IDX_dev_NewDevice ON Devices (devIsNew);
- CREATE INDEX IF NOT EXISTS IDX_dev_Archived ON Devices (devIsArchived);
- """
-
- # Execute the creation of the Devices table and indexes
- self.sql.executescript(sql_create_devices_new)
-
- # copy over data
- sql_copy_from_devices_tmp = """
- INSERT OR IGNORE INTO Devices (
- devMac,
- devName,
- devOwner,
- devType,
- devVendor,
- devFavorite,
- devGroup,
- devComments,
- devFirstConnection,
- devLastConnection,
- devLastIP,
- devStaticIP,
- devScan,
- devLogEvents,
- devAlertEvents,
- devAlertDown,
- devSkipRepeated,
- devLastNotification,
- devPresentLastScan,
- devIsNew,
- devLocation,
- devIsArchived,
- devParentMAC,
- devParentPort,
- devIcon,
- devGUID,
- devSite,
- devSSID,
- devSyncHubNode,
- devSourcePlugin
- )
- SELECT
- devMac,
- devName,
- devOwner,
- devType,
- devVendor,
- devFavorite,
- devGroup,
- devComments,
- devFirstConnection,
- devLastConnection,
- devLastIP,
- devStaticIP,
- devScan,
- devLogEvents,
- devAlertEvents,
- devAlertDown,
- devSkipRepeated,
- devLastNotification,
- devPresentLastScan,
- devIsNew,
- devLocation,
- devIsArchived,
- devParentMAC,
- devParentPort,
- devIcon,
- devGUID,
- devSite,
- devSSID,
- devSyncHubNode,
- devSourcePlugin
- FROM Devices_tmp;
- """
-
- self.sql.execute(sql_copy_from_devices_tmp)
- self.sql.execute(""" DROP TABLE Devices_tmp;""")
-
+ # -------------------------------------------------------------------------
+ # Alter Devices table
+ # -------------------------------------------------------------------------
# VIEWS
@@ -477,17 +117,19 @@ class DB():
# add fields if missing
-
- # devCustomProps column
- devCustomProps_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devCustomProps'
+
+ # devFQDN missing?
+ devFQDN_missing = self.sql.execute ("""
+ SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devFQDN'
""").fetchone()[0] == 0
+
+ if devFQDN_missing:
- if devCustomProps_missing :
- mylog('verbose', ["[upgradeDB] Adding devCustomProps to the Devices table"])
+ mylog('verbose', ["[upgradeDB] Adding devFQDN to the Devices table"])
self.sql.execute("""
- ALTER TABLE "Devices" ADD "devCustomProps" TEXT
+ ALTER TABLE "Devices" ADD "devFQDN" TEXT
""")
+
# -------------------------------------------------------------------------
# Settings table setup
@@ -564,48 +206,6 @@ class DB():
); """
self.sql.execute(sql_Plugins_Objects)
- # -----------------------------------------
- # REMOVE after 6/6/2025 - START
- # -----------------------------------------
- # syncHubNodeName column
- plug_SyncHubNodeName_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='SyncHubNodeName'
- """).fetchone()[0] == 0
-
- if plug_SyncHubNodeName_missing :
- mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_Objects table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_Objects" ADD "SyncHubNodeName" TEXT
- """)
-
- # helper columns HelpVal1-4
- plug_HelpValues_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='HelpVal1'
- """).fetchone()[0] == 0
-
- if plug_HelpValues_missing :
- mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_Objects table"])
- self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal1" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal2" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal3" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal4" TEXT')
-
- # plug_ObjectGUID_missing column
- plug_ObjectGUID_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='ObjectGUID'
- """).fetchone()[0] == 0
-
- if plug_ObjectGUID_missing :
- mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_Objects table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_Objects" ADD "ObjectGUID" TEXT
- """)
-
-
- # -----------------------------------------
- # REMOVE after 6/6/2025 - END
- # -----------------------------------------
-
# Plugin execution results
sql_Plugins_Events = """ CREATE TABLE IF NOT EXISTS Plugins_Events(
"Index" INTEGER,
@@ -631,49 +231,6 @@ class DB():
); """
self.sql.execute(sql_Plugins_Events)
- # -----------------------------------------
- # REMOVE after 6/6/2025 - START
- # -----------------------------------------
-
- # syncHubNodeName column
- plug_SyncHubNodeName_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='SyncHubNodeName'
- """).fetchone()[0] == 0
-
- if plug_SyncHubNodeName_missing :
- mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_Events table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_Events" ADD "SyncHubNodeName" TEXT
- """)
-
- # helper columns HelpVal1-4
- plug_HelpValues_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='HelpVal1'
- """).fetchone()[0] == 0
-
- if plug_HelpValues_missing :
- mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_Events table"])
- self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal1" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal2" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal3" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal4" TEXT')
-
- # plug_ObjectGUID_missing column
- plug_ObjectGUID_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='ObjectGUID'
- """).fetchone()[0] == 0
-
- if plug_ObjectGUID_missing :
- mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_Events table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_Events" ADD "ObjectGUID" TEXT
- """)
-
- # -----------------------------------------
- # REMOVE after 6/6/2025 - END
- # -----------------------------------------
-
-
# Plugin execution history
sql_Plugins_History = """ CREATE TABLE IF NOT EXISTS Plugins_History(
"Index" INTEGER,
@@ -699,48 +256,6 @@ class DB():
); """
self.sql.execute(sql_Plugins_History)
- # -----------------------------------------
- # REMOVE after 6/6/2025 - START
- # -----------------------------------------
-
- # syncHubNodeName column
- plug_SyncHubNodeName_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='SyncHubNodeName'
- """).fetchone()[0] == 0
-
- if plug_SyncHubNodeName_missing :
- mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_History table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_History" ADD "SyncHubNodeName" TEXT
- """)
-
- # helper columns HelpVal1-4
- plug_HelpValues_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='HelpVal1'
- """).fetchone()[0] == 0
-
- if plug_HelpValues_missing :
- mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_History table"])
- self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal1" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal2" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal3" TEXT')
- self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal4" TEXT')
-
-
- # plug_ObjectGUID_missing column
- plug_ObjectGUID_missing = self.sql.execute ("""
- SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='ObjectGUID'
- """).fetchone()[0] == 0
-
- if plug_ObjectGUID_missing :
- mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_History table"])
- self.sql.execute("""
- ALTER TABLE "Plugins_History" ADD "ObjectGUID" TEXT
- """)
-
- # -----------------------------------------
- # REMOVE after 6/6/2025 - END
- # -----------------------------------------
# -------------------------------------------------------------------------
# Plugins_Language_Strings table setup
@@ -845,21 +360,6 @@ class DB():
# Init the AppEvent database table
AppEvent_obj(self)
- # -------------------------------------------------------------------------
- # DELETING OBSOLETE TABLES - to remove with updated db file after 9/9/2024
- # -------------------------------------------------------------------------
-
- # Deletes obsolete ScanCycles
- self.sql.execute(""" DROP TABLE IF EXISTS ScanCycles;""")
- self.sql.execute(""" DROP TABLE IF EXISTS DHCP_Leases;""")
- self.sql.execute(""" DROP TABLE IF EXISTS PiHole_Network;""")
-
- self.commitDB()
-
- # -------------------------------------------------------------------------
- # DELETING OBSOLETE TABLES - to remove with updated db file after 9/9/2024
- # -------------------------------------------------------------------------
-
#-------------------------------------------------------------------------------
def get_table_as_json(self, sqlQuery):
diff --git a/server/graphql_server/graphql_schema.py b/server/graphql_server/graphql_schema.py
index 83bff5a6..d5c93a6e 100755
--- a/server/graphql_server/graphql_schema.py
+++ b/server/graphql_server/graphql_schema.py
@@ -73,6 +73,7 @@ class Device(ObjectType):
devParentChildrenCount = Int()
devIpLong = Int()
devFilterStatus = String()
+ devFQDN = String()
class DeviceResult(ObjectType):
@@ -180,7 +181,7 @@ class Query(ObjectType):
searchable_fields = [
"devName", "devMac", "devOwner", "devType", "devVendor", "devLastIP",
"devGroup", "devComments", "devLocation", "devStatus",
- "devSSID", "devSite", "devSourcePlugin", "devSyncHubNode"
+ "devSSID", "devSite", "devSourcePlugin", "devSyncHubNode", "devFQDN"
]
search_term = options.search.lower()
diff --git a/server/helper.py b/server/helper.py
index 6b402b2b..e8c570a9 100755
--- a/server/helper.py
+++ b/server/helper.py
@@ -18,6 +18,7 @@ import hashlib
import random
import string
import ipaddress
+import dns.resolver
import conf
from const import *
@@ -427,211 +428,6 @@ def check_IP_format (pIP):
# Return IP
return IP.group(0)
-#-------------------------------------------------------------------------------
-def get_device_name_mdns(db, pMAC, pIP):
-
- nameNotFound = "(name not found)"
-
- sql = db.sql
-
- name = nameNotFound
-
- # get names from the AVAHISCAN plugin entries vased on MAC
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'AVAHISCAN' AND
- Object_PrimaryID = '{pMAC}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], False)
-
- return name
-
- # get names from the AVAHISCAN plugin entries based on IP
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'AVAHISCAN' AND
- Object_SecondaryID = '{pIP}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], True)
-
- return name
-
- return name
-
-#-------------------------------------------------------------------------------
-def get_device_name_nslookup(db, pMAC, pIP):
-
- nameNotFound = "(name not found)"
-
- sql = db.sql
-
- name = nameNotFound
-
- # get names from the NSLOOKUP plugin entries vased on MAC
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'NSLOOKUP' AND
- Object_PrimaryID = '{pMAC}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], False)
-
- return name
-
- # get names from the NSLOOKUP plugin entries based on IP
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'NSLOOKUP' AND
- Object_SecondaryID = '{pIP}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], True)
-
- return name
-
- return name
-
-#-------------------------------------------------------------------------------
-def get_device_name_nbtlookup(db, pMAC, pIP):
-
- nameNotFound = "(name not found)"
-
- sql = db.sql
-
- name = nameNotFound
-
- # get names from the NBTSCAN plugin entries vased on MAC
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'NBTSCAN' AND
- Object_PrimaryID = '{pMAC}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], False)
-
- return name
-
- # get names from the NSLOOKUP plugin entries based on IP
- sql.execute(
- f"""
- SELECT Watched_Value2 FROM Plugins_Objects
- WHERE
- Plugin = 'NBTSCAN' AND
- Object_SecondaryID = '{pIP}'
- """
- )
- nameEntry = sql.fetchall()
- db.commitDB()
-
- if len(nameEntry) != 0:
- name = cleanDeviceName(nameEntry[0][0], True)
-
- return name
-
- return name
-
-
-#-------------------------------------------------------------------------------
-def resolve_device_name_dig (pMAC, pIP):
-
- nameNotFound = "(name not found)"
-
- dig_args = ['dig', '+short', '-x', pIP]
-
- # Execute command
- try:
- # try runnning a subprocess
- newName = subprocess.check_output (dig_args, universal_newlines=True)
-
- # Check returns
- newName = newName.strip()
-
- if len(newName) == 0 :
- return nameNotFound
-
- # Cleanup
- newName = cleanDeviceName(newName, True)
-
- if newName == "" or len(newName) == 0 or newName == '-1' or newName == -1 or "communications error" in newName or 'malformed message packet' in newName :
- return nameNotFound
-
- # all checks passed
- mylog('debug', [f'[resolve_device_name_dig] Found a new name: "{newName}"'])
-
- return newName
-
- except subprocess.CalledProcessError as e:
- # An error occured, handle it
- mylog('none', ['[resolve_device_name_dig] ⚠ ERROR: ', e.output])
- # newName = "Error - check logs"
- return nameNotFound
-
-
-#-------------------------------------------------------------------------------
-# DNS record (Name resolution) cleanup methods
-#-------------------------------------------------------------------------------
-
-import dns.resolver
-
-def cleanDeviceName(str, match_IP):
-
- mylog('debug', ["[cleanDeviceName] input: " + str])
-
- # add matching info
- if match_IP:
- str = str + " (IP match)"
-
- # Applying cleanup REGEXEs
- mylog('debug', ["[Name cleanup] Using old cleanDeviceName(" + str + ")"])
-
- regexes = get_setting_value('NEWDEV_NAME_CLEANUP_REGEX')
-
- for rgx in regexes:
- mylog('trace', ["[cleanDeviceName] applying regex : " + rgx])
- mylog('trace', ["[cleanDeviceName] name before regex : " + str])
- str = re.sub(rgx, "", str)
- mylog('trace', ["[cleanDeviceName] name after regex : " + str])
-
- # str = re.sub(r'\.\b', '', str) # trailing dot after words
- str = re.sub(r'\.$', '', str) # trailing dot at the end of the string
- str = str.replace(". (IP match)", " (IP match)") # Remove dot if (IP match) is added
-
- mylog('debug', ["[cleanDeviceName] output: " + str])
-
- return str
-
#-------------------------------------------------------------------------------
# String manipulation methods
#-------------------------------------------------------------------------------
diff --git a/server/initialise.py b/server/initialise.py
index fac42004..3a321f02 100755
--- a/server/initialise.py
+++ b/server/initialise.py
@@ -168,6 +168,7 @@ def importConfigs (db, all_plugins):
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.HRS_TO_KEEP_OFFDEV = ccd('HRS_TO_KEEP_OFFDEV', 0 , c_d, 'Keep offline devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.CLEAR_NEW_FLAG = ccd('CLEAR_NEW_FLAG', 0 , c_d, 'Clear new flag', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
+ conf.REFRESH_FQDN = ccd('REFRESH_FQDN', False , c_d, 'Refresh FQDN', """{"dataType": "boolean","elements": [{"elementType": "input","elementOptions": [{ "type": "checkbox" }],"transformers": []}]}""", '[]', 'General')
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE devPresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General')
@@ -445,46 +446,6 @@ replacements = {
r'\bREPORT_TO\b': 'SMTP_REPORT_TO',
r'\bSYNC_api_token\b': 'API_TOKEN',
r'\bAPI_TOKEN=\'\'': f'API_TOKEN=\'t_{generate_random_string(20)}\'',
- r'\bREPORT_FROM\b': 'SMTP_REPORT_FROM',
- r'\bPIALERT_WEB_PROTECTION\b': 'SETPWD_enable_password',
- r'\bPIALERT_WEB_PASSWORD\b': 'SETPWD_password',
- r'REPORT_MAIL=True': "SMTP_RUN='on_notification'",
- r'REPORT_APPRISE=True': "APPRISE_RUN='on_notification'",
- r'REPORT_NTFY=True': "NTFY_RUN='on_notification'",
- r'REPORT_WEBHOOK=True': "WEBHOOK_RUN='on_notification'",
- r'REPORT_PUSHSAFER=True': "PUSHSAFER_RUN='on_notification'",
- r'REPORT_MQTT=True': "MQTT_RUN='on_notification'",
- # r'PIHOLE_CMD=': 'PIHOLE_CMD_OLD=',
- r'\bINCLUDED_SECTIONS\b': 'NTFPRCS_INCLUDED_SECTIONS',
- r'\bDIG_GET_IP_ARG\b': 'INTRNT_DIG_GET_IP_ARG',
- r'dev_MAC': 'devMac',
- r'dev_Name': 'devName',
- r'dev_Favorite': 'devFavorite',
- r'dev_Group': 'devGroup',
- r'dev_Comments': 'devComments',
- r'dev_FirstConnection': 'devFirstConnection',
- r'dev_LastConnection': 'devLastConnection',
- r'dev_LastIP': 'devLastIP',
- r'dev_StaticIP': 'devStaticIP',
- r'dev_ScanCycle': 'devScan',
- r'dev_LogEvents': 'devLogEvents',
- r'dev_AlertEvents': 'devAlertEvents',
- r'dev_AlertDeviceDown': 'devAlertDown',
- r'dev_SkipRepeated': 'devSkipRepeated',
- r'dev_LastNotification': 'devLastNotification',
- r'dev_PresentLastScan': 'devPresentLastScan',
- r'dev_NewDevice': 'devIsNew',
- r'dev_Location': 'devLocation',
- r'dev_Archived': 'devIsArchived',
- r'dev_Network_Node_MAC_ADDR': 'devParentMAC',
- r'dev_Network_Node_port': 'devParentPort',
- r'dev_Icon': 'devIcon',
- r'dev_GUID': 'devGUID',
- r'dev_NetworkSite': 'devSite',
- r'dev_SSID': 'devSSID',
- r'dev_SyncHubNodeName': 'devSyncHubNode',
- r'dev_SourcePlugin': 'devSourcePlugin',
- r'/home/pi/pialert\b': '/app'
}
diff --git a/server/scan/device_handling.py b/server/scan/device_handling.py
index 948ab52d..636b47ce 100755
--- a/server/scan/device_handling.py
+++ b/server/scan/device_handling.py
@@ -8,10 +8,11 @@ import subprocess
import conf
import os
import re
-from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, get_device_name_nbtlookup, get_device_name_nslookup, get_device_name_mdns, check_IP_format, sanitize_SQL_input
+from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, check_IP_format, sanitize_SQL_input
from logger import mylog
from const import vendorsPath, vendorsPathNewest, sql_generateGuid
from models.device_instance import DeviceInstance
+from scan.name_resolution import NameResolver
#-------------------------------------------------------------------------------
# Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP
@@ -481,88 +482,109 @@ def update_devices_data_from_scan (db):
mylog('debug','[Update Devices] Update devices end')
#-------------------------------------------------------------------------------
-def update_devices_names (db):
- sql = db.sql #TO-DO
- # Initialize variables
- recordsToUpdate = []
- recordsNotFound = []
+def update_devices_names(db):
+ sql = db.sql
+ resolver = NameResolver(db)
+ device_handler = DeviceInstance(db)
nameNotFound = "(name not found)"
- ignored = 0
- notFound = 0
+ # Define resolution strategies in priority order
+ strategies = [
+ (resolver.resolve_dig, 'dig'),
+ (resolver.resolve_mdns, 'mdns'),
+ (resolver.resolve_nslookup, 'nslookup'),
+ (resolver.resolve_nbtlookup, 'nbtlookup')
+ ]
- foundDig = 0
- foundmDNSLookup = 0
- foundNsLookup = 0
- foundNbtLookup = 0
+ def resolve_devices(devices, resolve_both_name_and_fqdn=True):
+ """
+ Attempts to resolve device names and/or FQDNs using available strategies.
+
+ Parameters:
+ devices (list): List of devices to resolve.
+ resolve_both_name_and_fqdn (bool): If True, resolves both name and FQDN.
+ If False, resolves only FQDN.
+
+ Returns:
+ recordsToUpdate (list): List of [newName, newFQDN, devMac] or [newFQDN, devMac] for DB update.
+ recordsNotFound (list): List of [nameNotFound, devMac] for DB update.
+ foundStats (dict): Number of successes per strategy.
+ notFound (int): Number of devices not resolved.
+ """
+ recordsToUpdate = []
+ recordsNotFound = []
+ foundStats = {label: 0 for _, label in strategies}
+ notFound = 0
- # Gen unknown devices
- device_handler = DeviceInstance(db)
- # Retrieve devices
+ for device in devices:
+ newName = nameNotFound
+ newFQDN = ''
+
+ # Attempt each resolution strategy in order
+ for resolve_fn, label in strategies:
+ resolved = resolve_fn(device['devMac'], device['devLastIP'])
+
+ # Only use name if resolving both name and FQDN
+ newName = resolved.cleaned if resolve_both_name_and_fqdn else None
+ newFQDN = resolved.raw
+
+ # If a valid result is found, record it and stop further attempts
+ if newFQDN not in [nameNotFound, '', 'localhost.'] and ' communications error to ' not in newFQDN:
+ foundStats[label] += 1
+
+ if resolve_both_name_and_fqdn:
+ recordsToUpdate.append([newName, newFQDN, device['devMac']])
+ else:
+ recordsToUpdate.append([newFQDN, device['devMac']])
+ break
+
+ # If no name was resolved, queue device for "(name not found)" update
+ if resolve_both_name_and_fqdn and newName == nameNotFound:
+ notFound += 1
+ if device['devName'] != nameNotFound:
+ recordsNotFound.append([nameNotFound, device['devMac']])
+
+ return recordsToUpdate, recordsNotFound, foundStats, notFound
+
+ # --- Step 1: Update device names for unknown devices ---
unknownDevices = device_handler.getUnknown()
+ if unknownDevices:
+ mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}')
- # skip checks if no unknown devices
- if len(unknownDevices) == 0:
- return
+ # Try resolving both name and FQDN
+ recordsToUpdate, recordsNotFound, foundStats, notFound = resolve_devices(unknownDevices)
- # Devices without name
- mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}')
+ # Log summary
+ mylog('verbose', f"[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['dig']}/{foundStats['mdns']}/{foundStats['nslookup']}/{foundStats['nbtlookup']})")
+ mylog('verbose', f'[Update Device Name] Names Not Found : {notFound}')
- for device in unknownDevices:
- newName = nameNotFound
-
- # Resolve device name with DiG
- newName = resolve_device_name_dig (device['devMac'], device['devLastIP'])
-
- # count
- if newName != nameNotFound:
- foundDig += 1
-
- # Resolve device name with AVAHISCAN plugin data
- if newName == nameNotFound:
- newName = get_device_name_mdns(db, device['devMac'], device['devLastIP'])
+ # Apply updates to database
+ sql.executemany("UPDATE Devices SET devName = ? WHERE devMac = ?", recordsNotFound)
+ sql.executemany("UPDATE Devices SET devName = ?, devFQDN = ? WHERE devMac = ?", recordsToUpdate)
- if newName != nameNotFound:
- foundmDNSLookup += 1
+ # --- Step 2: Optionally refresh FQDN for all devices ---
+ if get_setting_value("REFRESH_FQDN"):
+ allDevices = device_handler.getAll()
+ if allDevices:
+ mylog('verbose', f'[Update FQDN] Trying to resolve FQDN. Devices count: {len(allDevices)}')
- # Resolve device name with NSLOOKUP plugin data
- if newName == nameNotFound:
- newName = get_device_name_nslookup(db, device['devMac'], device['devLastIP'])
+ # Try resolving only FQDN
+ recordsToUpdate, _, foundStats, notFound = resolve_devices(allDevices, resolve_both_name_and_fqdn=False)
- if newName != nameNotFound:
- foundNsLookup += 1
-
- # Resolve device name with NBTLOOKUP plugin data
- if newName == nameNotFound:
- newName = get_device_name_nbtlookup(db, device['devMac'], device['devLastIP'])
+ # Log summary
+ mylog('verbose', f"[Update FQDN] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['dig']}/{foundStats['mdns']}/{foundStats['nslookup']}/{foundStats['nbtlookup']})")
+ mylog('verbose', f'[Update FQDN] Names Not Found : {notFound}')
- if newName != nameNotFound:
- foundNbtLookup += 1
-
- # if still not found update name so we can distinguish the devices where we tried already
- if newName == nameNotFound :
+ # Apply FQDN-only updates
+ sql.executemany("UPDATE Devices SET devFQDN = ? WHERE devMac = ?", recordsToUpdate)
- notFound += 1
-
- # if devName is the same as what we will change it to, take no action
- # this mitigates a race condition which would overwrite a users edits that occured since the select earlier
- if device['devName'] != nameNotFound:
- recordsNotFound.append (["(name not found)", device['devMac']])
- else:
- # name was found
- recordsToUpdate.append ([newName, device['devMac']])
-
- # Print log
- mylog('verbose', [f'[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundDig}/{foundmDNSLookup}/{foundNsLookup}/{foundNbtLookup})'] )
- mylog('verbose', [f'[Update Device Name] Names Not Found : {notFound}'] )
-
- # update not found devices with (name not found)
- sql.executemany ("UPDATE Devices SET devName = ? WHERE devMac = ? ", recordsNotFound )
- # update names of devices which we were bale to resolve
- sql.executemany ("UPDATE Devices SET devName = ? WHERE devMac = ? ", recordsToUpdate )
+ # Commit all database changes
db.commitDB()
+
+
+
#-------------------------------------------------------------------------------
# Check if the variable contains a valid MAC address or "Internet"
def check_mac_or_internet(input_str):
diff --git a/server/scan/name_resolution.py b/server/scan/name_resolution.py
new file mode 100755
index 00000000..4e057696
--- /dev/null
+++ b/server/scan/name_resolution.py
@@ -0,0 +1,83 @@
+import sys
+import re
+import subprocess
+import socket
+import dns.resolver
+
+# Register NetAlertX directories
+INSTALL_PATH = "/app"
+sys.path.extend([f"{INSTALL_PATH}/server"])
+
+import conf
+from const import *
+from logger import mylog
+from helper import get_setting_value
+
+class ResolvedName:
+ def __init__(self, raw: str = "(name not found)", cleaned: str = "(name not found)"):
+ self.raw = raw
+ self.cleaned = cleaned
+
+ def __str__(self):
+ return self.cleaned
+
+class NameResolver:
+ def __init__(self, db):
+ self.db = db
+
+ def resolve_from_plugin(self, plugin: str, pMAC: str, pIP: str) -> ResolvedName:
+ sql = self.db.sql
+ nameNotFound = ResolvedName()
+
+ # Check by MAC
+ sql.execute(f"""
+ SELECT Watched_Value2 FROM Plugins_Objects
+ WHERE Plugin = '{plugin}' AND Object_PrimaryID = '{pMAC}'
+ """)
+ result = sql.fetchall()
+ self.db.commitDB()
+ if result:
+ raw = result[0][0]
+ return ResolvedName(raw, self.clean_device_name(raw, False))
+
+ # Check by IP
+ sql.execute(f"""
+ SELECT Watched_Value2 FROM Plugins_Objects
+ WHERE Plugin = '{plugin}' AND Object_SecondaryID = '{pIP}'
+ """)
+ result = sql.fetchall()
+ self.db.commitDB()
+ if result:
+ raw = result[0][0]
+ return ResolvedName(raw, self.clean_device_name(raw, True))
+
+ return nameNotFound
+
+ def resolve_mdns(self, pMAC, pIP) -> ResolvedName:
+ return self.resolve_from_plugin("AVAHISCAN", pMAC, pIP)
+
+ def resolve_nslookup(self, pMAC, pIP) -> ResolvedName:
+ return self.resolve_from_plugin("NSLOOKUP", pMAC, pIP)
+
+ def resolve_nbtlookup(self, pMAC, pIP) -> ResolvedName:
+ return self.resolve_from_plugin("NBTSCAN", pMAC, pIP)
+
+ def resolve_dig(self, pMAC, pIP) -> ResolvedName:
+ return self.resolve_from_plugin("DIGSCAN", pMAC, pIP)
+
+ def clean_device_name(self, name: str, match_ip: bool) -> str:
+ mylog('debug', [f"[cleanDeviceName] input: {name}"])
+
+ if match_ip:
+ name += " (IP match)"
+
+ regexes = get_setting_value('NEWDEV_NAME_CLEANUP_REGEX') or []
+ for rgx in regexes:
+ mylog('trace', [f"[cleanDeviceName] applying regex: {rgx}"])
+ name = re.sub(rgx, "", name)
+
+ name = re.sub(r'\.$', '', name)
+ name = name.replace(". (IP match)", " (IP match)")
+
+ mylog('debug', [f"[cleanDeviceName] output: {name}"])
+ return name
diff --git a/server/workflows/app_events.py b/server/workflows/app_events.py
index 364fb791..2d89fe98 100755
--- a/server/workflows/app_events.py
+++ b/server/workflows/app_events.py
@@ -170,7 +170,7 @@ class AppEvent_obj:
END;
"""
- mylog("verbose", [query])
+ # mylog("verbose", [query])
self.db.sql.execute(query)
diff --git a/test/test_helper.py b/test/test_helper.py
index 576b8601..f1493acf 100755
--- a/test/test_helper.py
+++ b/test/test_helper.py
@@ -86,9 +86,10 @@ def insert_devices(db_path, num_entries=1):
devSSID,
devSyncHubNode,
devSourcePlugin,
- devCustomProps
+ devCustomProps,
+ devFQDN
)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
"""
# List of device types, vendors, groups, locations
@@ -130,6 +131,7 @@ def insert_devices(db_path, num_entries=1):
dev_sync_hub_node = "" # Left as NULL
dev_source_plugin = "" # Left as NULL
dev_devCustomProps = "" # Left as NULL
+ dev_devFQDN = "" # Left as NULL
# Execute the insert query
cursor.execute(insert_query, (
@@ -163,7 +165,8 @@ def insert_devices(db_path, num_entries=1):
dev_ssid,
dev_sync_hub_node,
dev_source_plugin,
- dev_devCustomProps
+ dev_devCustomProps,
+ dev_devFQDN
))
# Commit after every 1000 rows to improve performance