Add logger

This commit is contained in:
Arinerron 2018-01-25 23:22:48 -08:00
parent 033290656a
commit 197d47eb7d
No known key found for this signature in database
GPG Key ID: 99383627861C62F0
9 changed files with 232 additions and 78 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ __pycache__/
onionr/data/config.ini
onionr/data/*.db
onionr/data-old/*
onionr/*.pyc
onionr/*.log

View File

@ -20,7 +20,7 @@
import flask
from flask import request, Response, abort
from multiprocessing import Process
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, gnupg, os
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, gnupg, os, logging
from core import Core
import onionrutils
@ -42,10 +42,13 @@ class API:
This also saves the used host (random localhost IP address) to the data folder in host.txt
'''
if os.path.exists('dev-enabled'):
print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
logger.set_level(logger.LEVEL_INFO)
self.config = config
self.debug = debug
self._privateDelayTime = 3
@ -55,13 +58,13 @@ class API:
bindPort = int(self.config['CLIENT']['PORT'])
self.bindPort = bindPort
self.clientToken = self.config['CLIENT']['CLIENT HMAC']
print(self.clientToken)
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
if not debug and not self._developmentMode:
hostNums = [random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)]
self.host = '127.' + str(hostNums[0]) + '.' + str(hostNums[1]) + '.' + str(hostNums[2])
else:
self.host = '127.0.0.1'
self.host = '127.0.0.1'
hostFile = open('data/host.txt', 'w')
hostFile.write(self.host)
hostFile.close()
@ -80,11 +83,11 @@ class API:
resp.headers['Access-Control-Allow-Origin'] = '*'
else:
resp.headers['server'] = 'Onionr'
resp.headers['content-type'] = 'text/plain'
resp.headers['Content-Type'] = 'text/plain'
resp.headers["Content-Security-Policy"] = "default-src 'none'"
resp.headers['x-frame-options'] = 'deny'
resp.headers['X-Frame-Options'] = 'deny'
return resp
@app.route('/client/')
def private_handler():
startTime = math.floor(time.time())
@ -101,7 +104,7 @@ class API:
request.environ.get('werkzeug.server.shutdown')()
resp = Response('Goodbye')
elif action == 'stats':
resp = Response('something')
resp = Response('me_irl')
elif action == 'init':
# generate PGP key
self._core.generateMainPGP()
@ -156,17 +159,17 @@ class API:
resp = Response("Invalid request")
return resp
print('Starting client on ' + self.host + ':' + str(bindPort))
print('Client token:', self.clientToken)
logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
logger.debug('Client token: ' + logger.colors.underline + self.clientToken)
app.run(host=self.host, port=bindPort, debug=True, threaded=True)
def validateHost(self, hostType):
''' Validate various features of the request including:
If private (/client/), is the host header local?
If public (/public/), is the host header onion or i2p?
Was x-request-with used?
Was X-Request-With used?
'''
if self.debug:
return
@ -181,7 +184,8 @@ class API:
# Validate x-requested-with, to protect against CSRF/metadata leaks
if not self._developmentMode:
try:
request.headers['x-requested-with']
request.headers['X-Requested-With']
except:
# we exit rather than abort to avoid fingerprinting
sys.exit(1)
logger.debug('Avoiding fingerprinting, exiting...')
sys.exit(1)

View File

@ -19,7 +19,7 @@ and code to operate as a daemon, getting commands from the command queue databas
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sqlite3, requests, hmac, hashlib, time, sys, os
import sqlite3, requests, hmac, hashlib, time, sys, os, logger
import core
class OnionrCommunicate:
def __init__(self, debug, developmentMode):
@ -30,8 +30,7 @@ class OnionrCommunicate:
self._core = core.Core()
blockProcessTimer = 0
blockProcessAmount = 5
if debug:
print('Communicator debugging enabled')
logger.debug('Communicator debugging enabled.')
torID = open('data/hs/hostname').read()
# get our own PGP fingerprint
@ -40,7 +39,7 @@ class OnionrCommunicate:
self._core.generateMainPGP(torID)
with open(fingerprintFile,'r') as f:
self.pgpOwnFingerprint = f.read()
print('My PGP fingerprint is ' + self.pgpOwnFingerprint)
logger.info('My PGP fingerprint is ' + logger.colors.underline + self.pgpOwnFingerprint + logger.colors.reset + logger.colors.fg.green + '.')
while True:
command = self._core.daemonQueue()
@ -51,11 +50,10 @@ class OnionrCommunicate:
self.lookupBlocks()
self._core.processBlocks()
blockProcessTimer = 0
if debug:
print('Communicator daemon heartbeat')
logger.debug('Communicator daemon heartbeat')
if command != False:
if command[0] == 'shutdown':
print('Daemon recieved exit command.')
logger.warn('Daemon recieved exit command.')
break
time.sleep(1)
return
@ -94,11 +92,11 @@ class OnionrCommunicate:
# skip hash if it isn't valid
continue
else:
print('adding', i, 'to hash database')
logger.debug('Adding ' + i + ' to hash database...')
self._core.addToBlockDB(i)
return
def performGet(self, action, peer, data=None, type='tor'):
'''performs a request to a peer through Tor or i2p (currently only tor)'''
if not peer.endswith('.onion') and not peer.endswith('.onion/'):
@ -114,7 +112,7 @@ class OnionrCommunicate:
except requests.exceptions.RequestException:
return False
return r.text
shouldRun = False
debug = False

View File

@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sqlite3, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl
import sqlite3, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger
from Crypto.Cipher import AES
from Crypto import Random
import netcontroller
@ -26,7 +26,7 @@ if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
sys.stderr.write('On Python 3 versions prior to 3.6.x, you need the sha3 module')
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
class Core:
@ -66,7 +66,7 @@ class Core:
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
t = (peerID, name, 'unknown')
c.execute('Insert into peers (id, name, dateSeen) values(?, ?, ?);', t)
c.execute('insert into peers (id, name, dateSeen) values(?, ?, ?);', t)
conn.commit()
conn.close()
return True
@ -295,4 +295,3 @@ class Core:
for i in row:
retData += i
return retData

153
onionr/logger.py Normal file
View File

@ -0,0 +1,153 @@
'''
Onionr - P2P Microblogging Platform & Social network
This file handles all operations involving logging
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import re
class colors:
'''
This class allows you to set the color if ANSI codes are supported
'''
reset='\033[0m'
bold='\033[01m'
disable='\033[02m'
underline='\033[04m'
reverse='\033[07m'
strikethrough='\033[09m'
invisible='\033[08m'
italics='\033[3m'
class fg:
black='\033[30m'
red='\033[31m'
green='\033[32m'
orange='\033[33m'
blue='\033[34m'
purple='\033[35m'
cyan='\033[36m'
lightgrey='\033[37m'
darkgrey='\033[90m'
lightred='\033[91m'
lightgreen='\033[92m'
yellow='\033[93m'
lightblue='\033[94m'
pink='\033[95m'
lightcyan='\033[96m'
class bg:
black='\033[40m'
red='\033[41m'
green='\033[42m'
orange='\033[43m'
blue='\033[44m'
purple='\033[45m'
cyan='\033[46m'
lightgrey='\033[47m'
@staticmethod
def filter(data):
return re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]').sub('', str(data))
'''
Use the bitwise operators to merge these settings
'''
USE_ANSI = 0b100
OUTPUT_TO_CONSOLE = 0b010
OUTPUT_TO_FILE = 0b001
LEVEL_DEBUG = 1
LEVEL_INFO = 2
LEVEL_WARN = 3
LEVEL_ERROR = 4
LEVEL_FATAL = 5
_type = OUTPUT_TO_CONSOLE | USE_ANSI # the default settings for logging
_level = LEVEL_DEBUG # the lowest level to log
_outputfile = './output.log' # the file to log to
'''
Set the settings for the logger using bitwise operators
'''
def set_settings(type):
global _type
_type = type
'''
Get settings from the logger
'''
def get_settings():
return _type
'''
Set the lowest log level to output
'''
def set_level(level):
global _level
_level = level
'''
Get the lowest log level currently being outputted
'''
def get_level():
return _level
'''
Outputs raw data to console without formatting
'''
def raw(data):
if get_settings() & OUTPUT_TO_CONSOLE:
print(data)
if get_settings() & OUTPUT_TO_FILE:
with open(_outputfile, "a+") as f:
f.write(colors.filter(data) + '\n')
'''
Logs the data
prefix : The prefix to the output
data : The actual data to output
color : The color to output before the data
'''
def log(prefix, data, color = ''):
output = colors.reset + str(color) + '[' + colors.bold + str(prefix) + colors.reset + str(color) + '] ' + str(data) + colors.reset
if not get_settings() & USE_ANSI:
output = colors.filter(output)
raw(output)
# debug: when there is info that could be useful for debugging purposes only
def debug(data):
if get_level() <= LEVEL_DEBUG:
log('/', data)
# info: when there is something to notify the user of, such as the success of a process
def info(data):
if get_level() <= LEVEL_INFO:
log('+', data, colors.fg.green)
# warn: when there is a potential for something bad to happen
def warn(data):
if get_level() <= LEVEL_WARN:
log('!', data, colors.fg.orange)
# error: when only one function, module, or process of the program encountered a problem and must stop
def error(data):
if get_level() <= LEVEL_ERROR:
log('-', data, colors.fg.red)
# fatal: when the something so bad has happened that the prorgam must stop
def fatal(data):
if get_level() <= LEVEL_FATAL:
log('#', data, colors.bg.red + colors.fg.black)

View File

@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import subprocess, os, random, sys
import subprocess, os, random, sys, logger
class NetController:
'''NetController
This class handles hidden service setup on Tor and I2P
@ -56,10 +56,10 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
if 'Bootstrapped 100%: Done' in line.decode():
break
elif 'Opening Socks listener' in line.decode():
print(line.decode())
print('Finished starting Tor')
logger.debug(line.decode())
logger.info('Finished starting Tor')
self.readyState = True
myID = open('data/hs/hostname', 'r')
self.myID = myID.read()
myID.close()
return
return

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
'''
Onionr - P2P Microblogging Platform & Social network.
Onionr - P2P Microblogging Platform & Social network.
Onionr is the name for both the protocol and the original/reference software.
@ -20,10 +20,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time
import gui, api, colors, core
import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger
import gui, api, core
from onionrutils import OnionrUtils
from colors import Colors
from netcontroller import NetController
class Onionr:
@ -37,18 +36,18 @@ class Onionr:
except FileNotFoundError:
pass
if os.path.exists('dev-enabled'):
print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
colors = Colors()
logger.set_level(logger.LEVEL_INFO)
self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils(self.onionrCore)
# Get configuration and Handle commands
self.debug = False # Whole application debugging
if os.path.exists('data-encrypted.dat'):
@ -59,12 +58,12 @@ class Onionr:
if os.path.exists('data/'):
break
else:
print('Failed to decrypt: ' + result[1])
logger.error('Failed to decrypt: ' + result[1])
else:
if not os.path.exists('data/'):
os.mkdir('data/')
os.mkdir('data/blocks/')
if not os.path.exists('data/peers.db'):
self.onionrCore.createPeerDB()
pass
@ -94,7 +93,7 @@ class Onionr:
finally:
if command == 'start':
if os.path.exists('.onionr-lock'):
self.onionrUtils.printErr('Cannot start. Daemon is already running, or it did not exit cleanly.\n(if you are sure that there is not a daemon running, delete .onionr-lock & try again).')
logger.fatal('Cannot start. Daemon is already running, or it did not exit cleanly.\n(if you are sure that there is not a daemon running, delete .onionr-lock & try again).')
else:
if not self.debug and not self._developmentMode:
lockFile = open('.onionr-lock', 'w')
@ -110,32 +109,31 @@ class Onionr:
elif command == 'help' or command == '--help':
self.showHelp()
elif command == '':
print('Do', sys.argv[0], ' --help for Onionr help.')
logger.info('Do ' + logger.colors.bold + sys.argv[0] + ' --help' + logger.colors.reset + logger.colors.fg.green + ' for Onionr help.')
else:
print(colors.RED, 'Invalid Command', colors.RESET)
logger.error('Invalid command.')
if not self._developmentMode:
encryptionPassword = self.onionrUtils.getPassword('Enter password to encrypt directory.')
encryptionPassword = self.onionrUtils.getPassword('Enter password to encrypt directory: ')
self.onionrCore.dataDirEncrypt(encryptionPassword)
shutil.rmtree('data/')
return
def daemon(self):
''' Start the Onionr communication daemon
'''
colors = Colors()
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
net = NetController(self.config['CLIENT']['PORT'])
print('Tor is starting...')
logger.info('Tor is starting...')
net.startTor()
print(colors.GREEN + 'Started Tor .onion service: ' + colors.UNDERLINE + net.myID + colors.RESET)
logger.info('Started Tor .onion service: ' + logger.colors.underline + net.myID)
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
print('Started communicator')
logger.debug('Started communicator')
api.API(self.config, self.debug)
return
def killDaemon(self):
'''Shutdown the Onionr Daemon'''
print('Killing the running daemon')
logger.warn('Killing the running daemon')
try:
self.onionrUtils.localCommand('shutdown')
except requests.exceptions.ConnectionError:
@ -149,6 +147,6 @@ class Onionr:
def showHelp(self):
'''Show help for Onionr'''
return
Onionr()
Onionr()

View File

@ -18,12 +18,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
# Misc functions that do not fit in the main api, but are useful
import getpass, sys, requests, configparser, os, socket, gnupg, hashlib
import getpass, sys, requests, configparser, os, socket, gnupg, hashlib, logger
if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
sys.stderr.write('On Python 3 versions prior to 3.6.x, you need the sha3 module')
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
class OnionrUtils:
'''Various useful functions'''
@ -33,7 +33,7 @@ class OnionrUtils:
return
def printErr(self, text='an error occured'):
'''Print an error message to stderr with a new line'''
sys.stderr.write(text + '\n')
logger.error(text)
def localCommand(self, command):
'''Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.'''
config = configparser.ConfigParser()
@ -51,7 +51,7 @@ class OnionrUtils:
print('Confirm password: ')
pass2 = getpass.getpass()
if pass1 != pass2:
print("Passwords do not match.")
logger.error("Passwords do not match.")
input()
else:
break
@ -101,4 +101,4 @@ class OnionrUtils:
int(data, 16)
except ValueError:
retVal = False
return retVal
return retVal

View File

@ -14,18 +14,18 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import unittest, sys, os, base64, tarfile, shutil, simplecrypt
import unittest, sys, os, base64, tarfile, shutil, simplecrypt, logger
class OnionrTests(unittest.TestCase):
def testPython3(self):
if sys.version_info.major != 3:
print(sys.version_info.major)
logger.debug('Python version: ' + sys.version_info.major)
self.assertTrue(False)
else:
self.assertTrue(True)
def testNone(self):
print('--------------------------')
print('Running simple program run test')
logger.debug('--------------------------')
logger.info('Running simple program run test...')
# Test just running ./onionr with no arguments
blank = os.system('./onionr.py')
if blank != 0:
@ -33,8 +33,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(True)
def testPeer_a_DBCreation(self):
print('--------------------------')
print('Running peer db creation test')
logger.debug('--------------------------')
logger.info('Running peer db creation test...')
if os.path.exists('data/peers.db'):
os.remove('data/peers.db')
import core
@ -45,8 +45,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testPeer_b_addPeerToDB(self):
print('--------------------------')
print('Running peer db insertion test')
logger.debug('--------------------------')
logger.info('Running peer db insertion test...')
import core
myCore = core.Core()
if not os.path.exists('data/peers.db'):
@ -58,8 +58,8 @@ class OnionrTests(unittest.TestCase):
def testData_b_Encrypt(self):
self.assertTrue(True)
return
print('--------------------------')
print('Running data dir encrypt test')
logger.debug('--------------------------')
logger.info('Running data dir encrypt test...')
import core
myCore = core.Core()
myCore.dataDirEncrypt('password')
@ -70,8 +70,8 @@ class OnionrTests(unittest.TestCase):
def testData_a_Decrypt(self):
self.assertTrue(True)
return
print('--------------------------')
print('Running data dir decrypt test')
logger.debug('--------------------------')
logger.info('Running data dir decrypt test...')
import core
myCore = core.Core()
myCore.dataDirDecrypt('password')
@ -80,8 +80,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testPGPGen(self):
print('--------------------------')
print('Testing PGP key generation')
logger.debug('--------------------------')
logger.info('Running PGP key generation test...')
if os.path.exists('data/pgp/'):
self.assertTrue(True)
else:
@ -94,8 +94,8 @@ class OnionrTests(unittest.TestCase):
if os.path.exists('data/pgp/'):
self.assertTrue(True)
def testHMACGen(self):
print('--------------------------')
print('running daemon queue test')
logger.debug('--------------------------')
logger.info('Running HMAC generation test...')
# Test if hmac key generation is working
import core
myCore = core.Core()
@ -105,8 +105,8 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testQueue(self):
print('--------------------------')
print('running daemon queue test')
logger.debug('--------------------------')
logger.info('Running daemon queue test...')
# test if the daemon queue can read/write data
import core
myCore = core.Core()