Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
bead51db69 | |||
bf171bbfda | |||
8ca068c98f | |||
a696cc4ed8 | |||
0a2a28cb34 | |||
0f20f1fcdc | |||
9a61f90fec | |||
5a88856825 | |||
0e4f772c69 | |||
8278ece803 | |||
eebe33f896 | |||
61d03b38b9 | |||
c0f447ca20 | |||
81465d69cc | |||
1e84ced9a9 |
@ -1,3 +1,6 @@
|
||||
**0.19.1**
|
||||
- Added ability to authenticate receivers with listing sites using "receiver id" tokens
|
||||
|
||||
**0.19.0**
|
||||
- Fix direwolf connection setup by implementing a retry loop
|
||||
- Pass direct sampling mode changes for rtl_sdr_soapy to owrx_connector
|
||||
|
@ -59,6 +59,19 @@ Antenna: Receiver Antenna<br />
|
||||
Website: <a href="http://localhost" target="_blank">http://localhost</a>
|
||||
"""
|
||||
|
||||
# ==== Public receiver listings ====
|
||||
# You can publish your receiver on online receiver directories, like https://www.receiverbook.de
|
||||
# You will receive a receiver key from the directory that will authenticate you as the operator of this receiver.
|
||||
# Please note that you not share your receiver keys publicly since anyone that obtains your receiver key can take over
|
||||
# your public listing.
|
||||
# Your receiver keys should be placed into this array:
|
||||
receiver_keys = []
|
||||
# If you list your receiver on multiple sites, you can place all your keys into the array above, or you can append
|
||||
# keys to the arraylike this:
|
||||
# receiver_keys += ["my-receiver-key"]
|
||||
|
||||
# If you're not sure, simply copy & paste the code you received from your listing site below this line:
|
||||
|
||||
# ==== DSP/RX settings ====
|
||||
fft_fps = 9
|
||||
fft_size = 4096 # Should be power of 2
|
||||
|
7
debian/changelog
vendored
7
debian/changelog
vendored
@ -1,3 +1,10 @@
|
||||
openwebrx (0.19.1) buster focal; urgency=low
|
||||
|
||||
* Added ability to authenticate receivers with listing sites using
|
||||
"receiver id" tokens
|
||||
|
||||
-- Jakob Ketterl <jakob.ketterl@gmx.de> Sat, 13 Jun 2020 16:46:00 +0000
|
||||
|
||||
openwebrx (0.19.0) buster focal; urgency=low
|
||||
* Fix direwolf connection setup by implementing a retry loop
|
||||
* Pass direct sampling mode changes for rtl_sdr_soapy to owrx_connector
|
||||
|
@ -138,7 +138,6 @@ class OpenWebRxReceiverClient(OpenWebRxClient):
|
||||
def handleTextMessage(self, conn, message):
|
||||
try:
|
||||
message = json.loads(message)
|
||||
logger.debug(message)
|
||||
if "type" in message:
|
||||
if message["type"] == "dspcontrol":
|
||||
if "action" in message and message["action"] == "start":
|
||||
|
@ -7,14 +7,18 @@ class Controller(object):
|
||||
self.request = request
|
||||
self.options = options
|
||||
|
||||
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None):
|
||||
def send_response(self, content, code=200, content_type="text/html", last_modified: datetime = None, max_age=None, headers=None):
|
||||
self.handler.send_response(code)
|
||||
if headers is None:
|
||||
headers = {}
|
||||
if content_type is not None:
|
||||
self.handler.send_header("Content-Type", content_type)
|
||||
headers["Content-Type"] = content_type
|
||||
if last_modified is not None:
|
||||
self.handler.send_header("Last-Modified", last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"))
|
||||
headers["Last-Modified"] = last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
if max_age is not None:
|
||||
self.handler.send_header("Cache-Control", "max-age: {0}".format(max_age))
|
||||
headers["Cache-Control"] = "max-age: {0}".format(max_age)
|
||||
for key, value in headers.items():
|
||||
self.handler.send_header(key, value)
|
||||
self.handler.end_headers()
|
||||
if type(content) == str:
|
||||
content = content.encode()
|
||||
|
@ -1,11 +1,13 @@
|
||||
from . import Controller
|
||||
from owrx.client import ClientRegistry
|
||||
from owrx.version import openwebrx_version
|
||||
from owrx.sdr import SdrService
|
||||
from owrx.config import Config
|
||||
import os
|
||||
from owrx.receiverid import ReceiverId, KeyException
|
||||
import json
|
||||
import pkg_resources
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StatusController(Controller):
|
||||
@ -27,7 +29,12 @@ class StatusController(Controller):
|
||||
|
||||
def indexAction(self):
|
||||
pm = Config.get()
|
||||
|
||||
headers = None
|
||||
if "Authorization" in self.request.headers:
|
||||
try:
|
||||
headers = ReceiverId.getResponseHeader(self.request.headers["Authorization"])
|
||||
except KeyException:
|
||||
logger.exception("error processing authorization header")
|
||||
status = {
|
||||
"receiver": {
|
||||
"name": pm["receiver_name"],
|
||||
@ -40,4 +47,4 @@ class StatusController(Controller):
|
||||
"version": openwebrx_version,
|
||||
"sdrs": [self.getReceiverStats(r) for r in SdrService.getSources().values()]
|
||||
}
|
||||
self.send_response(json.dumps(status), content_type="application/json")
|
||||
self.send_response(json.dumps(status), content_type="application/json", headers=headers)
|
||||
|
22
owrx/http.py
22
owrx/http.py
@ -35,19 +35,26 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||
logger.debug("%s - - [%s] %s", self.address_string(), self.log_date_time_string(), format % args)
|
||||
|
||||
def do_GET(self):
|
||||
self.router.route(self, "GET")
|
||||
self.router.route(self, self.get_request("GET"))
|
||||
|
||||
def do_POST(self):
|
||||
self.router.route(self, "POST")
|
||||
self.router.route(self, self.get_request("POST"))
|
||||
|
||||
def get_request(self, method):
|
||||
url = urlparse(self.path)
|
||||
return Request(url, method, self.headers)
|
||||
|
||||
|
||||
class Request(object):
|
||||
def __init__(self, url, method, cookies):
|
||||
def __init__(self, url, method, headers):
|
||||
self.path = url.path
|
||||
self.query = parse_qs(url.query)
|
||||
self.matches = None
|
||||
self.method = method
|
||||
self.cookies = cookies
|
||||
self.headers = headers
|
||||
self.cookies = SimpleCookie()
|
||||
if "Cookie" in headers:
|
||||
self.cookies.load(headers["Cookie"])
|
||||
|
||||
def setMatches(self, matches):
|
||||
self.matches = matches
|
||||
@ -114,12 +121,7 @@ class Router(object):
|
||||
if r.matches(request):
|
||||
return r
|
||||
|
||||
def route(self, handler, method):
|
||||
url = urlparse(handler.path)
|
||||
cookies = SimpleCookie()
|
||||
if "Cookie" in handler.headers:
|
||||
cookies.load(handler.headers["Cookie"])
|
||||
request = Request(url, method, cookies)
|
||||
def route(self, handler, request):
|
||||
route = self.find_route(request)
|
||||
if route is not None:
|
||||
controller = route.controller
|
||||
|
81
owrx/receiverid.py
Normal file
81
owrx/receiverid.py
Normal file
@ -0,0 +1,81 @@
|
||||
import re
|
||||
import logging
|
||||
import hashlib
|
||||
import hmac
|
||||
from datetime import datetime
|
||||
from owrx.config import Config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
keyRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{64})$")
|
||||
keyChallengeRegex = re.compile("^([a-zA-Z]+)-([0-9a-f]{32})-([0-9a-f]{32})$")
|
||||
headerRegex = re.compile("^ReceiverId (.*)$")
|
||||
|
||||
|
||||
class KeyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Key(object):
|
||||
def __init__(self, keyString):
|
||||
matches = keyRegex.match(keyString)
|
||||
if not matches:
|
||||
raise KeyException("invalid key format")
|
||||
self.source = matches.group(1)
|
||||
self.id = matches.group(2)
|
||||
self.secret = matches.group(3)
|
||||
|
||||
|
||||
class KeyChallenge(object):
|
||||
def __init__(self, challengeString):
|
||||
matches = keyChallengeRegex.match(challengeString)
|
||||
if not matches:
|
||||
raise KeyException("invalid key challenge format")
|
||||
self.source = matches.group(1)
|
||||
self.id = matches.group(2)
|
||||
self.challenge = matches.group(3)
|
||||
|
||||
|
||||
class KeyResponse(object):
|
||||
def __str__(self):
|
||||
return "TODO"
|
||||
|
||||
|
||||
class ReceiverId(object):
|
||||
@staticmethod
|
||||
def getResponseHeader(requestHeader):
|
||||
matches = headerRegex.match(requestHeader)
|
||||
if not matches:
|
||||
raise KeyException("invalid authorization header")
|
||||
challenge = KeyChallenge(matches.group(1))
|
||||
key = ReceiverId.findKey(challenge)
|
||||
if key is None:
|
||||
return {}
|
||||
time, signature = ReceiverId.signChallenge(challenge, key)
|
||||
return {
|
||||
"Signature": signature,
|
||||
"Time": time,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def findKey(challenge):
|
||||
def parseKey(keyString):
|
||||
try:
|
||||
return Key(keyString)
|
||||
except KeyException as e:
|
||||
logger.error(e)
|
||||
keys = [parseKey(keyString) for keyString in Config.get()['receiver_keys']]
|
||||
keys = [key for key in keys if key is not None]
|
||||
matching_keys = [key for key in keys if key.source == challenge.source and key.id == challenge.id]
|
||||
if matching_keys:
|
||||
return matching_keys[0]
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def signChallenge(challenge, key):
|
||||
now = datetime.utcnow().isoformat()
|
||||
m = hmac.new(bytes.fromhex(key.secret), digestmod=hashlib.sha256)
|
||||
m.update(bytes.fromhex(challenge.challenge))
|
||||
m.update(now.encode('utf8'))
|
||||
return now, m.hexdigest()
|
@ -17,10 +17,10 @@ class FifiSdrSource(DirectSource):
|
||||
return super().getEventNames() + ["device"]
|
||||
|
||||
def getFormatConversion(self):
|
||||
return ["csdr convert_s16_f", "csdr gain_ff 30"]
|
||||
return ["csdr convert_s16_f", "csdr gain_ff 5"]
|
||||
|
||||
def sendRockProgFrequency(self, frequency):
|
||||
process = Popen(["rockprog", "--vco", "-w", "--", "freq={}".format(frequency / 1E6)])
|
||||
process = Popen(["rockprog", "--vco", "-w", "--freq={}".format(frequency / 1E6)])
|
||||
process.communicate()
|
||||
rc = process.wait()
|
||||
if rc != 0:
|
||||
|
@ -1,5 +1,5 @@
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
_versionstring = "0.19.0"
|
||||
_versionstring = "0.19.1"
|
||||
strictversion = StrictVersion(_versionstring)
|
||||
openwebrx_version = "v{0}".format(strictversion)
|
||||
|
Reference in New Issue
Block a user