work on exchanging data

This commit is contained in:
Kevin Froman 2018-01-26 00:28:11 -06:00
parent 67a84e2a19
commit 033290656a
No known key found for this signature in database
GPG Key ID: 0D414D0FE405B63B
6 changed files with 152 additions and 11 deletions

26
docs/api.md Normal file
View File

@ -0,0 +1,26 @@
HTTP API
------------------------------------------------
/client/ (Private info, not publicly accessible)
- hello
- hello world
- shutdown
- exit onionr
- stats
- show node stats
/public/
- firstConnect
- initialize with peer
- ping
- pong
- setHMAC
- set a created symmetric key
- getDBHash
- get the hash of the current hash database state
- getPGP
- export node's PGP public key
- getData
- get a data block
-------------------------------------------------

View File

@ -50,7 +50,7 @@ class API:
self.debug = debug self.debug = debug
self._privateDelayTime = 3 self._privateDelayTime = 3
self._core = Core() self._core = Core()
self._utils = onionrutils.OnionrUtils() self._utils = onionrutils.OnionrUtils(self._core)
app = flask.Flask(__name__) app = flask.Flask(__name__)
bindPort = int(self.config['CLIENT']['PORT']) bindPort = int(self.config['CLIENT']['PORT'])
self.bindPort = bindPort self.bindPort = bindPort
@ -127,6 +127,10 @@ class API:
resp = Response("pong!") resp = Response("pong!")
elif action == 'setHMAC': elif action == 'setHMAC':
pass pass
elif action == 'getDBHash':
resp = Response(self._utils.getBlockDBHash())
elif action == 'getBlockHashes':
resp = Response(self._core.getBlockList())
elif action == 'getPGP': elif action == 'getPGP':
resp = Response(self._utils.exportMyPubkey()) resp = Response(self._utils.exportMyPubkey())
# setData should be something the communicator initiates, not this api # setData should be something the communicator initiates, not this api

View File

@ -29,7 +29,7 @@ class OnionrCommunicate:
''' '''
self._core = core.Core() self._core = core.Core()
blockProcessTimer = 0 blockProcessTimer = 0
blockProccesAmount = 5 blockProcessAmount = 5
if debug: if debug:
print('Communicator debugging enabled') print('Communicator debugging enabled')
torID = open('data/hs/hostname').read() torID = open('data/hs/hostname').read()
@ -48,9 +48,9 @@ class OnionrCommunicate:
# Process blocks based on a timer # Process blocks based on a timer
blockProcessTimer += 1 blockProcessTimer += 1
if blockProcessTimer == blockProcessAmount: if blockProcessTimer == blockProcessAmount:
self.lookupBlocks()
self._core.processBlocks() self._core.processBlocks()
blockProcessTimer = 0 blockProcessTimer = 0
if debug: if debug:
print('Communicator daemon heartbeat') print('Communicator daemon heartbeat')
if command != False: if command != False:
@ -79,6 +79,26 @@ class OnionrCommunicate:
'''This function sends the proof result to a peer previously fetched with getPeerProof''' '''This function sends the proof result to a peer previously fetched with getPeerProof'''
return return
def lookupBlocks(self):
'''Lookup blocks and merge new ones'''
peerList = self._core.listPeers()
blocks = ''
for i in peerList:
lastDB = self._core.getPeerInfo(i, 'blockDBHash')
currentDB = self.performGet('getDBHash', i)
if lastDB != currentDB:
blocks += self.performGet('getBlockHashes', i)
blockList = blocks.split('\n')
for i in blockList:
if not self._core.validateHash(i):
# skip hash if it isn't valid
continue
else:
print('adding', i, 'to hash database')
self._core.addToBlockDB(i)
return
def performGet(self, action, peer, data=None, type='tor'): def performGet(self, action, peer, data=None, type='tor'):
'''performs a request to a peer through Tor or i2p (currently only tor)''' '''performs a request to a peer through Tor or i2p (currently only tor)'''
if not peer.endswith('.onion') and not peer.endswith('.onion/'): if not peer.endswith('.onion') and not peer.endswith('.onion/'):

View File

