fixed config and other bugs, improved connection server
parent
4e3ad27485
commit
b582377c8c
|
@ -27,10 +27,12 @@ Users are identified by ed25519/curve25519 public keys, which can be used to sig
|
||||||
|
|
||||||
Onionr can be used for mail, as a social network, instant messenger, file sharing software, or for encrypted group discussion.
|
Onionr can be used for mail, as a social network, instant messenger, file sharing software, or for encrypted group discussion.
|
||||||
|
|
||||||
The whitepaper (subject to change prior to first alpha release) is available [here](docs/whitepaper.md).
|
The whitepaper (subject to change prior to alpha release) is available [here](docs/whitepaper.md).
|
||||||
|
|
||||||
![node web illustration](docs/onionr-web.png)
|
![node web illustration](docs/onionr-web.png)
|
||||||
|
|
||||||
|
![Tor stinks slide image](docs/tor-stinks-02.png)
|
||||||
|
|
||||||
## Main Features
|
## Main Features
|
||||||
|
|
||||||
* [X] 🌐 Fully p2p/decentralized, no trackers or other single points of failure
|
* [X] 🌐 Fully p2p/decentralized, no trackers or other single points of failure
|
||||||
|
@ -118,5 +120,3 @@ The 'open source badge' is by Maik Ellerbrock and is licensed under a Creative C
|
||||||
The Onionr logo was created by [Anhar Ismail](https://github.com/anharismail) under the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/).
|
The Onionr logo was created by [Anhar Ismail](https://github.com/anharismail) under the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
If you modify and redistribute our code ("forking"), please use a different logo and project name to avoid confusion. Please do not use our logo in a way that makes it seem like we endorse you without our permission.
|
If you modify and redistribute our code ("forking"), please use a different logo and project name to avoid confusion. Please do not use our logo in a way that makes it seem like we endorse you without our permission.
|
||||||
|
|
||||||
![Tor stinks slide image](docs/tor-stinks-02.png)
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PublicAPI:
|
||||||
def validateRequest():
|
def validateRequest():
|
||||||
'''Validate request has the correct hostname'''
|
'''Validate request has the correct hostname'''
|
||||||
# If high security level, deny requests to public (HS should be disabled anyway for Tor, but might not be for I2P)
|
# If high security level, deny requests to public (HS should be disabled anyway for Tor, but might not be for I2P)
|
||||||
if config.get('general.security_level', default=0) > 0:
|
if config.get('general.security_level', default=1) > 0:
|
||||||
abort(403)
|
abort(403)
|
||||||
if type(self.torAdder) is None and type(self.i2pAdder) is None:
|
if type(self.torAdder) is None and type(self.i2pAdder) is None:
|
||||||
# abort if our hs addresses are not known
|
# abort if our hs addresses are not known
|
||||||
|
|
|
@ -33,6 +33,7 @@ OnionrCommunicatorTimers = onionrcommunicatortimers.OnionrCommunicatorTimers
|
||||||
config.reload()
|
config.reload()
|
||||||
class OnionrCommunicatorDaemon:
|
class OnionrCommunicatorDaemon:
|
||||||
def __init__(self, onionrInst, proxyPort, developmentMode=config.get('general.dev_mode', False)):
|
def __init__(self, onionrInst, proxyPort, developmentMode=config.get('general.dev_mode', False)):
|
||||||
|
config.reload()
|
||||||
onionrInst.communicatorInst = self
|
onionrInst.communicatorInst = self
|
||||||
# configure logger and stuff
|
# configure logger and stuff
|
||||||
onionr.Onionr.setupConfig('data/', self = self)
|
onionr.Onionr.setupConfig('data/', self = self)
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
'''
|
'''
|
||||||
import communicator, onionrexceptions
|
import communicator, onionrexceptions
|
||||||
import logger
|
import logger, onionrpeers
|
||||||
|
|
||||||
def download_blocks_from_communicator(comm_inst):
|
def download_blocks_from_communicator(comm_inst):
|
||||||
assert isinstance(comm_inst, communicator.OnionrCommunicatorDaemon)
|
assert isinstance(comm_inst, communicator.OnionrCommunicatorDaemon)
|
||||||
for blockHash in list(comm_inst.blockQueue):
|
for blockHash in list(comm_inst.blockQueue):
|
||||||
|
if len(comm_inst.onlinePeers) == 0:
|
||||||
|
break
|
||||||
triedQueuePeers = [] # List of peers we've tried for a block
|
triedQueuePeers = [] # List of peers we've tried for a block
|
||||||
try:
|
try:
|
||||||
blockPeers = list(comm_inst.blockQueue[blockHash])
|
blockPeers = list(comm_inst.blockQueue[blockHash])
|
||||||
|
|
|
@ -98,9 +98,6 @@ def check():
|
||||||
|
|
||||||
if not os.path.exists(os.path.dirname(get_config_file())):
|
if not os.path.exists(os.path.dirname(get_config_file())):
|
||||||
os.makedirs(os.path.dirname(get_config_file()))
|
os.makedirs(os.path.dirname(get_config_file()))
|
||||||
if not os.path.isfile(get_config_file()):
|
|
||||||
open(get_config_file(), 'a', encoding="utf8").close()
|
|
||||||
save()
|
|
||||||
|
|
||||||
def save():
|
def save():
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -103,7 +103,7 @@ CookieAuthentication 1
|
||||||
ControlPort ''' + str(controlPort) + '''
|
ControlPort ''' + str(controlPort) + '''
|
||||||
HashedControlPassword ''' + str(password) + '''
|
HashedControlPassword ''' + str(password) + '''
|
||||||
'''
|
'''
|
||||||
if config.get('general.security_level') == 0:
|
if config.get('general.security_level', 1) == 0:
|
||||||
torrcData += '''\nHiddenServiceDir ''' + self.dataDir + '''hs/
|
torrcData += '''\nHiddenServiceDir ''' + self.dataDir + '''hs/
|
||||||
\n''' + hsVer + '''\n
|
\n''' + hsVer + '''\n
|
||||||
HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort)
|
HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort)
|
||||||
|
|
|
@ -71,7 +71,7 @@ class Onionr:
|
||||||
logger.set_file(os.environ.get('LOG_DIR', 'data') + '/onionr.log')
|
logger.set_file(os.environ.get('LOG_DIR', 'data') + '/onionr.log')
|
||||||
|
|
||||||
# Load global configuration data
|
# Load global configuration data
|
||||||
data_exists = Onionr.setupConfig(self.dataDir, self = self)
|
data_exists = Onionr.setupConfig(self.dataDir, self)
|
||||||
|
|
||||||
if netcontroller.torBinary() is None:
|
if netcontroller.torBinary() is None:
|
||||||
logger.error('Tor is not installed')
|
logger.error('Tor is not installed')
|
||||||
|
@ -122,7 +122,6 @@ class Onionr:
|
||||||
config.set('client.client.port', randomPort, savefile=True)
|
config.set('client.client.port', randomPort, savefile=True)
|
||||||
if type(config.get('client.public.port')) is type(None):
|
if type(config.get('client.public.port')) is type(None):
|
||||||
randomPort = netcontroller.getOpenPort()
|
randomPort = netcontroller.getOpenPort()
|
||||||
print(randomPort)
|
|
||||||
config.set('client.public.port', randomPort, savefile=True)
|
config.set('client.public.port', randomPort, savefile=True)
|
||||||
if type(config.get('client.participate')) is type(None):
|
if type(config.get('client.participate')) is type(None):
|
||||||
config.set('client.participate', True, savefile=True)
|
config.set('client.participate', True, savefile=True)
|
||||||
|
|
|
@ -50,7 +50,7 @@ def daemon(o_inst):
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
onionr.Onionr.setupConfig('data/', self = o_inst)
|
#onionr.Onionr.setupConfig('data/', self = o_inst)
|
||||||
|
|
||||||
if o_inst._developmentMode:
|
if o_inst._developmentMode:
|
||||||
logger.warn('DEVELOPMENT MODE ENABLED (NOT RECOMMENDED)', timestamp = False)
|
logger.warn('DEVELOPMENT MODE ENABLED (NOT RECOMMENDED)', timestamp = False)
|
||||||
|
@ -59,7 +59,7 @@ def daemon(o_inst):
|
||||||
if not net.startTor():
|
if not net.startTor():
|
||||||
o_inst.onionrUtils.localCommand('shutdown')
|
o_inst.onionrUtils.localCommand('shutdown')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if len(net.myID) > 0 and o_inst.onionrCore.config.get('general.security_level') == 0:
|
if len(net.myID) > 0 and o_inst.onionrCore.config.get('general.security_level', 1) == 0:
|
||||||
logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID))
|
logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID))
|
||||||
else:
|
else:
|
||||||
logger.debug('.onion service disabled')
|
logger.debug('.onion service disabled')
|
||||||
|
|
|
@ -23,6 +23,9 @@ import core
|
||||||
from . import connectionserver, bootstrapservice
|
from . import connectionserver, bootstrapservice
|
||||||
|
|
||||||
class OnionrServices:
|
class OnionrServices:
|
||||||
|
'''
|
||||||
|
Create a client or server for connecting to peer interfaces
|
||||||
|
'''
|
||||||
def __init__(self, onionr_core):
|
def __init__(self, onionr_core):
|
||||||
assert isinstance(onionr_core, core.Core)
|
assert isinstance(onionr_core, core.Core)
|
||||||
self._core = onionr_core
|
self._core = onionr_core
|
||||||
|
@ -32,13 +35,19 @@ class OnionrServices:
|
||||||
return
|
return
|
||||||
|
|
||||||
def create_server(self, peer, address):
|
def create_server(self, peer, address):
|
||||||
|
'''
|
||||||
|
When a client wants to connect, contact their bootstrap address and tell them our
|
||||||
|
ephemeral address for our service by creating a new ConnectionServer instance
|
||||||
|
'''
|
||||||
assert self._core._utils.validateID(address)
|
assert self._core._utils.validateID(address)
|
||||||
BOOTSTRAP_TRIES = 10
|
BOOTSTRAP_TRIES = 10 # How many times to attempt contacting the bootstrap server
|
||||||
TRY_WAIT = 3
|
TRY_WAIT = 3 # Seconds to wait before trying bootstrap again
|
||||||
|
# HTTP is fine because .onion/i2p is encrypted/authenticated
|
||||||
base_url = 'http://%s/' % (address,)
|
base_url = 'http://%s/' % (address,)
|
||||||
socks = self._core.config.get('tor.socksport')
|
socks = self._core.config.get('tor.socksport')
|
||||||
for x in range(BOOTSTRAP_TRIES):
|
for x in range(BOOTSTRAP_TRIES):
|
||||||
if self._core._utils.doGetRequest(base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!':
|
if self._core._utils.doGetRequest(base_url + 'ping', port=socks, ignoreAPI=True) == 'pong!':
|
||||||
|
# if bootstrap sever is online, tell them our service address
|
||||||
connectionserver.ConnectionServer(peer, address, core_inst=self._core)
|
connectionserver.ConnectionServer(peer, address, core_inst=self._core)
|
||||||
else:
|
else:
|
||||||
time.sleep(TRY_WAIT)
|
time.sleep(TRY_WAIT)
|
||||||
|
|
|
@ -22,6 +22,7 @@ from gevent.pywsgi import WSGIServer, WSGIHandler
|
||||||
from stem.control import Controller
|
from stem.control import Controller
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
import core, logger, httpapi
|
import core, logger, httpapi
|
||||||
|
import onionrexceptions
|
||||||
from netcontroller import getOpenPort
|
from netcontroller import getOpenPort
|
||||||
import api
|
import api
|
||||||
from . import httpheaders
|
from . import httpheaders
|
||||||
|
@ -66,11 +67,23 @@ class ConnectionServer:
|
||||||
with Controller.from_port(port=core_inst.config.get('tor.controlPort')) as controller:
|
with Controller.from_port(port=core_inst.config.get('tor.controlPort')) as controller:
|
||||||
# Connect to the Tor process for Onionr
|
# Connect to the Tor process for Onionr
|
||||||
controller.authenticate(core_inst.config.get('tor.controlpassword'))
|
controller.authenticate(core_inst.config.get('tor.controlpassword'))
|
||||||
# Create the v3 onion service
|
# Create the v3 onion service for the peer to connect to
|
||||||
response = controller.create_ephemeral_hidden_service({80: service_port}, await_publication = True, key_type='NEW', key_content = 'ED25519-V3')
|
response = controller.create_ephemeral_hidden_service({80: service_port}, await_publication = True, key_type='NEW', key_content = 'ED25519-V3')
|
||||||
|
|
||||||
|
try:
|
||||||
|
for x in range(3):
|
||||||
|
attempt = self.core_inst._utils.doPostRequest('http://' + address + '/bs/' + response.service_id, port=socks)
|
||||||
|
if attempt == 'success':
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ConnectionError
|
||||||
|
except ConnectionError:
|
||||||
|
# Re-raise
|
||||||
|
raise ConnectionError('Could not reach %s bootstrap address %s' % (peer, address))
|
||||||
|
else:
|
||||||
|
# If no connection error, create the service and save it to local global key store
|
||||||
self.core_inst.keyStore.put('dc-' + response.service_id, self.core_inst._utils.bytesToStr(peer))
|
self.core_inst.keyStore.put('dc-' + response.service_id, self.core_inst._utils.bytesToStr(peer))
|
||||||
self.core_inst._utils.doPostRequest('http://' + address + '/bs/' + response.service_id, port=socks)
|
|
||||||
logger.info('hosting on %s with %s' % (response.service_id, peer))
|
logger.info('hosting on %s with %s' % (response.service_id, peer))
|
||||||
http_server.serve_forever()
|
http_server.serve_forever()
|
||||||
self.core_inst.keyStore.delete('dc-' + response.service_id)
|
|
||||||
http_server.stop()
|
http_server.stop()
|
||||||
|
self.core_inst.keyStore.delete('dc-' + response.service_id)
|
|
@ -3,10 +3,12 @@ import config, logger
|
||||||
|
|
||||||
def setup_config(dataDir, o_inst = None):
|
def setup_config(dataDir, o_inst = None):
|
||||||
data_exists = os.path.exists(dataDir)
|
data_exists = os.path.exists(dataDir)
|
||||||
|
|
||||||
if not data_exists:
|
if not data_exists:
|
||||||
os.mkdir(dataDir)
|
os.mkdir(dataDir)
|
||||||
|
config.reload()
|
||||||
|
|
||||||
|
|
||||||
|
if not os.path.exists(config._configfile):
|
||||||
if os.path.exists('static-data/default_config.json'):
|
if os.path.exists('static-data/default_config.json'):
|
||||||
# this is the default config, it will be overwritten if a config file already exists. Else, it saves it
|
# this is the default config, it will be overwritten if a config file already exists. Else, it saves it
|
||||||
with open('static-data/default_config.json', 'r') as configReadIn:
|
with open('static-data/default_config.json', 'r') as configReadIn:
|
||||||
|
@ -15,9 +17,8 @@ def setup_config(dataDir, o_inst = None):
|
||||||
# the default config file doesn't exist, try hardcoded config
|
# the default config file doesn't exist, try hardcoded config
|
||||||
logger.warn('Default configuration file does not exist, switching to hardcoded fallback configuration!')
|
logger.warn('Default configuration file does not exist, switching to hardcoded fallback configuration!')
|
||||||
config.set_config({'dev_mode': True, 'log': {'file': {'output': True, 'path': dataDir + 'output.log'}, 'console': {'output': True, 'color': True}}})
|
config.set_config({'dev_mode': True, 'log': {'file': {'output': True, 'path': dataDir + 'output.log'}, 'console': {'output': True, 'color': True}}})
|
||||||
if not data_exists:
|
|
||||||
config.save()
|
config.save()
|
||||||
config.reload() # this will read the configuration file into memory
|
|
||||||
|
|
||||||
settings = 0b000
|
settings = 0b000
|
||||||
if config.get('log.console.color', True):
|
if config.get('log.console.color', True):
|
||||||
|
|
Loading…
Reference in New Issue