diff --git a/plugins/public_info.py b/plugins/public_info.py
new file mode 100644
index 0000000..54012da
--- /dev/null
+++ b/plugins/public_info.py
@@ -0,0 +1,41 @@
+import json
+import account
+import requests
+
+# Plugin Data
+info = {
+ "name": "Public Node Dashboard",
+ "description": "Dashboard modules for public nodes",
+ "version": "1.0",
+ "author": "Nathan.Woodburn/"
+}
+
+# Functions
+functions = {
+ "main":{
+ "name": "Info Dashboard widget",
+ "type": "dashboard",
+ "description": "This creates the widget that shows on the dashboard",
+ "params": {},
+ "returns": {
+ "status":
+ {
+ "name": "Status of Node",
+ "type": "text"
+ }
+ }
+ }
+}
+
+def main(params, authentication):
+ info = account.hsd.getInfo()
+
+ status = f"Version: {info['version']}
Inbound Connections: {info['pool']['inbound']}
Outbound Connections: {info['pool']['outbound']}
"
+ if info['pool']['public']['listen']:
+ status += f"Public Node: Yes
Host: {info['pool']['public']['host']}
Port: {info['pool']['public']['port']}
"
+ else:
+ status += f"Public Node: No
"
+ status += f"Agent: {info['pool']['agent']}
Services: {info['pool']['services']}
"
+
+ return {"status": status}
+
\ No newline at end of file
diff --git a/plugins/renewal.py b/plugins/renewal.py
index fe9b2d8..9a9f850 100644
--- a/plugins/renewal.py
+++ b/plugins/renewal.py
@@ -88,7 +88,7 @@ def main(params, authentication):
if batch["error"] != "":
print("Failed to verify batch",flush=True)
print(batch["error"]["message"],flush=True)
- return {"status": "Failed", "transaction": "None"}
+ return {"status": f"Failed: {batch['error']['message']}", "transaction": "None"}
if 'result' in batch:
if batch['result'] != None:
diff --git a/plugins/troubleshooting.py b/plugins/troubleshooting.py
new file mode 100644
index 0000000..a5325e8
--- /dev/null
+++ b/plugins/troubleshooting.py
@@ -0,0 +1,237 @@
+import json
+import account
+import requests
+
+import dns.resolver
+import dns.message
+import dns.query
+import dns.rdatatype
+import dns.rrset
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+import tempfile
+import subprocess
+import binascii
+import datetime
+import dns.asyncresolver
+import httpx
+from requests_doh import DNSOverHTTPSSession, add_dns_provider
+import domainLookup
+
+doh_url = "https://hnsdoh.com/dns-query"
+
+# Plugin Data
+info = {
+ "name": "Troubleshooting",
+ "description": "Various troubleshooting functions",
+ "version": "1.0",
+ "author": "Nathan.Woodburn/"
+}
+
+# Functions
+functions = {
+ "dig":{
+ "name": "DNS Lookup",
+ "type": "default",
+ "description": "Do DNS lookups on a domain",
+ "params": {
+ "domain": {
+ "name":"Domain to lookup (eg. woodburn)",
+ "type":"text"
+ },
+ "type": {
+ "name":"Type of lookup (A,TXT,NS,DS,TLSA)",
+ "type":"text"
+ }
+ },
+ "returns": {
+ "result":
+ {
+ "name": "Result",
+ "type": "list"
+ }
+ }
+ },
+ "https_check":{
+ "name": "HTTPS Check",
+ "type": "default",
+ "description": "Check if a domain has an HTTPS certificate",
+ "params": {
+ "domain": {
+ "name":"Domain to lookup (eg. woodburn)",
+ "type":"text"
+ }
+ },
+ "returns": {
+ "result":
+ {
+ "name": "Result",
+ "type": "text"
+ }
+ }
+ },
+ "hip_lookup": {
+ "name": "Hip Lookup",
+ "type": "default",
+ "description": "Look up a domain's hip address",
+ "params": {
+ "domain": {
+ "name": "Domain to lookup",
+ "type": "text"
+ }
+ },
+ "returns": {
+ "result": {
+ "name": "Result",
+ "type": "text"
+ }
+ }
+ }
+}
+
+def dns_request(domain: str, rType:str) -> list[dns.rrset.RRset]:
+ if rType == "":
+ rType = "A"
+ rType = dns.rdatatype.from_text(rType.upper())
+
+
+ with httpx.Client() as client:
+ q = dns.message.make_query(domain, rType)
+ r = dns.query.https(q, doh_url, session=client)
+ return r.answer
+
+
+def dig(params, authentication):
+ domain = params["domain"]
+ type = params["type"]
+ result: list[dns.rrset.RRset] = dns_request(domain, type)
+ print(result)
+ if result:
+ if len(result) == 1:
+ result: dns.rrset.RRset = result[0]
+ result = result.items
+ return {"result": result}
+
+ else:
+ return {"result": result}
+ else:
+ return {"result": ["No result"]}
+
+
+
+def https_check(params, authentication):
+ domain = params["domain"]
+ domain_check = False
+ try:
+ # Get the IP
+ ip = list(dns_request(domain,"A")[0].items.keys())
+ if len(ip) == 0:
+ return {"result": "No IP found"}
+ ip = ip[0]
+ print(ip)
+
+ # Run the openssl s_client command
+ s_client_command = ["openssl","s_client","-showcerts","-connect",f"{ip}:443","-servername",domain,]
+
+ s_client_process = subprocess.Popen(s_client_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+ s_client_output, _ = s_client_process.communicate(input=b"\n")
+
+ certificates = []
+ current_cert = ""
+ for line in s_client_output.split(b"\n"):
+ current_cert += line.decode("utf-8") + "\n"
+ if "-----END CERTIFICATE-----" in line.decode("utf-8"):
+ certificates.append(current_cert)
+ current_cert = ""
+
+ # Remove anything before -----BEGIN CERTIFICATE-----
+ certificates = [cert[cert.find("-----BEGIN CERTIFICATE-----"):] for cert in certificates]
+
+ if certificates:
+ cert = certificates[0]
+
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_cert_file:
+ temp_cert_file.write(cert)
+ temp_cert_file.seek(0) # Move back to the beginning of the temporary file
+
+ tlsa_command = ["openssl","x509","-in",temp_cert_file.name,"-pubkey","-noout","|","openssl","pkey","-pubin","-outform","der","|","openssl","dgst","-sha256","-binary",]
+
+ tlsa_process = subprocess.Popen(" ".join(tlsa_command), shell=True, stdout=subprocess.PIPE)
+ tlsa_output, _ = tlsa_process.communicate()
+
+ tlsa_server = "3 1 1 " + binascii.hexlify(tlsa_output).decode("utf-8")
+ print(f"TLSA Server: {tlsa_server}")
+
+
+ # Get domains
+ cert_obj = x509.load_pem_x509_certificate(cert.encode("utf-8"), default_backend())
+
+ domains = []
+ for ext in cert_obj.extensions:
+ if ext.oid == x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME:
+ san_list = ext.value.get_values_for_type(x509.DNSName)
+ domains.extend(san_list)
+
+ # Extract the common name (CN) from the subject
+ common_name = cert_obj.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
+ if common_name:
+ if common_name[0].value not in domains:
+ domains.append(common_name[0].value)
+
+ if domains:
+ if domain in domains:
+ domain_check = True
+ else:
+ # Check if matching wildcard domain exists
+ for d in domains:
+ if d.startswith("*"):
+ if domain.split(".")[1:] == d.split(".")[1:]:
+ domain_check = True
+ break
+
+
+ expiry_date = cert_obj.not_valid_after
+ # Check if expiry date is past
+ if expiry_date < datetime.datetime.now():
+ return {"result": "Certificate is expired"}
+ else:
+ return {"result": "No certificate found"}
+
+ try:
+ # Check for TLSA record
+ tlsa = dns_request(f"_443._tcp.{domain}","TLSA")
+ tlsa = list(tlsa[0].items.keys())
+ if len(tlsa) == 0:
+ return {"result": "No TLSA record found"}
+ tlsa = tlsa[0]
+ print(f"TLSA: {tlsa}")
+
+ if not tlsa:
+ return {"result": "TLSA lookup failed"}
+ else:
+ if tlsa_server == str(tlsa):
+ if domain_check:
+ add_dns_provider("HNSDoH", "https://hnsdoh.com/dns-query")
+
+ session = DNSOverHTTPSSession("HNSDoH")
+ r = session.get(f"https://{domain}/",verify=False)
+ if r.status_code != 200:
+ return {"result": "Webserver returned status code: " + str(r.status_code)}
+ return {"result": "HTTPS check successful"}
+ else:
+ return {"result": "TLSA record matches certificate, but domain does not match certificate"}
+
+ else:
+ return {"result": "TLSA record does not match certificate"}
+
+ except Exception as e:
+ return {"result": "TLSA lookup failed with error: " + str(e)}
+
+ # Catch all exceptions
+ except Exception as e:
+ return {"result": "Lookup failed.
Error: " + str(e)}
+
+def hip_lookup(params, authentication):
+ domain = params["domain"]
+ hip = domainLookup.hip2(domain)
+ return {"result": hip}
\ No newline at end of file
diff --git a/templates/components/dashboard-plugin.html b/templates/components/dashboard-plugin.html
index c6fdb75..12aeb15 100644
--- a/templates/components/dashboard-plugin.html
+++ b/templates/components/dashboard-plugin.html
@@ -2,7 +2,7 @@