@ -84,6 +84,7 @@ class Core:
name text, name text,
pgpKey text, pgpKey text,
hmacKey text, hmacKey text,
blockDBHash text,
forwardKey text, forwardKey text,
dateSeen not null, dateSeen not null,
bytesStored int, bytesStored int,
@ -108,7 +109,8 @@ class Core:
hash text not null, hash text not null,
dateReceived int, dateReceived int,
decrypted int, decrypted int,
dataFound int dataFound int,
dataSaved int
); );
''') ''')
conn.commit() conn.commit()
@ -229,9 +231,68 @@ class Core:
key = base64.b64encode(os.urandom(32)) key = base64.b64encode(os.urandom(32))
return key return key
def listPeers(self):
'''Return a list of peers
'''
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
peers = c.execute('SELECT * FROM peers;')
peerList = []
for i in peers:
peerList.append(i[0])
conn.close()
return peerList
def processBlocks(self): def processBlocks(self):
''' '''
Work with the block database and download any missing blocks Work with the block database and download any missing blocks
This is meant to be called from the communicator daemon on its timer. This is meant to be called from the communicator daemon on its timer.
''' '''
conn = sqlite3.connect(self.blockDB)
c = conn.cursor()
for i in blocks:
pass
conn.close()
return return
def getPeerInfo(self, peer, info):
'''
get info about a peer
id text 0
name text, 1
pgpKey text, 2
hmacKey text, 3
blockDBHash text, 4
forwardKey text, 5
dateSeen not null, 7
bytesStored int, 8
trust int 9
'''
# Lookup something about a peer from their database entry
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
command = (peer,)
infoNumbers = {'id': 0, 'name': 1, 'pgpKey': 2, 'hmacKey': 3, 'blockDBHash': 4, 'forwardKey': 5, 'dateSeen': 6, 'bytesStored': 7, 'trust': 8}
info = infoNumbers[info]
iterCount = 0
retVal = ''
for row in c.execute('SELECT * from peers where id=?;', command):
for i in row:
if iterCount == info:
retVal = i
break
else:
iterCount += 1
conn.close()
return retVal
def getBlockList(self):
'''get list of our blocks'''
conn = sqlite3.connect(self.blockDB)
c = conn.cursor()
retData = ''
for row in c.execute('SELECT hash FROM hashes;'):
for i in row:
retData += i
return retData

View File

@ -45,7 +45,7 @@ class Onionr:
colors = Colors() colors = Colors()
self.onionrCore = core.Core() self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils() self.onionrUtils = OnionrUtils(self.onionrCore)
# Get configuration and Handle commands # Get configuration and Handle commands
@ -63,6 +63,7 @@ class Onionr:
else: else:
if not os.path.exists('data/'): if not os.path.exists('data/'):
os.mkdir('data/') os.mkdir('data/')
os.mkdir('data/blocks/')
if not os.path.exists('data/peers.db'): if not os.path.exists('data/peers.db'):
self.onionrCore.createPeerDB() self.onionrCore.createPeerDB()
@ -128,7 +129,7 @@ class Onionr:
net.startTor() net.startTor()
print(colors.GREEN + 'Started Tor .onion service: ' + colors.UNDERLINE + net.myID + colors.RESET) print(colors.GREEN + 'Started Tor .onion service: ' + colors.UNDERLINE + net.myID + colors.RESET)
time.sleep(1) time.sleep(1)
subprocess.Popen(["./communicator.py", "run", net.socksPort]) subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
print('Started communicator') print('Started communicator')
api.API(self.config, self.debug) api.API(self.config, self.debug)
return return
@ -149,4 +150,5 @@ class Onionr:
'''Show help for Onionr''' '''Show help for Onionr'''
return return
Onionr() Onionr()

View File

@ -18,11 +18,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. 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 # Misc functions that do not fit in the main api, but are useful
import getpass, sys, requests, configparser, os, socket, gnupg import getpass, sys, requests, configparser, os, socket, gnupg, hashlib
class OnionrUtils(): 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')
sys.exit(1)
class OnionrUtils:
'''Various useful functions''' '''Various useful functions'''
def __init__(self): def __init__(self, coreInstance):
self.fingerprintFile = 'data/own-fingerprint.txt' self.fingerprintFile = 'data/own-fingerprint.txt'
self._core = coreInstance
return return
def printErr(self, text='an error occured'): def printErr(self, text='an error occured'):
'''Print an error message to stderr with a new line''' '''Print an error message to stderr with a new line'''
@ -74,3 +81,24 @@ class OnionrUtils():
fingerprint = f.read() fingerprint = f.read()
ascii_armored_public_keys = gpg.export_keys(fingerprint) ascii_armored_public_keys = gpg.export_keys(fingerprint)
return ascii_armored_public_keys return ascii_armored_public_keys
def getBlockDBHash(self):
'''Return a sha3_256 hash of the blocks DB'''
with open(self._core.blockDB, 'rb') as data:
data = data.read()
hasher = hashlib.sha3_256()
hasher.update(data)
dataHash = hasher.hexdigest()
return dataHash
def validateHash(self, data, length=64):
'''validate if a string is a valid hex formatted hash'''
retVal = True
if len(data) != length:
retVal = False
else:
try:
int(data, 16)
except ValueError:
retVal = False
return retVal