import json import account import requests import os # Plugin Data info = { "name": "HNS Links", "description": "This is a plugin to quickly setup a HNS Links site<br>1. Connect varo instance with api key<br>2. Add domain to varo<br> 3. Add it to HNS Links<br>4. Wait a few minutes and readd it to HNS Links to ensure the generated TLSA is added<br>5. Wait for the DNS update to propogate (up to 7 hrs)", "version": "1.0", "author": "Nathan.Woodburn/" } # Functions functions = { "login":{ "name": "Login to Varo Instance", "type": "default", "description": "You need to connect to a varo instance to setup the dns for domains", "params": { "instance": { "name":"Varo Instance (eg. domains.hns.au)", "type":"text" }, "key": { "name":"API Key", "type":"text" } }, "returns": { "status": { "name": "Status", "type": "text" } } }, "addDomain":{ "name": "Add domain to varo", "type": "default", "description": "Add a domain to Varo", "params": { "domain": { "name":"Domain", "type":"text" } }, "returns": { "status": { "name": "Status of the function", "type": "text" } } }, "hnslinks":{ "name": "Add domain to HNS Links with wallet address", "type": "default", "description": "Add a domain to HNS Links", "params": { "domain": { "name":"Domain", "type":"text" } }, "returns": { "status": { "name": "Status of the function", "type": "text" } } }, "edit": { "name":" Edit HNS Links", "type": "default", "description": "Login and Edit your HNS Links", "params": { "domain": { "name":"Domain", "type":"text" } }, "returns": { "url": { "name": "Login URL", "type": "list" } } } } def login(params, authentication): instance = params["instance"] key = params["key"] # Test Connection if instance == "": return {"status":"Please enter a valid instance"} instance = instance.replace("https://","") instance = instance.replace("http://","") if instance[-1] == "/": instance = instance[:-1] try: # Authorization: Bearer <API Key> response = requests.post("https://" + instance + "/api", json={"action":"getInfo"}, headers={"Authorization": "Bearer " + key}) if response.status_code != 200: return {"status":"Error connecting to instance"} except: return {"status":"Error connecting to instance"} with open("user_data/hnslinks.json", "w") as f: json.dump({"instance":instance,"key":key},f) return {"status":"Connected to " + instance} def addDomain(params, authentication): # Add a domain to Varo domain = params["domain"] if not os.path.exists("user_data/hnslinks.json"): return {"status": "Missing Varo API or instance"} with open("user_data/hnslinks.json", "r") as f: auth = json.load(f) if not auth: return {"status": "Missing Varo API or instance"} if 'key' not in auth or 'instance' not in auth: return {"status": "Missing Varo API or instance"} api = auth["key"] instance = auth["instance"] headers = {"Authorization": f"Bearer {api}"} data = { "action": "getZones" } zones = requests.post(f"https://{instance}/api", json=data, headers=headers) if zones.status_code != 200: return {"status": "Error connecting to Varo"} if zones.json()["success"] != True: return {"status": "Error connecting to Varo"} zones = zones.json()["data"] for zone in zones: if zone["name"] == domain: return {"status": "Domain already exists"} # Check domain is owned by user wallet = authentication.split(":")[0] owned = account.getDomains(wallet) # Only keep owned domains ["name"] ownedNames = [domain["name"] for domain in owned] if domain not in ownedNames: return {"status": "Domain not owned by user"} data = { "action": "createZone", "domain": domain } response = requests.post(f"https://{instance}/api", json=data, headers=headers) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} zoneID = response.json()["data"]["zone"] data = { "action": "showZone", "zone": zoneID } response = requests.post(f"https://{instance}/api", json=data, headers=headers) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} zone = response.json()["data"] dns = [] for ns in zone['NS']: dns.append({'type': 'NS', 'value': ns}) ds = zone['DS'] ds = ds.split(' ') dns.append({'type': 'DS', 'keyTag': int(ds[0]), 'algorithm': int(ds[1]), 'digestType': int(ds[2]), 'digest': ds[3]}) dns = json.dumps(dns) response = account.setDNS(authentication,domain,dns) return {"status": "Success"} def hnslinks(params, authentication): # Verify domain is owned by user domain = params["domain"] wallet = authentication.split(":")[0] owned = account.getDomains(wallet) # Only keep owned domains ["name"] ownedNames = [domain["name"] for domain in owned] if domain not in ownedNames: return {"status": "Domain not owned by user"} # Get wallet address address = account.getAddress(wallet) if address == "": return {"status": "Error getting wallet address"} # Sign message hns-links response = account.signMessage(authentication,domain,"hns-links") if "error" in response and response["error"]: return {"status": f"Error: {response['error']}"} if 'result' not in response: return {"status": "Error signing message"} signature = response["result"] # Send request to hns-links data = { "domain": domain, "signature": signature, "data":{ "title": domain, "description": "Connected via FireWallet", "address": [ { "token": "hns", "address": address } ] } } response = requests.post("https://links.hns.au/api/v1/site", json=data) if response.status_code != 200: return {"status": "Error connecting to hns-links"} response = response.json() if "error" in response and response["error"]: return {"status": f"Error: {response['error']}"} ip = response["IP"] tlsa = response["TLSA"] # Get zones from varo with open("user_data/hnslinks.json", "r") as f: auth = json.load(f) if not auth: return {"status": "Missing Varo API or instance"} if 'key' not in auth or 'instance' not in auth: return {"status": "Missing Varo API or instance"} api = auth["key"] instance = auth["instance"] response = requests.post(f"https://{instance}/api", json={"action":"getZones"}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} zones = response.json()["data"] for zone in zones: if zone["name"] == domain: zoneID = zone["id"] break if zoneID == "": return {"status": "Error finding zone in Varo"} response = requests.post(f"https://{instance}/api", json={"action":"getRecords","zone":zoneID}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} if 'data' in response.json(): records = response.json()["data"] else: records = [] for record in records: if record["type"] == "A" and record["name"] == domain: response = requests.post(f"https://{instance}/api", json={"action":"deleteRecord","zone":zoneID,"record":record["uuid"]}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} if record["type"] == "TLSA" and record["name"] == f"_443._tcp.{domain}": response = requests.post(f"https://{instance}/api", json={"action":"deleteRecord","zone":zoneID,"record":record["uuid"]}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} response = requests.post(f"https://{instance}/api", json={"action":"addRecord","zone":zoneID,"name":"@","type":"A","content":ip}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} if tlsa != "": response = requests.post(f"https://{instance}/api", json={"action":"addRecord","zone":zoneID,"name":f"_443._tcp","type":"TLSA","content":tlsa}, headers={"Authorization": f"Bearer {api}"}) if response.status_code != 200: return {"status": "Error connecting to Varo"} if response.json()["success"] != True: return {"status": "Error connecting to Varo"} return {"status": "YAY!!! All done! Just wait a few minutes for HNS Links to fully initialize the site."} def edit(params, authentication): # Verify domain is owned by user domain = params["domain"] wallet = authentication.split(":")[0] owned = account.getDomains(wallet) # Only keep owned domains ["name"] ownedNames = [domain["name"] for domain in owned] if domain not in ownedNames: return {"status": "Domain not owned by user"} # Get wallet address address = account.getAddress(wallet) if address == "": return {"status": "Error getting wallet address"} # Sign message hns-links response = account.signMessage(authentication,domain,"hns-links") if "error" in response and response["error"]: return {"status": f"Error: {response['error']}"} if 'result' not in response: return {"status": "Error signing message"} signature = response["result"] return {"url": [f"<a href=https://links.hns.au/auth?username={domain}&signature={signature} target='_blank'>Edit HNS Links</a>"]}