Files
p2prc-home-server/python/library.py

254 lines
6.5 KiB
Python

import ctypes
from ctypes import Structure, c_char_p, c_int, cdll
from dataclasses import dataclass, astuple
import time
import json
from urllib.parse import urlparse
from typing import List
import subprocess
import uuid
import dacite
import os
import sqlalchemy
import dataclasses
from typing import Union
import schedule
import threading
import requests
p2prc = ctypes.CDLL("SharedObjects/p2prc.so")
p2prc.Init("")
# Start running schedule
schedule.run_pending()
# # Create Sqlite database to track processes
# engine=sqlalchemy.create_engine(f'sqlite:///homeserver.db')
# Global variable
# A global variable will be populated on runtime.
# It is read from P2PRC directly or from a local
# database.
# Node information
# Generated using: https://jsonformatter.org/json-to-python
@dataclass
class Node:
Name: str
MachineUsername: str
IPV4: str
IPV6: str
Latency: int
Download: int
Upload: int
ServerPort: str
BareMetalSSHPort: str
NAT: str
EscapeImplementation: str
ProxyServer: str
UnSafeMode: bool
PublicKey: str
CustomInformation: str
@dataclass
class IPAddress:
ip_address: List[Node]
# ----------------------------------------------------------------------------
# ----------------------------- Helper functions -----------------------------
# ----------------------------------------------------------------------------
def StartServer():
# Starting P2PRC as a server mode
p2prc.Server()
for _ in iter(int, 1):
pass
# Class to create string to pass as string function
# parameter to shared object file
class go_string(Structure):
_fields_ = [
("p", c_char_p),
("n", c_int)]
# Local port intended to ensure that
def P2PRCMapPort(port="",domainname="",serveraddress=""):
# Local Port intended to be escaped outside NAT
# Converting to the appropirate datatype to be
# passed as a string to GoLang.
port = go_string(c_char_p(port.encode('utf-8')), len(port))
domainname = go_string(c_char_p(domainname.encode('utf-8')), len(domainname))
serveraddress = go_string(c_char_p(serveraddress.encode('utf-8')), len(serveraddress))
# Defining the response type of the GoLang function
# function
p2prc.MapPort.restype = c_char_p
# Calling the Go function
address = p2prc.MapPort(port,domainname,serveraddress)
res = str(address).strip("b'")
return res
def ListNodes():
# View IP Table information
p2prc.ViewIPTable.restype = c_char_p
ipTable = p2prc.ViewIPTable()
# View IP Table as
ipTableObject = json.loads((str(ipTable).strip("b'")))
dat: IPAddress = dacite.from_dict(IPAddress,ipTableObject)
return dat
# -----------------------------------------------------------------------------------
# ----------------------------- End of helper functions -----------------------------
# -----------------------------------------------------------------------------------
def infinity():
while True:
yield
# Process datatype
@dataclass
class Process:
ID: str
InternalPortNo: str
ExternalAddress: str
NodeInfo: Node
TaskName: str
CommandToRunScript: str
CommandToKillScript: str
Status: bool
DomainName: str
@dataclass
class Processes:
Processes: List[Process]
PublicProcesses: Union[Processes, None] = None
# Initial defined functions
# Exposed functions
def SpinProcess(process: Process):
# Starts P2PRC process
os.system(process.CommandToRunScript)
# concats to the public exposed IP address + The Node information exposed P2PRC
# server port.
ServerAddress = process.NodeInfo.IPV4 + ":" + process.NodeInfo.ServerPort
process.ExternalAddress = P2PRCMapPort(port=process.InternalPortNo,
domainname=process.DomainName,
serveraddress=ServerAddress)
process.ID = str(uuid.uuid4())
process.Status = True
# Save the process to memory
AddProcessToMemory(process)
# Save to disk
SaveProcess()
return process
# Kill process based on the process provided
def KillProcess(process: Process):
os.system(process.CommandToKillScript)
process.Status = False
# Remove the process from memory
PublicProcesses.Processes.remove(process)
# Remove from disk
SaveProcess()
return process
# Saves the list of processes provided as a JSON file
def SaveProcess():
global PublicProcesses
with open('data.json', 'w+') as f:
json.dump(dataclasses.asdict(PublicProcesses), f, indent=4)
# Read saves processes
def ReadSavedProcesses():
try:
global PublicProcesses
with open('data.json', 'r') as file:
data = json.load(file)
PublicProcesses = dacite.from_dict(Processes,data)
except Exception as e:
PublicProcesses = None
# Gets called when the python file is called
ReadSavedProcesses()
# List processes
def ListProcess():
global PublicProcesses
return PublicProcesses
# Adds the process dataclass the to
# the global variable PublicProcess.
def AddProcessToMemory(process: Process):
global PublicProcesses
if PublicProcesses == None:
PublicProcesses = Processes(Processes=[process])
# PublicProcesses.Processes.append(process)
else:
PublicProcesses.Processes.append(process)
# Tracks processes in the background and removes them
# if not able to ping to the process
def BackgroundTrackProcess():
global PublicProcesses
while 1:
print("starting background process")
for i in PublicProcesses.Processes:
if not check_ping(i.ExternalAddress):
i.Status = False
SaveProcess()
time.sleep(3)
# Run the track processes every 3 seconds
# schedule.every(3).seconds.do(BackgroundTrackProcess)
Background_track_thread = threading.Thread(target=BackgroundTrackProcess, name="Track Process")
# Kills the tread of system exit
Background_track_thread.setDaemon(True)
Background_track_thread.start()
def check_ping(host):
try:
page = requests.get('http://' + host)
if page.status_code == 200:
return True
return False
except:
return False
# source: https://stackoverflow.com/questions/26468640/python-function-to-test-ping
# def check_ping(host: str):
# response = os.system("ping -c 1 " + host)
# if response == 0:
# pingstatus = True
# else:
# pingstatus = False
# return pingstatus
# def ProcessInformation():
# def ListProcess():