From f8b10cfe124ef67b0becebe3c7a27dc3266fa32a Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Wed, 19 Sep 2018 23:35:26 -0500 Subject: [PATCH] a lot of work on sockets, and added chat module --- onionr/communicator2.py | 8 ++- onionr/core.py | 3 +- onionr/netcontroller.py | 1 + onionr/onionrchat.py | 30 +++++++++ onionr/onionrsockets.py | 63 ++++++++++++------- .../default-plugins/metadataprocessor/main.py | 6 +- 6 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 onionr/onionrchat.py diff --git a/onionr/communicator2.py b/onionr/communicator2.py index c0fbb698..01af7270 100755 --- a/onionr/communicator2.py +++ b/onionr/communicator2.py @@ -466,8 +466,12 @@ class OnionrCommunicatorDaemon: self.blockToUpload = cmd[1] threading.Thread(target=self.uploadBlock).start() elif cmd[0] == 'startSocket': - # Create a socket or connect to one - self.onionrsockets.append(onionrsockets.OnionrSockets(self._core, startData)) + # Create a socket or connect to one. + # The socket handler (such as the plugin or app using it) is specified in startData['reason] + startData = json.loads(cmd[1]) + rCallback = onionrsockets.getSocketCallbackRecieveHandler(self._core, startData['reason'], startData['create']) + sCallback = onionrsockets.getSocketCallbackSendHandler(self._core, startData['reason'], startData['create']) + self.onionrsockets.append(onionrsockets.OnionrSockets(self._core, startData, recieveCallback=rCallback, sendCallback=sCallback)) else: logger.info('Recieved daemonQueue command:' + cmd[0]) diff --git a/onionr/core.py b/onionr/core.py index 3ce4bbfd..54219390 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -21,7 +21,7 @@ import sqlite3, os, sys, time, math, base64, tarfile, getpass, simplecrypt, hash from onionrblockapi import Block import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions, onionrvalues -import onionrblacklist +import onionrblacklist, onionrchat import dbcreator if sys.version_info < (3, 6): try: @@ -79,6 +79,7 @@ class Core: # Initialize the crypto object self._crypto = onionrcrypto.OnionrCrypto(self) self._blacklist = onionrblacklist.OnionrBlackList(self) + self.chatInst = onionrchat.OnionrChat(self) except Exception as error: logger.error('Failed to initialize core Onionr library.', error=error) diff --git a/onionr/netcontroller.py b/onionr/netcontroller.py index b3691956..56b12573 100644 --- a/onionr/netcontroller.py +++ b/onionr/netcontroller.py @@ -68,6 +68,7 @@ class NetController: # Set the Tor control password. Meant to make it harder to manipulate our Tor instance plaintext = base64.b64encode(os.urandom(50)).decode() config.set('tor.controlpassword', plaintext, savefile=True) + config.set('tor.socksport', self.socksPort, savefile=True) controlPort = random.randint(1025, 65535) diff --git a/onionr/onionrchat.py b/onionr/onionrchat.py new file mode 100644 index 00000000..a4051828 --- /dev/null +++ b/onionr/onionrchat.py @@ -0,0 +1,30 @@ +''' + Onionr - P2P Anonymous Storage Network + + Onionr Chat Messages +''' +''' + 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 . +''' +import logger +class OnionrChat: + def __init__(self, coreInst): + return + + def recieveMessage(self, socketInst, data): + logger.info('Got %s' % (data,)) + return '' + + def sendMessage(self, socketInst, data): + return "Hello" \ No newline at end of file diff --git a/onionr/onionrsockets.py b/onionr/onionrsockets.py index 8111f039..2a63613c 100644 --- a/onionr/onionrsockets.py +++ b/onionr/onionrsockets.py @@ -18,13 +18,25 @@ along with this program. If not, see . ''' import stem.control -import socket, selectors -import onionrexceptions, time +import socket, selectors, socks, config +import onionrexceptions, time, onionrchat from dependencies import secrets sel = selectors.DefaultSelector() +def getSocketCallbackRecieveHandler(coreInst, reason, create): + '''Return the recieve handler function for a given socket reason''' + retData = '' + if startData == 'chat': + retData = coreInst.chatInst.recieveMessage + +def getSocketCallbackSendHandler(coreInst, reason, create): + '''Return the send handler function for a given socket reason''' + retData = '' + if startData == 'chat': + retData = coreInst.chatInst.sendMessage + class OnionrSockets: - def __init__(self, coreInst, socketInfo): + def __init__(self, coreInst, socketInfo, recieveCallback=None, sendCallback=None): '''Create a new Socket object. This interface is named a bit misleadingly and does not actually forward network requests. @@ -36,6 +48,12 @@ class OnionrSockets: self.socketID = secrets.token_hex(32) # Generate an ID for this socket self._core = coreInst self.socketInfo = socketInfo + + if not callable(sendCallback) or not callable(recieveCallback) + raise ValueError("callback must be a function") + + self.sendCallback = sendCallback + self.recieveCallback = recieveCallback # Make sure socketInfo provides all necessary values for i in ('peer', 'address', 'create', 'port'): @@ -50,11 +68,11 @@ class OnionrSockets: self.socketPort = socketInfo['port'] self.serverAddress = socketInfo['address'] self.connected = False - self.segment = 0 - self.connData = {} if self.isServer: self.createServer() + else: + self.connectServer() def createServer(self): # Create our HS and advertise it via a block @@ -87,34 +105,31 @@ class OnionrSockets: callback(key.fileobj, mask) return - - def connectServer(self): - return def _accept(self, sock, mask): # Just accept the connection and pass it to our handler conn, addr = sock.accept() conn.setblocking(False) sel.register(conn, selectors.EVENT_READ, self._read) + self.connected = True def _read(self, conn, mask): - data = conn.recv(1000).decode() + data = conn.recv(1024) if data: - self.segment += 1 - self.connData[self.segment] = data - conn.send(data) + data = data.decode() + self.callback(self, data) else: sel.unregister(conn) conn.close() - - def readConnection(self): - if not self.connected: - raise Exception("Connection closed") - count = 0 - while self.connected: - try: - yield self.connData[count] - count += 1 - except KeyError: - pass - time.sleep(0.01) + + def connectServer(self): + # Set the Tor proxy + socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', config.get('tor.socksport'), rdns=True) + socket.socket = socks.socksocket + remoteSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + with remoteSocket as s: + s.connect((self.serverAddress, self.port)) + data = s.recv(1024) + data.send(self.sendCallback(self, data.decode())) + return \ No newline at end of file diff --git a/onionr/static-data/default-plugins/metadataprocessor/main.py b/onionr/static-data/default-plugins/metadataprocessor/main.py index 3b0191ae..940dc424 100644 --- a/onionr/static-data/default-plugins/metadataprocessor/main.py +++ b/onionr/static-data/default-plugins/metadataprocessor/main.py @@ -85,8 +85,12 @@ def on_processBlocks(api): port = api.data['port'] except KeyError: raise ValueError("Missing port for new socket") + try: + reason = api.data['reason'] + except KeyError: + raise ValueError("Missing socket reason") - socketInfo = json.dumps({'peer': api.data['signer'], 'address': address, 'port': port, create = False}) + socketInfo = json.dumps({'peer': api.data['signer'], 'address': address, 'port': port, 'create' = False, 'reason': reason}) api.get_core().daemonQueueAdd('startSocket', socketInfo) def on_init(api, data = None):