Skip to content

Commit

Permalink
Explicit args on modem primitive methods. API error format.
Browse files Browse the repository at this point in the history
  • Loading branch information
ptsmonteiro committed Nov 22, 2023
1 parent eda5580 commit 8c2c6a8
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 54 deletions.
5 changes: 5 additions & 0 deletions modem/api_validations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import re

def validate_freedata_callsign(callsign):
regexp = "^[a-zA-Z]+\d+\w+-\d{1,2}$"
return re.compile(regexp).match(callsign) is not None

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings starting with 'A9' and with many repetitions of '0'.
5 changes: 3 additions & 2 deletions modem/data_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ def worker_transmit(self) -> None:
self.arq.arq_session_handler(data[1], data[2])

elif data[0] == "PING":
# [1] mycallsign
# [1] mycallsign // this is being injected as None
# [2] dxcallsign
self.ping.transmit_ping(data[1], data[2])
mycallsign = f"{self.config['STATION']['mycall']}-{self.config['STATION']['myssid']}"
self.ping.transmit_ping(mycallsign, data[2])

elif data[0] == "BEACON":
# [1] INTERVAL int
Expand Down
21 changes: 11 additions & 10 deletions modem/data_handler_ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ def __init__(self, config, event_queue, states):
self.config = config

# ---------- PING
def transmit_ping(self, mycallsign: bytes, dxcallsign: bytes) -> None:
def transmit_ping(self, mycallsign: str, dxcallsign: str) -> None:
"""
Funktion for controlling pings
Function for controlling pings
Args:
mycallsign:bytes:
dxcallsign:bytes:
mycallsign
dxcallsign
"""
# check if specific callsign is set with different SSID than the Modem is initialized
Expand All @@ -40,14 +40,15 @@ def transmit_ping(self, mycallsign: bytes, dxcallsign: bytes) -> None:

self.dxcallsign = dxcallsign
self.dxcallsign_crc = helpers.get_crc_24(self.dxcallsign)
self.send_data_to_socket_queue(
freedata="modem-message",
ping="transmitting",
dxcallsign=str(dxcallsign, "UTF-8"),
)
self.event_queue.put({
'freedata': "modem-message",
'ping': "transmitting",
'dxcallsign': str(dxcallsign, "UTF-8"),
})

