Compare commits

..

14 Commits

13 changed files with 437 additions and 121 deletions

View File

@ -8,7 +8,7 @@ import bcrypt
def login(config, user, password, sysid): def login(config, user, password, sysid):
fprint("Attempting to login as " + user) fprint("Attempting to login as " + user)
filename = sysid + "login.csv" filename = sysid + "login.csv"
#return True
#hashpasswd = bcrypt.hashpw(password.encode('utf-8'), user).decode() #hashpasswd = bcrypt.hashpw(password.encode('utf-8'), user).decode()
with open(find_data_file(filename), "w", newline="") as f: with open(find_data_file(filename), "w", newline="") as f:
writer = csv.writer(f) writer = csv.writer(f)

View File

@ -10,10 +10,15 @@ import time
import csv import csv
import ssh import ssh
def get_blocklist(config): def get_blocklist(config, settings):
setup_child() setup_child()
appendbad = settings["appendbad"]
whitelist = settings["whitelist"]
fprint("Downloading deny list from server") fprint("Downloading deny list from server")
try:
data = ssh.check_for_file(config, "BadIPs.csv", "receive") data = ssh.check_for_file(config, "BadIPs.csv", "receive")
except:
data = list()
#fprint(data.stdout) #fprint(data.stdout)
csvreader = csv.reader(data.stdout.split("\n"), delimiter=',', quotechar='|') csvreader = csv.reader(data.stdout.split("\n"), delimiter=',', quotechar='|')
data2 = list() data2 = list()
@ -21,14 +26,29 @@ def get_blocklist(config):
data2.append(row) data2.append(row)
data2 = [i for i in data2 if i] data2 = [i for i in data2 if i]
#fprint(data2) #fprint(data2)
data2.append(["N/A", "TCP", "N/A", "N/A", "20.112.52.29", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) for line in appendbad:
data2.append(["N/A", "TCP", "N/A", "N/A", "20.81.111.85", "80", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) data2.append(line)
data2.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
data2.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
data2.append(["N/A", "TCP", "N/A", "N/A", "174.143.130.167", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
data2.append(["N/A", "TCP", "N/A", "N/A", "216.47.134.203", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
data2.append(["N/A", "TCP", "N/A", "N/A", "34.111.83.189", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
for line in whitelist:
for line2 in data2:
if line[0] in line2 and line[1] in line2:
fprint("Whitelisting P1 " + line[0] + ":" + str(line[1]))
data2.remove(line2)
if line[2] in settings["badapps"]:
tmp = settings["badapps"]
tmp.remove(line[2])
settings["badapps"] = tmp
if line[0] in settings["badips"]:
tmp = settings["badips"]
tmp.remove(line[0])
settings["badips"] = tmp
for line3 in settings["badlines"]:
if line[0] in line3 and line[1] in line3:
fprint("Whitelisting P2" + line[0] + ":" + str(line[1]))
tmp = settings["badlines"]
tmp.remove(line3)
settings["badlines"] = tmp
fprint(data2) fprint(data2)
with open(find_data_file("blocklist.csv"), "w", newline="") as f: with open(find_data_file("blocklist.csv"), "w", newline="") as f:
@ -52,7 +72,7 @@ def block_conn(config, datafile, res):
baddata = res baddata = res
fprint("Local loaded successfully") fprint("Local loaded successfully")
goodct = 0
#fprint(mydata) #fprint(mydata)
for line in mydata: for line in mydata:
#fprint(line) #fprint(line)
@ -61,19 +81,25 @@ def block_conn(config, datafile, res):
continue continue
srcip = line[2].split(":")[0] srcip = line[2].split(":")[0]
srcport = line[2].split(":")[1]
destip = line[3].split(":")[0] destip = line[3].split(":")[0]
destport = line[3].split(":")[1]
pid = line[5] pid = line[5]
try: try:
pid = int(pid) pid = int(pid)
except ValueError: except ValueError:
continue continue
found = False
for line in baddata: for line in baddata:
#fprint(destip + " " + line[4]) #fprint(destip + " " + line[4])
badsrcip = line[2] badsrcip = line[2]
badsrcport = line[3]
baddestip = line[4] baddestip = line[4]
baddestport = line[5]
badpid = line[11] badpid = line[11]
if srcip == badsrcip or destip == baddestip and not pid in badapps:
if ((srcip == badsrcip and srcport == badsrcport) or (destip == baddestip and destport == baddestport)) and not pid in badapps:
found = True
fprint("FLAG " + srcip + " " + destip + " " + str(pid)) fprint("FLAG " + srcip + " " + destip + " " + str(pid))
badapps.append(pid) badapps.append(pid)
badips.append(baddestip) badips.append(baddestip)
@ -81,4 +107,8 @@ def block_conn(config, datafile, res):
#fprint(badapps) #fprint(badapps)
#fprint("FLAG " + srcip + " " + destip + " " + str(pid)) #fprint("FLAG " + srcip + " " + destip + " " + str(pid))
#kill(pid) #kill(pid)
return badapps, badips, badlines
if not found:
goodct = goodct + 1
return badapps, badips, badlines, goodct

View File

@ -1,8 +1,8 @@
core: core:
autostart: false autostart: true
clockspeed: 20 clockspeed: 20
interval: 10 interval: 10
level: 3 level: 2
localadmin: true localadmin: true
sftp: sftp:
filepath: filepath:

View File

@ -10,6 +10,7 @@ from util import find_data_file
from util import fprint from util import fprint
from util import kill from util import kill
from util import run_cmd from util import run_cmd
from notification import send_notification
import taskbartool import taskbartool
import util import util
import netstat import netstat
@ -18,8 +19,8 @@ import auth
import panel import panel
import block import block
badapps = [756, 278670]
badips = ["208.59.79.12",] history = list()
displaydata = None displaydata = None
settings = None settings = None
netdata_res = None netdata_res = None
@ -48,7 +49,7 @@ if win32:
_, username = res.strip().rsplit("\n", 1) _, username = res.strip().rsplit("\n", 1)
userid, sysdom = username.rsplit("\\", 1) userid, sysdom = username.rsplit("\\", 1)
if linux: if linux or macos:
sysid = hex(uuid.getnode()) sysid = hex(uuid.getnode())
#fprint(sysid) #fprint(sysid)
datafile += sysid datafile += sysid
@ -66,11 +67,15 @@ def netstat_done(res):
def process_done(res): def process_done(res):
if settings["running"] == True: if settings["running"] == True:
fprint("uploading to sftp...", settings) fprint("uploading to sftp...", settings)
#ssh.sftp_send_data(res, config, datafile) #ssh.sftp_send_data(config, datafile, 'send')
procdata_res = pool.apply_async(ssh.sftp_send_data, (config, datafile, 'send'), callback=upload_done) procdata_res = pool.apply_async(ssh.sftp_send_data, (config, datafile, 'send'), callback=upload_done)
def upload_done(res): def upload_done(res):
settings["block"] = True settings["block"] = True
tmpstat = settings["stats"]
tmpstat[2] += 1
settings["stats"] = tmpstat
def login_done(res): def login_done(res):
if not res: if not res:
@ -87,7 +92,16 @@ def blockdata_done(res):
tmpkill = settings["kill"] tmpkill = settings["kill"]
settings["kill"] = False settings["kill"] = False
#block_res = pool.apply_async(block.block_conn, (config, datafile, res, settings)) #block_res = pool.apply_async(block.block_conn, (config, datafile, res, settings))
block_pids, block_ips, block_data = block.block_conn(config, datafile, res) block_pids, block_ips, block_data, goodct = block.block_conn(config, datafile, res)
tmpstat = settings["stats"]
tmpstat[1] += goodct
if tmpstat[0] > 0 and goodct > 0:
tmpstat[4] = 1.0 / (goodct * 100.0 / tmpstat[0])
else:
tmpstat[4] = 0.0
tmpstat[3] += 1
settings["stats"] = tmpstat
tmplist = settings["badapps"] tmplist = settings["badapps"]
for x in block_pids: for x in block_pids:
@ -117,6 +131,11 @@ def blockdata_done(res):
settings["newdata"] = True settings["newdata"] = True
def readstat_done(res):
settings["stats"] = res
fprint("Read stats!" + str(settings["stats"]), settings)
def killall(): def killall():
kids = active_children() kids = active_children()
for kid in kids: for kid in kids:
@ -138,10 +157,13 @@ def mainloop(pool):
#print(killme) #print(killme)
if killme.value > 0: if killme.value > 0:
#print("killing") #print("killing")
util.clear_fwll() # clear the firewall rules before shutdown
killall() killall()
#print(res.get(timeout=1)) #print(res.get(timeout=1))
if counter == 0: # runs every INTERVAL if counter == 0: # runs every INTERVAL
#fprint("start loop") #fprint("start loop")
if settings["stats"][1] > 0:
util.write_stats(settings["stats"])
if netdata_res is None or netdata_res.ready(): if netdata_res is None or netdata_res.ready():
#rawdata = netdata_res.get() #rawdata = netdata_res.get()
#procdata_res = pool.apply_async(process_netstat, (rawdata)) #procdata_res = pool.apply_async(process_netstat, (rawdata))
@ -153,11 +175,11 @@ def mainloop(pool):
if settings["continueui"] == True: if settings["continueui"] == True:
settings["continueui"] = False settings["continueui"] = False
if ppanel is not None: #if ppanel is not None:
# login panel is already open # login panel is already open
ppanel.terminate() # ppanel.terminate()
ppanel = Process(target=panel.openwindow, args=(displaydata,settings,killme)) # ppanel = Process(target=panel.openwindow, args=(displaydata,settings,killme))
ppanel.start() # ppanel.start()
if settings["showui"] == True: if settings["showui"] == True:
settings["showui"] = False settings["showui"] = False
@ -171,8 +193,9 @@ def mainloop(pool):
if settings["block"] == True and settings["running"] == True: if settings["block"] == True and settings["running"] == True:
blockdata_res = pool.apply_async(block.get_blocklist, (config,), callback=blockdata_done) #blockdata_res = pool.apply_async(block.get_blocklist, (config, settings), callback=blockdata_done)
#block.get_blocklist(config) res = block.get_blocklist(config, settings)
blockdata_done(res)
settings["block"] = False settings["block"] = False
if config["core"]["level"] == 0: if config["core"]["level"] == 0:
@ -195,18 +218,34 @@ def mainloop(pool):
tmplist = settings["badapps"] tmplist = settings["badapps"]
settings["badapps"] = list() settings["badapps"] = list()
for x in tmplist: for x in tmplist:
if x > 4:
send_notification("Killing PID " + str(x))
kill(x) kill(x)
if settings["fwll"] == True: if settings["fwll"] == True:
global history
tmplist = settings["badlines"] tmplist = settings["badlines"]
tmpstat = settings["stats"]
tmpstat[0] += len(tmplist)
settings["stats"] = tmpstat
settings["badlines"] = list() settings["badlines"] = list()
for line in tmplist: for line in tmplist:
badproto = line[1] badproto = line[1]
badip = line[4] badip = line[4]
badport = line[5] badport = line[5]
if (badip, badport) not in history:
fprint("Firewalling " + badip + ":" + str(badport), settings) fprint("Firewalling " + badip + ":" + str(badport), settings)
send_notification("Firewalling " + badip + ":" + str(badport))
history.append((badip, badport))
if win32:
cmd = 'New-NetFirewallRule -DisplayName "IPPigeon Security Rule ' + badip + ':' + str(badport) + '" -Group "IPPigeon" -Direction Outbound -LocalPort Any -Protocol ' + badproto + ' -Action Block -RemoteAddress ' + badip + ' -RemotePort ' + str(badport) cmd = 'New-NetFirewallRule -DisplayName "IPPigeon Security Rule ' + badip + ':' + str(badport) + '" -Group "IPPigeon" -Direction Outbound -LocalPort Any -Protocol ' + badproto + ' -Action Block -RemoteAddress ' + badip + ' -RemotePort ' + str(badport)
run_cmd(cmd) run_cmd(cmd)
if linux:
cmd = "nft add rule ip ippigeon output ip daddr " + badip + " " + badproto.lower() + " dport " + str(badport) + " drop"
run_cmd(cmd)
settings["badapps"] = list()
if settings["applyconfig"] == True: if settings["applyconfig"] == True:
@ -218,7 +257,6 @@ def mainloop(pool):
yaml.dump(config, filewrite) yaml.dump(config, filewrite)
fprint("Config saved!", settings) fprint("Config saved!", settings)
sleep(interval / (interval * config["core"]["clockspeed"])) sleep(interval / (interval * config["core"]["clockspeed"]))
counter += 1 counter += 1
if counter == interval * config["core"]["clockspeed"]: if counter == interval * config["core"]["clockspeed"]:
@ -259,6 +297,7 @@ if __name__ == '__main__':
interval = config['core']['interval'] interval = config['core']['interval']
displaydata = manager.list(range(2)) # data to be printed displaydata = manager.list(range(2)) # data to be printed
settings = manager.dict() # configuration settings = manager.dict() # configuration
# setup shared data variables
settings["login"] = False settings["login"] = False
settings["loggedin"] = False settings["loggedin"] = False
settings["showui"] = False settings["showui"] = False
@ -274,20 +313,41 @@ if __name__ == '__main__':
settings["fwll"] = 0 settings["fwll"] = 0
settings["running"] = config["core"]["autostart"] settings["running"] = config["core"]["autostart"]
settings["newdata"] = False settings["newdata"] = False
settings["appendbad"] = list()
settings["logMsg"] = list() settings["logMsg"] = list()
settings["whitelist"] = list()
tmp = list()
tmp.append(["N/A", "TCP", "N/A", "N/A", "20.112.52.29", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "20.81.111.85", "80", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "174.143.130.167", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "216.47.134.203", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
tmp.append(["N/A", "TCP", "N/A", "N/A", "34.111.83.189", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
settings["appendbad"] = tmp
# connections blocked, total connections allowed, count of data sent, data recieved, ratio blocked-unblocked
settings["stats"] = [0, 0, 0, 0, 0.0]
killme = manager.Value('d', 0) killme = manager.Value('d', 0)
#killme = False #killme = False
# launch background UI app as process # launch background UI app as process
util.clear_fwll() util.clear_fwll()
p = Process(target=taskbartool.background, args=(displaydata,settings,killme)) p = Process(target=taskbartool.background, args=(displaydata,settings,killme))
p.start() p.start()
#p.join() # not a foreground job, so let's not join it #p.join() # not a foreground job, so let's not join it
keeprunning = True keeprunning = True
# initial setup # initial setup
#netdata_res = pool.apply_async(netstat, callback=netstat_done) #netdata_res = pool.apply_async(netstat, callback=netstat_done)
if linux:
# clear existing nftables entries, create new table
run_cmd("nft delete table ip ippigeon")
run_cmd("nft create table ip ippigeon")
run_cmd("nft add chain ip ippigeon output { type filter hook output priority 0 \; policy accept\; }")
#run_cmd("nft add chain ippigeon filter")
pool.apply_async(util.read_stats, callback=readstat_done)
# launch loop - non-blocking! # launch loop - non-blocking!
counter = 0 counter = 0
while(keeprunning): while(keeprunning):

BIN
music.mp3 Normal file

Binary file not shown.

View File

@ -4,6 +4,7 @@ from util import fprint
from util import run_cmd from util import run_cmd
from util import win32 from util import win32
from util import linux from util import linux
from util import macos
from ippigeon import settings from ippigeon import settings
import util import util
import time import time
@ -18,7 +19,7 @@ def process(data):
#output = data.stdout #output = data.stdout
#print(output) #print(output)
output = data.stdout.decode().split('\r\n') # split stdout into lines output = data.stdout.decode().split('\r\n') # split stdout into lines
print("data", data) #print(output)
if output[0].find("The requested operation requires elevation.") >= 0: if output[0].find("The requested operation requires elevation.") >= 0:
#print("test3") #print("test3")
raise PermissionError("Unable to acquire netstat data without admin!") raise PermissionError("Unable to acquire netstat data without admin!")
@ -109,6 +110,43 @@ def process(data):
writer.writerows(output2) writer.writerows(output2)
fprint("done creating csv", settings) fprint("done creating csv", settings)
if macos:
output = data.stdout.decode().split('\n') # split stdout into lines
#output = data.stdout.decode().split(',')
#fprint("output data: " + str(output))
output = [i for i in output if i]
if output[0].find("Not all processes could be identified") >= 0:
fprint("Not enough permissions", settings)
raise PermissionError("Unable to acquire netstat data without admin!")
output2 = list()
output2.append([util.sysid, util.userid, util.sysdom, util.time()]) # add metadata
for line in output:
string_split = line.split(" ")
string_split = [i for i in string_split if i]
#fprint("Input: " + str(string_split))
if string_split[1].find("Multipath") >= 0:
break
if string_split[0].find("Active") >= 0 or string_split[0].find("Proto") >= 0:
continue
if len(string_split) == 10: # no connection status
#fprint(string_split)
string_split.append(string_split[-1])
string_split[-7] = "UNKNOWN"
string_split[-4] = string_split[-5]
#fprint(string_split)
output2.append(["Unknown", string_split[0], string_split[3], string_split[4], string_split[5], string_split[8]])
#fprint("FINAL CSV: " + str(output2))
with open(find_data_file(util.datafile), "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(output2)
fprint("done creating csv", settings)
def start(): def start():
setup_child() setup_child()
@ -124,3 +162,8 @@ def start():
fprint("data acquired", settings) fprint("data acquired", settings)
return data return data
if macos:
data = run_cmd("netstat -anv")
fprint("data acquired", settings)
return data

19
notification.py Normal file
View File

@ -0,0 +1,19 @@
from plyer import notification #for getting notification on your PC
import sys
from playsound import playsound #pip install playsound==1.2.2
from util import find_data_file
relative_path = (sys.path[0])
sound = find_data_file('music.mp3')
def send_notification(msg="Test notification"):
notification.notify(
title = "IP Pigeon",
message = msg,
app_name = "IPPigeon",
app_icon = find_data_file("icon.ico"),
timeout = 5 #number of seconds message is displayed
)
playsound(sound)
if __name__ == '__main__':
send_notification()

212
panel.py
View File

@ -1,7 +1,7 @@
import glob import glob
import wx
import logging import logging
import sys import sys
import wx
import wx.lib.buttons as buttons import wx.lib.buttons as buttons
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -42,7 +42,7 @@ class OtherFrame(wx.Frame):
Class used for creating frames other than the main one Class used for creating frames other than the main one
""" """
def __init__(self): def __init__(self):
wx.Frame.__init__(self, None, -1, 'Server Panel', size=(1000, 620)) wx.Frame.__init__(self, None, -1, 'Server Panel', size=(1000, 675))
panel = ServerPanel(self) panel = ServerPanel(self)
self.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO)) self.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO))
@ -89,12 +89,12 @@ class LoginPanel(wx.Panel):
vbox.Add(pwdText, 0, wx.ALIGN_CENTER | 100, 5) vbox.Add(pwdText, 0, wx.ALIGN_CENTER | 100, 5)
login_button = wx.Button(self, label='Login') login_button = wx.Button(self, label='Login')
login_button.Bind(wx.EVT_BUTTON, self.on_login) login_button.Bind(wx.EVT_BUTTON, self.on_login)
help_button = wx.Button(self, label='Help') #help_button = wx.Button(self, label='Help')
help_button.Bind(wx.EVT_BUTTON, self.on_help) #help_button.Bind(wx.EVT_BUTTON, self.on_help)
# signup_button = wx.Button(self, label='Sign Up') # signup_button = wx.Button(self, label='Sign Up')
hbox.Add(login_button, 0, wx.ALL | 200, 20) hbox.Add(login_button, 0, wx.ALL | 200, 20)
# hbox.Add(signup_button, 0, wx.ALL | 200, 20) # hbox.Add(signup_button, 0, wx.ALL | 200, 20)
hbox.Add(help_button, 0, wx.ALL | 200, 20) #hbox.Add(help_button, 0, wx.ALL | 200, 20)
vbox.Add(hbox, 0, wx.ALIGN_CENTER | 100, 5) vbox.Add(hbox, 0, wx.ALIGN_CENTER | 100, 5)
self.SetSizer(vbox) self.SetSizer(vbox)
@ -103,6 +103,9 @@ class LoginPanel(wx.Panel):
settings["username"] = basicText.GetValue() settings["username"] = basicText.GetValue()
settings["password"] = pwdText.GetValue() settings["password"] = pwdText.GetValue()
settings["login"] = True settings["login"] = True
#self.Destroy()
self.Close()
self.Parent.Close()
#OtherFrame() #OtherFrame()
def on_help(self, event): def on_help(self, event):
@ -144,7 +147,11 @@ class ServerPanel(wx.Panel):
super().__init__(parent, size=(500, 500)) super().__init__(parent, size=(500, 500))
#self.SetBackgroundColour((44, 51, 51)) #self.SetBackgroundColour((44, 51, 51))
self.main_sizer = wx.BoxSizer(wx.VERTICAL) self.main_sizer = wx.BoxSizer(wx.VERTICAL)
secondary_sizer = wx.BoxSizer(wx.HORIZONTAL) self.secondary_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sub_sizer_left = wx.BoxSizer(wx.VERTICAL)
self.sub_sizer_right = wx.BoxSizer(wx.VERTICAL)
self.sub_sizer_stats = wx.BoxSizer(wx.VERTICAL)
self.sub_sizer_filter = wx.BoxSizer(wx.VERTICAL)
self.row_obj_dict = {} self.row_obj_dict = {}
self.list_ctrl = wx.ListCtrl( self.list_ctrl = wx.ListCtrl(
self, size=(-1, 400), self, size=(-1, 400),
@ -161,63 +168,127 @@ class ServerPanel(wx.Panel):
else: else:
txt = "Status: Not running" txt = "Status: Not running"
if settings["loggedin"] == True:
logintxt = "Logged in"
else:
logintxt = "Not logged in"
stattxt = "Statistics:\nBlocked Connections: " + str(settings["stats"][0]) + " \nAllowed Connections: " + str(settings["stats"][1]) + " \nUpload count: " + str(settings["stats"][2]) + " \nDownload count: " + str(settings["stats"][3]) + " \nBlock ratio: " + str("{:.4f}".format(settings["stats"][4]))
self.statustext = wx.StaticText(self, label=txt) # pos=(20,20)) self.statustext = wx.StaticText(self, label=txt) # pos=(20,20))
self.logintext = wx.StaticText(self, label=logintxt) # pos=(20,20))
self.stattext = wx.StaticText(self, label=stattxt)
#self.main_sizer.Add(tb) #self.main_sizer.Add(tb)
#main_sizer.SetBackgroundColour((44, 51, 51)) #main_sizer.SetBackgroundColour((44, 51, 51))
# self.pnl1.SetBackgroundColour(wx.BLACK) # self.pnl1.SetBackgroundColour(wx.BLACK)
self.handle_columns() self.handle_columns()
#self.updatedata() #self.updatedata()
wx.CallAfter(self.updatedata)
wx.GetApp().ProcessPendingEvents() self.main_sizer.Add(self.list_ctrl, 1, wx.ALL | wx.EXPAND, 20)
self.main_sizer.Add(self.list_ctrl, 0, wx.ALL | wx.EXPAND, 20) self.main_sizer.Add(self.secondary_sizer, 0, wx.ALL | wx.EXPAND, 20)
start_button = wx.Button(self, label='Start IPPigeon') self.secondary_sizer.Add(self.sub_sizer_left, 0, wx.ALL | wx.CENTER, 20)
start_button.SetBackgroundColour((205, 215, 206)) self.secondary_sizer.Add(self.sub_sizer_right, 0, wx.ALL | wx.CENTER, 20)
start_button.Bind(wx.EVT_BUTTON, self.on_start) self.secondary_sizer.Add(self.sub_sizer_filter, 0, wx.ALL | wx.CENTER, 20)
self.secondary_sizer.Add(self.sub_sizer_stats, 0, wx.ALL | wx.CENTER, 20)
self.start_button = wx.Button(self, label='Start IPPigeon')
self.start_button.SetBackgroundColour((205, 215, 206))
self.start_button.Bind(wx.EVT_BUTTON, self.on_start)
#start_button.Bind(wx.EVT_ENTER_WINDOW, self.on_start) #start_button.Bind(wx.EVT_ENTER_WINDOW, self.on_start)
self.stop_button = wx.Button(self, label='Stop IPPigeon')
self.stop_button.SetBackgroundColour('#F08080')
self.secondary_frame_button = wx.Button(self, label='Settings')
self.secondary_frame_button.Bind(wx.EVT_BUTTON, self.on_window)
# wx.BORDER(stop_button, wx.BORDER_NONE)
self.stop_button.Bind(wx.EVT_BUTTON, self.on_stop)
stop_button = wx.Button(self, label='Stop IPPigeon') self.login_button = wx.Button(self, label='Login')
stop_button.SetBackgroundColour('#F08080') #self.login_button.SetBackgroundColour((205, 215, 206))
self.login_button.Bind(wx.EVT_BUTTON, self.on_login)
self.quit_button = wx.Button(self, label='Quit IPPigeon')
#self.login_button.SetBackgroundColour((205, 215, 206))
self.quit_button.Bind(wx.EVT_BUTTON, self.on_quit)
secondary_frame_button = wx.Button(self, label='Settings') self.blacklist_button = wx.Button(self, label='Add to blacklist')
secondary_frame_button.Bind(wx.EVT_BUTTON, self.on_window) #self.login_button.SetBackgroundColour((205, 215, 206))
self.blacklist_button.Bind(wx.EVT_BUTTON, self.on_blacklist)
log_button = wx.Button(self, label='Logs') self.whitelist_button = wx.Button(self, label='Add to whitelist')
log_button.Bind(wx.EVT_BUTTON, self.on_log) #self.login_button.SetBackgroundColour((205, 215, 206))
self.whitelist_button.Bind(wx.EVT_BUTTON, self.on_whitelist)
self.log_button = wx.Button(self, label='Logs')
self.log_button.Bind(wx.EVT_BUTTON, self.on_log)
#self.log_window = wx.LogWindow(self, 'Log Window', True) #self.log_window = wx.LogWindow(self, 'Log Window', True)
#show_log_button = wx.Button(self, wx.NewId(), 'Show Log') #show_log_button = wx.Button(self, wx.NewId(), 'Show Log')
#show_log_button.Bind(wx.EVT_BUTTON, self._show_log) #show_log_button.Bind(wx.EVT_BUTTON, self._show_log)
# wx.BORDER(stop_button, wx.BORDER_NONE)
stop_button.Bind(wx.EVT_BUTTON, self.on_stop)
self.main_sizer.Add(self.statustext, 0, wx.CENTER | wx.ALL | 100, 5)
self.main_sizer.Add(start_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.main_sizer.Add(stop_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.main_sizer.Add(secondary_frame_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.SetSizer(self.main_sizer)
self.sub_sizer_right.Add(self.statustext, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_right.Add(self.logintext, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_left.Add(self.start_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_left.Add(self.stop_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_left.Add(self.secondary_frame_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_left.Add(self.log_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_right.Add(self.login_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_right.Add(self.quit_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_stats.Add(self.stattext, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_filter.Add(self.blacklist_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.sub_sizer_filter.Add(self.whitelist_button, 0, wx.CENTER | wx.ALL | 100, 5)
self.SetSizer(self.main_sizer)
self.start_button.Enable(enable=settings["loggedin"])
self.stop_button.Enable(enable=settings["loggedin"])
self.secondary_frame_button.Enable(enable=settings["loggedin"])
wx.CallAfter(self.updatedata)
wx.GetApp().ProcessPendingEvents()
def handle_columns(self): def handle_columns(self):
for col in range(len(COLUMN_NAMES)): for col in range(len(COLUMN_NAMES)):
self.list_ctrl.InsertColumn(0, COLUMN_NAMES[col], width=COLUMN_SIZES[col]) self.list_ctrl.InsertColumn(0, COLUMN_NAMES[col], width=COLUMN_SIZES[col])
def checklogin(self):
self.start_button.Enable(enable=settings["loggedin"])
self.stop_button.Enable(enable=settings["loggedin"])
self.secondary_frame_button.Enable(enable=settings["loggedin"])
self.quit_button.Enable(enable=settings["loggedin"])
if self.list_ctrl.GetFirstSelected() < 0:
self.blacklist_button.Enable(enable=False)
self.whitelist_button.Enable(enable=False)
else:
self.blacklist_button.Enable(enable=settings["loggedin"])
self.whitelist_button.Enable(enable=settings["loggedin"])
def updatedata(self): def updatedata(self):
global settings global settings
if settings["running"] == True: if settings["running"] == True:
txt = "Status: Running (" + str(settings["config"]["core"]["level"]) + ")" txt = "Status: Running (" + str(settings["config"]["core"]["level"]) + ")"
else: else:
txt = "Status: Not running" txt = "Status: Not running"
#self.list_ctrl.SetSize(self.GetSize()[0] - 50, self.GetSize()[1] - 200)
self.checklogin()
if settings["loggedin"] == True:
logintxt = " Logged in"
self.login_button.SetLabelText("Logout")
else:
self.login_button.SetLabelText("Login")
logintxt = "Not logged in"
stattxt = "Statistics:\nBlocked Connections: " + str(settings["stats"][0]) + " \nAllowed Connections: " + str(settings["stats"][1]) + " \nUpload count: " + str(settings["stats"][2]) + " \nDownload count: " + str(settings["stats"][3]) + " \nBlock ratio: " + str("{:.4f}".format(settings["stats"][4]))
self.statustext.SetLabel(txt)
self.logintext.SetLabel(logintxt)
self.stattext.SetLabel(stattxt)
if settings["newdata"] == True: if settings["newdata"] == True:
settings["newdata"] = False settings["newdata"] = False
else: else:
wx.CallLater(1000, self.updatedata) wx.CallLater(100, self.updatedata)
return return
fprint("updatedata called", settings) fprint("updatedata called", settings)
loaddata() loaddata()
#logData.doLogging(logData) list_total = self.list_ctrl.GetItemCount()
list_top = self.list_ctrl.GetTopItem()
list_pp = self.list_ctrl.GetCountPerPage()
list_bottom = min(list_top + list_pp, list_total - 1)
if self.list_ctrl.DeleteAllItems(): if self.list_ctrl.DeleteAllItems():
fprint("Items deleted", settings) fprint("Items deleted", settings)
else: else:
@ -226,10 +297,11 @@ class ServerPanel(wx.Panel):
if str(TEST_FILE.iloc[i, 4]).find("TIME_WAIT") >= 0 or str(TEST_FILE.iloc[i, 4]).find("FIN_WAIT_2") >= 0: if str(TEST_FILE.iloc[i, 4]).find("TIME_WAIT") >= 0 or str(TEST_FILE.iloc[i, 4]).find("FIN_WAIT_2") >= 0:
continue continue
idx = 0 idx = 0
for ip in settings["badips"]: for app in settings["badapps"]:
#fprint(pid) #fprint(pid)
idx = i idx = i
if str(TEST_FILE.iloc[i, 3]).find(ip) >= 0 and str(TEST_FILE.iloc[i, 4]).find("TIME_WAIT") < 0: # "bad" pid, highlight in red at the top if TEST_FILE.iloc[i, 5] == app: # "bad" pid, highlight in red at the top
fprint("BAD APP UI: " + str(app), settings)
idx = self.list_ctrl.InsertItem(0, TEST_FILE.iloc[i, 0]) idx = self.list_ctrl.InsertItem(0, TEST_FILE.iloc[i, 0])
self.list_ctrl.SetItemBackgroundColour(idx, wx.Colour(200, 51, 51)) self.list_ctrl.SetItemBackgroundColour(idx, wx.Colour(200, 51, 51))
break break
@ -243,10 +315,12 @@ class ServerPanel(wx.Panel):
#fprint(str(idx) + " " + str(TEST_FILE.iloc[i, 0])) #fprint(str(idx) + " " + str(TEST_FILE.iloc[i, 0]))
self.list_ctrl.SetItem(idx, j, str(TEST_FILE.iloc[i, j])) self.list_ctrl.SetItem(idx, j, str(TEST_FILE.iloc[i, j]))
#fprint(i, j, TEST_FILE.iloc[i, j]) #fprint(i, j, TEST_FILE.iloc[i, j])
#self.SetSizer(self.main_sizer) #self.SetSizer(self.main_sizer)
self.statustext.SetLabel(txt)
wx.CallLater(1000, self.updatedata) self.list_ctrl.EnsureVisible((list_bottom - 1))
wx.CallLater(100, self.updatedata)
def on_start(self, event): def on_start(self, event):
fprint('in on_start', settings) fprint('in on_start', settings)
@ -262,6 +336,47 @@ class ServerPanel(wx.Panel):
dg = GetData(parent = None) dg = GetData(parent = None)
dg.ShowModal() dg.ShowModal()
def on_login(self, event):
fprint("open login", settings)
if settings["loggedin"] == True:
settings["loggedin"] = False
return
dg2 = ServerFrame()
#dg2.ShowModal()
def on_quit(self, event):
global killme
killme.value += 1
self.Close()
self.Parent.Close()
def on_blacklist(self, event):
global settings
tmp = settings["appendbad"]
idx = self.list_ctrl.GetFirstSelected()
if idx < 0:
return
proto = self.list_ctrl.GetItem(idx, 1).GetText()
dest = self.list_ctrl.GetItem(idx, 3).GetText()
destip, destport = dest.split(":")
fprint([proto, destip, destport], settings)
tmp.append(["N/A", proto, "N/A", "N/A", destip, destport, "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"])
settings["appendbad"] = tmp
def on_whitelist(self, event):
global settings
tmp = settings["whitelist"]
idx = self.list_ctrl.GetFirstSelected()
if idx < 0:
return
proto = self.list_ctrl.GetItem(idx, 1).GetText()
dest = self.list_ctrl.GetItem(idx, 3).GetText()
pid = self.list_ctrl.GetItem(idx, 5).GetText()
destip, destport = dest.split(":")
fprint([proto, destip, destport], settings)
tmp.append([destip, destport, int(pid)])
settings["whitelist"] = tmp
def on_log(self, event): def on_log(self, event):
fprint("open log", settings) fprint("open log", settings)
dg = logData(parent = None) dg = logData(parent = None)
@ -270,8 +385,6 @@ class ServerPanel(wx.Panel):
#fprint("did logLoop work?", settings) #fprint("did logLoop work?", settings)
dg.ShowModal() dg.ShowModal()
def ShowImage(self, imageFile): def ShowImage(self, imageFile):
if imageFile == "": if imageFile == "":
self.bitmap = wx.StaticBitmap(self, -1, size=(0, 0)) self.bitmap = wx.StaticBitmap(self, -1, size=(0, 0))
@ -299,20 +412,23 @@ def openwindow(data, sets, kill):
killme = kill killme = kill
displaydata = data displaydata = data
settings = sets settings = sets
if settings["loggedin"]: #if settings["loggedin"]:
fprint("Creating server panel", settings) fprint("Creating server panel", settings)
loaddata() loaddata()
app = wx.App(False) app = wx.App(False)
frame = OtherFrame() frame = OtherFrame()
frame.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO)) frame.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO))
app.MainLoop() app.MainLoop()
else: #else:
fprint("Creating login panel", settings) #fprint("Creating login panel")
#app = wx.App(False)
#frame = ServerFrame()
#frame.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO))
#app.MainLoop()
app = wx.App(False)
frame = ServerFrame()
frame.SetIcon(wx.Icon(find_data_file("icon.ico"), wx.BITMAP_TYPE_ICO))
app.MainLoop()
class RedirectText(object): class RedirectText(object):
def __init__(self,aWxTextCtrl): def __init__(self,aWxTextCtrl):
self.out = aWxTextCtrl self.out = aWxTextCtrl
@ -331,20 +447,25 @@ class logData(wx.Dialog):
#keepLoop = False #keepLoop = False
def logLoop(self): def logLoop(self):
self.logs.WriteText("before while loop\n") #self.logs.WriteText("before while loop\n")
i=0 i=0
if (settings["logMsg"] is not None): #self.logs.Clear()
tmp = settings["logMsg"]
if (tmp is not None):
while(i < len(settings["logMsg"])): while(i < len(settings["logMsg"])):
#for i in range(len(settings["logMsg"])): #for i in range(len(settings["logMsg"])):
#self.logs.WriteText("in while loop\n") #self.logs.WriteText("in while loop\n")
self.logs.WriteText(settings["logMsg"][i]) self.logs.WriteText(settings["logMsg"][i])
self.logs.WriteText("\n") self.logs.WriteText("\n")
tmp.remove(settings["logMsg"][i])
i+=1 i+=1
else: else:
self.logs.WriteText("settings is none :(") #self.logs.WriteText("settings is none :(")
settings["logMsg"].add("init string for list") tmp.add("init string for list")
logData.logLoop(self) logData.logLoop(self)
self.logs.WriteText("after while loop\n") settings["logMsg"] = tmp
wx.CallLater(100, self.logLoop)
#self.logs.WriteText("after while loop\n")
@ -358,7 +479,6 @@ class logData(wx.Dialog):
#self.logs.WriteText("hi from doLogging\n") #self.logs.WriteText("hi from doLogging\n")
#self.logs.WriteText(settings["logMsg"]) #self.logs.WriteText(settings["logMsg"])
class GetData(wx.Dialog): class GetData(wx.Dialog):
def __init__(self, parent): def __init__(self, parent):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "User Settings: ", size = (800,500)) wx.Dialog.__init__(self, parent, wx.ID_ANY, "User Settings: ", size = (800,500))

View File

@ -6,3 +6,5 @@ pandas
pyyaml pyyaml
numpy numpy
bcrypt bcrypt
plyer
playsound==1.2.2

View File

@ -5,7 +5,7 @@ debug = True
debug = not debug debug = not debug
# Dependencies are automatically detected, but it might need fine tuning. # Dependencies are automatically detected, but it might need fine tuning.
# "packages": ["os"] is used as example only # "packages": ["os"] is used as example only
build_exe_options = {"packages": ["os"], "excludes": ["tkinter"], "include_msvcr": True, "include_files": ["icon.png", "config.yml", "keyfile-admin.pem", "WXPython.png", "WXPython_30x30.png", "settings.png", "settings_30x30.png", "icon.ico"], "optimize": 1} build_exe_options = {"packages": ["os", "plyer.platforms.win.notification"], "excludes": ["tkinter"], "include_msvcr": True, "include_files": ["icon.png", "config.yml", "keyfile-admin.pem", "WXPython.png", "WXPython_30x30.png", "settings.png", "settings_30x30.png", "icon.ico", "music.mp3"], "optimize": 1}
# base="Win32GUI" should be used only for Windows GUI app # base="Win32GUI" should be used only for Windows GUI app
base = None base = None

14
ssh.py
View File

@ -3,13 +3,21 @@ from fabric import Connection
from util import find_data_file from util import find_data_file
from util import setup_child from util import setup_child
from util import fprint from util import fprint
from util import macos
from invoke import exceptions from invoke import exceptions
import sys import sys
c = None
settings = None settings = None
def sftp_send_data(config, filename, filetype): def sftp_send_data(config, filename, filetype):
setup_child() setup_child()
if not macos:
fprint("Connecting over SSH to " + config['sftp']['host'], settings) fprint("Connecting over SSH to " + config['sftp']['host'], settings)
global c
if c is None:
c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),}) c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),})
fprint("Sending data over SFTP: " + filename, settings) fprint("Sending data over SFTP: " + filename, settings)
fprint(c.put(find_data_file(filename), remote=config['sftp']['filepath'][filetype]), settings) fprint(c.put(find_data_file(filename), remote=config['sftp']['filepath'][filetype]), settings)
fprint("Data sent over SFTP successfully", settings) fprint("Data sent over SFTP successfully", settings)
@ -19,6 +27,8 @@ def sftp_send_data(config, filename, filetype):
def check_for_file(config, filename, location): def check_for_file(config, filename, location):
setup_child() setup_child()
fprint("Connecting over SSH to " + config['sftp']['host'], settings) fprint("Connecting over SSH to " + config['sftp']['host'], settings)
global c
if c is None:
c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),}) c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),})
fprint("Checking for existence of file " + config['sftp']['filepath'][location] + "/" + filename, settings) fprint("Checking for existence of file " + config['sftp']['filepath'][location] + "/" + filename, settings)
try: try:
@ -30,7 +40,9 @@ def check_for_file(config, filename, location):
def run_ssh(config, command, location): def run_ssh(config, command, location):
setup_child() setup_child()
fprint("Connecting over SSH to " + config['sftp']['host']) fprint("Connecting over SSH to " + config['sftp']['host'], settings)
global c
if c is None:
c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),}) c = Connection(host=config['sftp']['host'], user=config['sftp']['user'], port=config['sftp']['port'], connect_kwargs={"key_filename": find_data_file(config['sftp']['keyfile']),})
fprint("cd to " + config['sftp']['filepath'][location], settings) fprint("cd to " + config['sftp']['filepath'][location], settings)
with c.cd(config['sftp']['filepath'][location]): with c.cd(config['sftp']['filepath'][location]):

60
util.py
View File

@ -6,14 +6,17 @@ from sys import platform
import time as t import time as t
from time import sleep from time import sleep
import uuid import uuid
import csv
logMsg = ""
logCont = ""
win32 = platform == "win32" win32 = platform == "win32"
linux = platform == "linux" or platform == "linux2" linux = platform == "linux" or platform == "linux2"
macos = platform == "darwin" macos = platform == "darwin"
datafile = "" datafile = ""
logMsg = ""
logCont = ""
settings = None settings = None
if win32: if win32:
sysid = hex(uuid.getnode()) sysid = hex(uuid.getnode())
datafile += sysid datafile += sysid
@ -28,7 +31,7 @@ if win32:
_, username = res.strip().rsplit("\n", 1) _, username = res.strip().rsplit("\n", 1)
userid, sysdom = username.rsplit("\\", 1) userid, sysdom = username.rsplit("\\", 1)
if linux: if linux or macos:
sysid = hex(uuid.getnode()) sysid = hex(uuid.getnode())
#fprint(sysid) #fprint(sysid)
datafile += sysid datafile += sysid
@ -44,14 +47,17 @@ def time():
def kill(pid): def kill(pid):
setup_child() setup_child()
if pid > 0: try:
if pid > 4:
fprint("Killing PID " + str(pid), settings) fprint("Killing PID " + str(pid), settings)
os.kill(int(pid), 9) os.kill(int(pid), 9)
fprint("Signal 9 sent to PID " + str(pid), settings) fprint("Signal 9 sent to PID " + str(pid), settings)
except:
fprint("Unable to kill " + str(pid), settings)
def fprint(msg, settings = None): def fprint(msg, settings = None):
#if not getattr(sys, "frozen", False): #if not getattr(sys, "frozen", False):
setup_child(settings) setup_child()
try: try:
frm = inspect.stack()[1] frm = inspect.stack()[1]
@ -66,7 +72,7 @@ def fprint(msg, settings = None):
except Exception as e: except Exception as e:
try: try:
print('[????:' + frm.function + ']:', str(msg)) print('[????:' + frm.function + ']:', str(msg))
print(e) print('[util:fprint]: ' + str(e))
except: except:
print('[????]:', str(msg)) print('[????]:', str(msg))
@ -100,7 +106,7 @@ def run_cmd(cmd):
fprint("ran PS command successfully", settings) fprint("ran PS command successfully", settings)
#completed = subprocess.run(["powershell", "-WindowStyle", "hidden", "-Command", cmd], capture_output=True, startupinfo=startupinfo) #completed = subprocess.run(["powershell", "-WindowStyle", "hidden", "-Command", cmd], capture_output=True, startupinfo=startupinfo)
return completed return completed
if linux: if linux or macos:
fprint("running sh command: " + cmd, settings) fprint("running sh command: " + cmd, settings)
completed = subprocess.run(["sh", "-c", cmd], capture_output=True) completed = subprocess.run(["sh", "-c", cmd], capture_output=True)
fprint("ran sh command successfully", settings) fprint("ran sh command successfully", settings)
@ -113,20 +119,12 @@ def setup_child(sets=None):
if sets is not None: if sets is not None:
settings = sets settings = sets
class Logger(object): class Logger(object):
def __init__(self, filename="output.log"): def __init__(self, filename="output.log"):
self.string = ""
self.log = open(filename, "a") self.log = open(filename, "a")
self.terminal = sys.stdout self.terminal = sys.stdout
def write(self, message): def write(self, message):
self.string += message
self.string += "\n"
self.log.write(message) self.log.write(message)
#close(filename) #close(filename)
#self.log = open(filename, "a") #self.log = open(filename, "a")
@ -141,3 +139,35 @@ class Logger(object):
def clear_fwll(): def clear_fwll():
if win32: if win32:
run_cmd('Remove-NetFirewallRule -Group "IPPigeon"') run_cmd('Remove-NetFirewallRule -Group "IPPigeon"')
if linux:
run_cmd("nft delete table ip ippigeon")
def write_stats(stats):
fprint("Writing stats", settings)
tmp = list()
tmp.append(["connections blocked", "connections allowed", "data uploaded", "data recieved", "block ratio"])
tmp.append(stats)
with open(find_data_file("stats.csv"), "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(tmp)
fprint("Done writing stats", settings)
def read_stats():
with open(find_data_file("stats.csv"), newline='') as csvfile:
csvreader = csv.reader(csvfile, delimiter=',', quotechar='|')
header = True
fprint(csvreader, settings)
data = list()
for line in csvreader:
fprint(line, settings)
if header:
header = False
continue
data = line
for idx in range(len(data) - 1):
data[idx] = int(data[idx])
data[len(data) - 1] = float(data[len(data) - 1])
return data