diff --git a/src/httpapi/security/lan.py b/src/httpapi/security/lan.py
new file mode 100644
index 00000000..1989188c
--- /dev/null
+++ b/src/httpapi/security/lan.py
@@ -0,0 +1,66 @@
+"""Onionr - Private P2P Communication.
+
+Process incoming requests to the public api server for certain attacks
+"""
+from flask import Blueprint, request, abort, g
+from onionrservices import httpheaders
+from onionrutils import epoch
+from lan import getip
+"""
+ 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 .
+"""
+
+
+class LANAPISecurity:
+ def __init__(self, lan_client):
+ lan_api_security_bp = Blueprint('lanapisecurity', __name__)
+ self.lan_api_security_bp = lan_api_security_bp
+
+ @lan_api_security_bp.before_app_request
+ def validate_request():
+ """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)
+ transports = getip.lan_ips
+ if lan_client.config.get('general.security_level', default=1) > 0:
+ abort(403)
+ if request.host not in transports:
+ # Abort conn if wrong HTTP hostname, to prevent DNS rebinding
+ abort(403)
+ lan_client.hitCount += 1 # raise hit count for valid requests
+ try:
+ if 'onionr' in request.headers['User-Agent'].lower():
+ g.is_onionr_client = True
+ else:
+ g.is_onionr_client = False
+ except KeyError:
+ g.is_onionr_client = False
+
+ @lan_api_security_bp.after_app_request
+ def send_headers(resp):
+ """Send api, access control headers"""
+ resp = httpheaders.set_default_onionr_http_headers(resp)
+ # Network API version
+ resp.headers['X-API'] = lan_client.API_VERSION
+ # Delete some HTTP headers for Onionr user agents
+ NON_NETWORK_HEADERS = ('Content-Security-Policy', 'X-Frame-Options',
+ 'X-Content-Type-Options', 'Feature-Policy',
+ 'Clear-Site-Data', 'Referrer-Policy')
+ try:
+ if g.is_onionr_client:
+ for header in NON_NETWORK_HEADERS: del resp.headers[header]
+ except AttributeError:
+ abort(403)
+ lan_client.lastRequest = epoch.get_rounded_epoch(roundS=5)
+ return resp
diff --git a/src/lan/client/__init__.py b/src/lan/client/__init__.py
index 8604f12b..74e904c1 100644
--- a/src/lan/client/__init__.py
+++ b/src/lan/client/__init__.py
@@ -4,7 +4,9 @@ LAN transport client thread
"""
from typing import List
+from onionrcrypto.cryptoutils.randomshuffle import random_shuffle
from utils.bettersleep import better_sleep
+from onionrutils.basicrequests import do_post_request, do_get_request
"""
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
@@ -24,9 +26,20 @@ from utils.bettersleep import better_sleep
class Client:
def __init__(self):
self.peers = []
- return
+ self.lookup_time = {}
+ self.poll_delay = 10
+
+ def get_lookup_time(self, peer):
+ try:
+ return self.lookup_time[peer]
+ except KeyError:
+ return 0
def start(self):
while True:
- #print(1, self.peers, type(self.peers))
- better_sleep(1)
+ self.peers = random_shuffle(self.peers)
+
+
+
+ better_sleep(self.pull_delay)
+
diff --git a/src/onionrutils/basicrequests.py b/src/onionrutils/basicrequests.py
index 13d65769..702cf0b2 100644
--- a/src/onionrutils/basicrequests.py
+++ b/src/onionrutils/basicrequests.py
@@ -1,8 +1,14 @@
-'''
- Onionr - Private P2P Communication
+'''Onionr - Private P2P Communication.
Do HTTP GET or POST requests through a proxy
'''
+from ipaddress import IPv4Address
+from urllib.parse import urlparse
+
+import requests, streamedrequests
+import logger, onionrexceptions
+from etc import onionrvalues
+from . import localcommand
'''
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
@@ -17,20 +23,22 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import requests, streamedrequests
-import logger, onionrexceptions
-from etc import onionrvalues
-from . import localcommand
+
+
def do_post_request(url, data={}, port=0, proxyType='tor', max_size=10000, content_type: str = ''):
- '''
- Do a POST request through a local tor or i2p instance
- '''
+ '''Do a POST request through a local tor or i2p instance.'''
if proxyType == 'tor':
if port == 0:
port = localcommand.local_command('/gettorsocks')
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
elif proxyType == 'i2p':
proxies = {'http': 'http://127.0.0.1:4444'}
+ elif proxyType == 'lan':
+ address = urlparse(url).hostname
+ if IPv4Address(address).is_private and not IPv4Address(address).is_loopback:
+ proxies = {}
+ else:
+ return
else:
return
headers = {'User-Agent': 'PyOnionr', 'Connection':'close'}
@@ -48,6 +56,7 @@ def do_post_request(url, data={}, port=0, proxyType='tor', max_size=10000, conte
retData = False
return retData
+
def do_get_request(url, port=0, proxyType='tor', ignoreAPI=False, returnHeaders=False, max_size=5242880):
'''
Do a get request through a local tor or i2p instance
@@ -56,10 +65,16 @@ def do_get_request(url, port=0, proxyType='tor', ignoreAPI=False, returnHeaders=
retData = False
if proxyType == 'tor':
if port == 0:
- raise onionrexceptions.MissingPort('Socks port required for Tor HTTP get request')
+ port = localcommand.local_command('/gettorsocks')
proxies = {'http': 'socks4a://127.0.0.1:' + str(port), 'https': 'socks4a://127.0.0.1:' + str(port)}
elif proxyType == 'i2p':
proxies = {'http': 'http://127.0.0.1:4444'}
+ elif proxyType == 'lan':
+ address = urlparse(url).hostname
+ if IPv4Address(address).is_private and not IPv4Address(address).is_loopback:
+ proxies = {}
+ else:
+ return
else:
return
headers = {'User-Agent': 'PyOnionr', 'Connection':'close'}