self.log.info(
"[Modem] PING REQ ["
+ mycallsign
+ str(mycallsign, "UTF-8")
+ "] >>> ["
+ str(dxcallsign, "UTF-8")
+ "]"
Expand Down
35 changes: 26 additions & 9 deletions modem/server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask import Flask, request, jsonify, make_response
from flask import Flask, request, jsonify, make_response, abort, Response
from flask_sock import Sock
from flask_cors import CORS
import os
Expand All @@ -12,6 +12,7 @@
import threading
import ujson as json
import websocket_manager as wsm
import api_validations as validations

app = Flask(__name__)
CORS(app)
Expand Down Expand Up @@ -55,9 +56,22 @@ def set_config():
app.modem_service.put("start")

# returns a standard API response
def api_response(data):
return make_response(jsonify(data), 200)

def api_response(data, status = 200):
return make_response(jsonify(data), status)

def api_abort(message, code):
jsonError = json.dumps({'error': message})
abort(Response(jsonError, code))

# validates a parameter
def validate(req, param, validator, isRequired = True):
if param not in req:
if isRequired:
api_abort(f"Required parameter '{param}' is missing.", 400)
else:
return True
if not validator(req[param]):
api_abort(f"Value of '{param}' is invalid.", 400)

## REST API
@app.route('/', methods=['GET'])
Expand Down Expand Up @@ -104,23 +118,26 @@ def post_cqcqcq():
if request.method not in ['POST']:
return api_response({"info": "endpoint for triggering a CQ via POST"})
if app.states.is_modem_running:
server_commands.cqcqcq(request.json)
server_commands.cqcqcq()
return api_response({"cmd": "cqcqcq"})

@app.route('/modem/beacon', methods=['POST'])
def post_beacon():
if request.method not in ['POST']:
return api_response({"info": "endpoint for controlling BEACON STATE via POST"})
if app.states.is_modem_running:
server_commands.beacon(request.json)
if not app.states.is_modem_running:
api_abort('Modem not running', 503)
server_commands.beacon(request.json['enable_beacon'])
return api_response(request.json)

@app.route('/modem/ping_ping', methods=['POST'])
def post_ping():
if request.method not in ['POST']:
return api_response({"info": "endpoint for controlling PING via POST"})
if app.states.is_modem_running:
server_commands.ping_ping(request.json)
if not app.states.is_modem_running:
api_abort('Modem not running', 503)
validate(request.json, 'dxcall', validations.validate_freedata_callsign)
server_commands.ping_ping(request.json['dxcall'])
return api_response(request.json)

@app.route('/modem/send_test_frame', methods=['POST'])
Expand Down
43 changes: 11 additions & 32 deletions modem/server_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,23 @@
from random import randrange
log = structlog.get_logger("COMMANDS")

def cqcqcq(data):
def cqcqcq():
try:
DATA_QUEUE_TRANSMIT.put(["CQ"])
return
except Exception as err:
log.warning("[CMD] error while transmiting CQ", e=err, command=data)
log.warning("[CMD] error while transmiting CQ", e=err)

def ping_ping(data):
def ping_ping(dxcall):
try:
dxcallsign = data["dxcall"]
if not str(dxcallsign).strip():
return
DATA_QUEUE_TRANSMIT.put(["PING", None, dxcallsign])
DATA_QUEUE_TRANSMIT.put(["PING", None, dxcall])

except Exception as err:
log.warning(
"[CMD] PING command execution error", e=err, command=data
"[CMD] PING command execution error", e=err
)

def beacon(data, interval=300):
beacon_state = data['enabled'] in [True]

def beacon(beacon_state, interval=300):
log.info(
"[CMD] Changing beacon state", state=beacon_state
)
Expand All @@ -41,55 +36,39 @@ def modem_send_test_frame():
)
DATA_QUEUE_TRANSMIT.put(["SEND_TEST_FRAME"])

def modem_arq_send_raw(data):
def modem_arq_send_raw(mycallsign, dxcallsign, payload, arq_uuid = "no-uuid"):

# wait some random time
threading.Event().wait(randrange(5, 25, 5) / 10.0)

base64data = data["data"]

# check if transmission uuid provided else set no-uuid
try:
arq_uuid = data["uuid"]
except Exception:
arq_uuid = "no-uuid"
base64data = payload

if len(base64data) % 4:
raise TypeError

binarydata = base64.b64decode(base64data)

DATA_QUEUE_TRANSMIT.put(
["ARQ_RAW", binarydata, arq_uuid, data["mycallsign"], data["dxcallsign"]]
["ARQ_RAW", binarydata, arq_uuid, mycallsign, dxcallsign]
)


def modem_fec_transmit(data):
def modem_fec_transmit(mode, wakeup, base64data, mycallsign = None):
log.info(
"[CMD] Send fec frame"
)
mode = data["mode"]
wakeup = data["wakeup"]
base64data = data["payload"]
if len(base64data) % 4:
raise TypeError
payload = base64.b64decode(base64data)

try:
mycallsign = data["mycallsign"]
except:
mycallsign = None

DATA_QUEUE_TRANSMIT.put(["FEC", mode, wakeup, payload, mycallsign])

def modem_fec_is_writing(data):
def modem_fec_is_writing(mycallsign):
try:
mycallsign = data["mycallsign"]
DATA_QUEUE_TRANSMIT.put(["FEC_IS_WRITING", mycallsign])
except Exception as err:
log.warning(
"[SCK] Send fec frame command execution error",
e=err,
command=data,
)

9 changes: 8 additions & 1 deletion modem/websocket_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ def handle_connection(sock, client_list, event_queue):
def transmit_sock_data_worker(client_list, event_queue):
while True:
event = event_queue.get()

if isinstance(event, str):
print(f"WARNING: Queue event:\n'{event}'\n still in string format")
json_event = event
else:
json_event = json.dumps(event)

clients = client_list.copy()
for client in clients:
try:
client.send(event)
client.send(json_event)
except Exception:
client_list.remove(client)

Expand Down

0 comments on commit 8c2c6a8

Please sign in to comment.