Merge pull request #1 from n7tae/main

run-time config nlohmann's json lib, codecstream improvements, readme rework
pull/8/head
nostar 3 years ago committed by GitHub
commit 37efcb21fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

12
.gitignore vendored

@ -1,13 +1,7 @@
*.o *.o
*.d *.d
.vscode .vscode
reflector/urfd.blacklist reflector/urfd.*
reflector/urfd.whitelist
reflector/urfd.interlink
reflector/urfd.terminal
configure.mk
configure.h
configure.sql
reflector.cfg
wiresx/configure.php
urfd urfd
inicheck
dbutil

@ -0,0 +1,129 @@
###### URFD CONFIGURATION Example ######
# comments begin with '#'
# Do not use quotes, unless in a comment!
[Names]
Callsign = URF??? # where ? is A-Z or 0-9
SysopEmail = me@somewhere.com
# 2-letter country codes are listed at https://www.iban.com/country-codes
Country = GB
Sponsor = My Home Club
[IP Addresses]
# Binding addresses are usually the 'any' address
IPv4Binding = 0.0.0.0
# define IPv6 if you want a "dual-stack" reflector
# IPv6Binding = ::
# define if you want to override what urfd finds using ipv4.icanhazip.com
# IPv4External = 4.3.2.1
# define if you want to override what urfd finds using ipv6.icanhazip.com
# IPv6External = f:e:d:c:b:a:9:0
Transcoder = local # SORRY, but only local TC's are supported right now!
[Modules]
# Modules = ABCDEFGHIJKLMNOPQRSTUVWXYZ
Modules = ADMSZ
Transcoded = A # comment out if you don't have transcoding hardware
# Create Descriptions as needed...
DescriptionA = Transcoded
DescriptionD = DMR Chat
DescriptionM = M17 Chat
DescriptionS = DStar Chat
DescriptionZ = Temp Meeting
# Protocols
[Brandmeister]
Enable = false # Set to true if you've configured BM connections in your urfd.interlink file.
Port = 10002
[DCS]
Port = 30051
[DExtra]
Port = 30001
[DPlus]
Port = 20001
[G3]
Enable = true
[DMRPlus]
Port = 8880
[M17]
Port = 17000
[MMDVM]
Port = 62030
DefaultId = 0
[NXDN]
Port = 41400
AutoLinkModule = A # comment out if you want to disable AL
ReflectorID = 12345
[P25]
Port = 41000
AutoLinkModule = A # comment out if you want to disable AL
ReflectorID = 12345
[URF]
Port = 10017
[USRP]
Enable = false
Callsign = ALLSTAR # set to NONE if you don't want to create this client
IPAddress = 1.2.3.4 # the IP address of the USRP client (the Allstar node)
RxPort = 34000
TxPort = 32000
Module = A # this has to be a transcoded module!
#FilePath = /home/usr/clients.txt # a list of listen-only clients
[YSF]
Port = 42000
AutoLinkModule = A # comment out if you want to disable AL
DefaultTxFreq = 446500000
DefaultRxFreq = 446500000
# if you've registered your reflector at register.ysfreflector.de:
RegistrationID = 12345
RegistrationName = US URF???
RegistrationDescription = URF Reflector
######## Database files
[DMR ID DB]
Mode = http #### Mode is "http", "file", or "both"
#### if "both", the url will be read first
FilePath = /home/user/dmrid.dat # for you to add your own values
# will be reloaded within 10s
URL = http://xlxapi.rlx.lu/api/exportdmr.php # if Mode "http" or "both"
RefreshMin = 179
[NXDN ID DB]
Mode = http
FilePath = /home/user/nxdn.dat
URL = https://radioid.net/static/nxdn.csv
RefreshMin = 1440 # radioid.net says this file is updated once/day
[YSF TX/RX DB]
Mode = http
FilePath = /home/user/ysfnode.dat
URL = http://xlxapi.rlx.lu/api/exportysfrepeaters.php
RefreshMin = 191
######### Other File locations
[Files]
PidPath = /var/run/xlxd.pid
XmlPath = /var/log/xlxd.xml
#JsonPath = /var/tmp/urfd.json # for future development
WhitelistPath = /home/user/urfd.whitelist
BlacklistPath = /home/user/urfd.blacklist
InterlinkPath = /home/user/urfd.interlink
G3TerminalPath = /home/user/urfd.terminal

@ -5,12 +5,10 @@
# each entry specifies a remote XLX or XRF to peer with # each entry specifies a remote XLX or XRF to peer with
# format: # format:
# <URF callsign> <ip> <list of modules shared> # <URF callsign> <ip> <list of modules shared>
# <XRF callsign> <ip> <local module><remote module>
# example: # example:
# URF270 158.64.26.132 ACD # URF270 158.64.26.132 ACD
# XRF270 158.64.26.132 BB
# #
# note: the remote XLX must list this XLX in its interlink file # note: the remote URFD must list this in its interlink file
# for the link to be established # for the link to be established
# #
############################################################################# #############################################################################

@ -0,0 +1,6 @@
# this is where the binary will be installed
BINDIR = /usr/local/bin
# besides making an executable that gdb can use,
# this will also provide some additional log messsage
debug = false

@ -5,7 +5,7 @@ After=systemd-user-session.service network.target
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/local/bin/urfd ExecStart=/usr/local/bin/urfd /PATH_TO_INI_FILE
Restart=always Restart=always
[Install] [Install]

@ -50,35 +50,13 @@ InstallReflector () {
if [ ! -e urfd.interlink ]; then if [ ! -e urfd.interlink ]; then
cp ../config/urfd.interlink . cp ../config/urfd.interlink .
fi fi
if [[ "$g3support" == true ]] && [ ! -e urfd.terminal ]; then if [ ! -e urfd.terminal ]; then
cp ../config/urfd.terminal . cp ../config/urfd.terminal .
fi fi
sudo make install || read -p "<Enter> to continue: " ans sudo make install || read -p "<Enter> to continue: " ans
popd popd
} }
Clean () {
pushd reflector
make clean
popd
pushd ../tcd
make clean
popd
}
Compile () {
local np
np=`getconf _NPROCESSORS_ONLN`
pushd reflector
make -j$np || read -p "<Enter to continue: " ans
popd
if [[ "$tcaddress" != none ]]; then
pushd ../tcd
make -j$np || read -p "<Enter to continue: " ans
popd
fi
}
# Execution starts here! # Execution starts here!
# service file locations # service file locations
@ -87,13 +65,25 @@ tcdserv='/etc/systemd/system/tcd.service'
# default values, we only need a few # default values, we only need a few
tcaddress='none' tcaddress='none'
g3support=false
if [ -e reflector.cfg ]; then if [[ $# -eq 1 ]]; then
source reflector.cfg if [ -e $1 ]; then
echo $1 found
callsign=$(awk '$1 == "Callsign"{print $3}' $1)
tcaddress=$(awk '$1 == "Transcoder"{print $3}' $1)
else
echo "$1 not found"
exit 1
fi
else else
echo 'No configuration file found...' if [[ -e reflector/urfd.ini ]]; then
exit 1 callsign=$(awk '$1 == "Callsign"{print $3}' reflector/urfd.ini)
tcaddress=$(awk '$1 == "Transcoder"{print $3}' reflector/urfd.ini)
else
echo "ERROR: could not find reflector/urfd.ini! You'll have to specify it."
echo "Usage: ./radmin PATH-TO-INI-FILE"
exit 1
fi
fi fi
key='x' key='x'
# main loop # main loop
@ -104,18 +94,13 @@ do
if [ "$pcount" -gt 1 ]; then if [ "$pcount" -gt 1 ]; then
echo echo
ps -aux | head -1 ps -aux | head -1
ps -aux | grep -e urfd -e tcd | grep -v grep ps -aux | grep -e urfd -e tcd | grep -v grep | grep -v radmin
fi fi
echo echo
echo " Reflector Administration, Version #211204" echo " Reflector Administration, Version #230308"
echo echo
git status | head -1 git status | head -1
echo echo
echo "ls : List the configuration file"
echo "cl : Clean (remove) compiled objects and executables"
echo "gp : Pull the latest software from the repo"
echo "br : Change git branch to <value>"
echo "co : Compile the system executable(s)"
if [ -e $urfserv ]; then if [ -e $urfserv ]; then
if [ -e $urfserv ]; then if [ -e $urfserv ]; then
echo "us : Uninstall the URF reflector" echo "us : Uninstall the URF reflector"
@ -146,11 +131,7 @@ do
echo echo
read -p "Please input <key> <value> - omit value to toggle a true/false : " key value garbage read -p "Please input <key> <value> - omit value to toggle a true/false : " key value garbage
if [[ "$key" == ls* ]]; then if [[ "$key" == us* ]]; then
cat reflector.cfg;
echo
read -p "<Enter> to return to main menu: " ans
elif [[ "$key" == us* ]]; then
if [ -e $urfserv ]; then if [ -e $urfserv ]; then
UninstallReflector UninstallReflector
fi fi
@ -158,33 +139,8 @@ do
if [ -e reflector/urfd ] && [ ! -e $urfserv ]; then if [ -e reflector/urfd ] && [ ! -e $urfserv ]; then
InstallReflector InstallReflector
fi fi
elif [[ "$key" == gp* ]]; then elif [[ "$key" == rr* ]] && [ -e $urfserv ]; then sudo systemctl restart urfd
echo
git pull
echo
read -p "<Enter> to continue: " ans
pushd ../tcd
git pull
echo
read -p "<Enter> to continue: " ans
popd
elif [[ "$key" == br* ]]; then
echo
git checkout "$value"
echo
read -p "<Enter> to continue: " ans
pushd ../tcd
git checkout "$value"
echo
read -p "<Enter> to continue: " ans
popd
elif [[ "$key" == rr* ]]; then
if [ -e $urfserv ]; then
sudo systemctl restart urfd
fi
elif [[ "$key" == tr* ]] && [ -e $tcdserv ]; then sudo systemctl restart tcd elif [[ "$key" == tr* ]] && [ -e $tcdserv ]; then sudo systemctl restart tcd
elif [[ "$key" == cl* ]]; then Clean
elif [[ "$key" == co* ]]; then Compile
elif [[ "$key" == tl* ]] && [ -e $tcdserv ]; then sudo journalctl -u tcd -f elif [[ "$key" == tl* ]] && [ -e $tcdserv ]; then sudo journalctl -u tcd -f
elif [[ "$key" == rl* ]] && [ -e $urfserv ]; then sudo journalctl -u urfd -f elif [[ "$key" == rl* ]] && [ -e $urfserv ]; then sudo journalctl -u urfd -f
fi fi

@ -1,653 +0,0 @@
#!/bin/bash
#
# Copyright (c) 2021 by Thomas A. Early N7TAE
#
# 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 <http://www.gnu.org/licenses/>.
#!/bin/bash
CharCount () {
local haystack=$1
local test="\${haystack//[^$2]}"
eval local result=$test
return ${#result}
}
RemoveDupes () {
local -n s=$1
local i j
for (( i=0; i<${#s}-1; i++)); do
for (( j=$i+1; j<${#s}; j++)); do
if [[ ${s:$i:1} != "." ]]; then
if [[ ${s:$i:1} == ${s:$j:1} ]]; then
local l=$(($j + 1))
s="${s:0:$j}.${s:$l}"
fi
fi
done
done
s=${s//.}
}
CheckModules () {
# only A through Z
modules=${1//[^A-Z]}
if (( ${#modules} < 1 )); then
unset modules
clear
echo "ERROR: You must specify at least one module, A to Z!"
echo
read -p "<Enter> to continue: " ans
return
fi
RemoveDupes modules
tcmodules=${modules:0:1}
}
CheckTranscodedModules () {
local tc=${1//[^A-Z]}
local tcorig=$tc
if (( ${#tc} < 1 )); then
tcmodules=${modules:0:1}
clear
echo "ERROR: You must specify at least one module!"
echo "If you aren't using a transcoder, set the transcoder"
echo "address value to the default 'none'!"
echo
read -p "<Enter> to continue: " ans
return
fi
RemoveDupes tc
local m=$modules_d
if [ ! -z ${modules+x} ]; then
m=$modules
fi
local i
for ((i=0; i<${#tc}; i++)); do
CharCount $m ${tc:$i:1}
local count=$?
if (( $count < 1 )); then
local j=$((i+1))
tc="${tc:0:$i}.${tc:$j}"
fi
done
tc=${tc//.}
if (( ${#tc} < 1 )); then
echo "ERROR: Module(s), '$tcorig', are not in the configure modules, '$m'!"
echo
read -p "<Enter> to continue: " ans
tcmodules=${modules:0:1}
return
fi
tcmodules=$tc
}
SetBooleanValue ()
{
local dvname
local cv
if [ -z $2 ]; then
if [ -z ${!1+x} ]; then
if [[ "$1" == module_[abc]_* ]]; then
echo matches
dvname=${1//_[abc]_/_x_}
else
echo does not match
dvname=${1}_d
fi
cv=${!dvname}
else
cv=${!1}
fi
if [[ $cv == [tT]* ]]; then
eval ${1}=false
else
eval ${1}=true
fi
elif [[ "$2" == [tT]* ]]; then
eval ${1}=true
else
eval ${1}=false
fi
}
EvaluateVar ()
{
if [ -z ${!1+x} ]; then
if [ -z "${!2}" ]; then
echo "'' <DEFAULT>"
else
echo "${!2} <DEFAULT>"
fi
else
if [ -z "${!1}" ]; then
echo "''"
else
echo "${!1}"
fi
fi
}
TestGainValue ()
{
if [[ "$1" =~ ^[-]?[0-9]+$ ]]; then
if (( $1 >= -24 && $1 <= 24 )); then
return 0
else
return 1
fi
else
return 1
fi
}
AudioGainMenu ()
{
while [[ "$key" != q* ]]
do
clear
echo
echo " DVSI AMBE Chip Audio Gain Menu"
echo
echo " All values are in dB and can be from -24 to 24"
echo " 'input' means the audio amplitude is adjusted before encoding"
echo " 'output' means the audio amplitude is adjusted after decoding"
echo
echo " Non-zero values are generally not required!"
echo " USE AT YOUR OWN RISK!"
echo
echo -n "si : D-Star audio input = "; EvaluateVar dstar_in_gain{,_d}
echo -n "so : D-Star audio output = "; EvaluateVar dstar_out_gain{,_d}
echo -n "mi : DMR/YSF audio input = "; EvaluateVar dmr_in_gain{,_d}
echo -n "mo : DMR/YSF audio output = "; EvaluateVar dmr_out_gain{,_d}
echo
echo "q : Return to Main Menu"
echo "u : Unset the value of <key> (revert to the default value)."
echo
read -p "Please input <key> <value> : " key value
if [[ "$key" == si* ]]; then
TestGainValue "$value" && dstar_in_gain="$value"
elif [[ "$key" == so* ]]; then
TestGainValue "$value" && dstar_out_gain="$value"
elif [[ "$key" == mi* ]]; then
TestGainValue "$value" && dmr_in_gain="$value"
elif [[ "$key" == mo* ]]; then
TestGainValue "$value" && dmr_out_gain="$value"
elif [[ "$key" == u* ]]; then
if [[ "$value" == si* ]]; then
unset dstar_in_gain
elif [[ "$value" == so* ]]; then
unset dstar_out_gain
elif [[ "$value" == mi* ]]; then
unset dmr_in_gain
elif [[ "$value" == mo* ]]; then
unset dmr_out_gain
fi
fi
done
}
WriteMemFile ()
{
local file
file="$rcfg"
echo "# created on `date`" > $file
[ -z ${callsign+x} ] || echo "callsign='$callsign'" >> $file
[ -z ${modules+x} ] || echo "modules='$modules'" >> $file
[ -z ${ip4addr+x} ] || echo "ip4addr='$ip4addr'" >> $file
[ -z ${ip6addr+x} ] || echo "ip6addr='$ip6addr'" >> $file
[ -z ${tcaddress+x} ] || echo "tcaddress='$tcaddress'" >> $file
[ -z ${tcmodules+x} ] || echo "tcmodules='$tcmodules'" >> $file
[ -z ${dstar_in_gain+x} ] || echo "dstar_in_gain=$dstar_in_gain" >> $file
[ -z ${dstar_out_gain+x} ] || echo "dstar_out_gain=$dstar_out_gain" >> $file
[ -z ${dmr_in_gain+x} ] || echo "dmr_in_gain=$dmr_in_gain" >> $file
[ -z ${dmr_out_gain+x} ] || echo "dmr_out_gain=$dmr_out_gain" >> $file
[ -z ${dmrdbuseserver+x} ] || echo "dmrdbuseserver=$dmrdbuseserver" >> $file
[ -z ${dmrdbrefresh+x} ] || echo "dmrdbrefresh=$dmrdbrefresh" >> $file
[ -z ${dmrdbpath+x} ] || echo "dmrdbpath='$dmrdbpath'" >> $file
[ -z ${ysfautolink+x} ] || echo "ysfautolink=$ysfautolink" >> $file
[ -z ${ysfmodule+x} ] || echo "ysfmodule='$ysfmodule'" >> $file
[ -z ${ysflocaldb+x} ] || echo "ysflocaldb=$ysflocaldb" >> $file
[ -z ${ysfdbname+x} ] || echo "ysfdbname='$ysfdbname'" >> $file
[ -z ${ysfdbuser+x} ] || echo "ysfdbuser='$ysfdbuser'" >> $file
[ -z ${ysfrname+x} ] || echo "ysfrname='$ysfrname'" >> $file
[ -z ${ysfrdesc+x} ] || echo "ysfrdesc='$ysfrdesc'" >> $file
[ -z ${ysfdbpw+x} ] || echo "ysfdbpw='$ysfdbpw'" >> $file
[ -z ${g3support+x} ] || echo "g3support=$g3support" >> $file
[ -z ${dbsupport+x} ] || echo "dbsupport=$dbsupport" >> $file
}
WriteSRCHFile ()
{
local file m
file="$srch"
echo "// Created on `date`" > $file
echo "#define CALLSIGN \"${callsign}\"" >> $file
if [[ "$callsign" == XRF* ]]; then
echo "#define NO_XLX" >> $file
fi
if [ -z ${modules+x} ]; then
echo "#define ACTIVE_MODULES \"${modules_d}\"" >> $file
else
echo "#define ACTIVE_MODULES \"${modules}\"" >> $file
fi
if [ ! -z ${ip4addr+x} ]; then
echo "#define LISTEN_IPV4 \"${ip4addr}\"" >> $file
fi
if [ ! -z ${ip6addr+x} ]; then
echo "#define LISTEN_IPV6 \"${ip6addr}\"" >> $file
fi
if [ -z ${ysfautolink+x} ]; then
echo "#define YSF_AUTOLINK_ENABLE ${ysfautolink_d}" >> $file
else
echo "#define YSF_AUTOLINK_ENABLE ${ysfautolink}" >> $file
fi
if [ -z ${ysfmodule+x} ]; then
echo "#define YSF_AUTOLINK_MODULE '${ysfmodule_d}'" >> $file
else
echo "#define YSF_AUTOLINK_MODULE '${ysfmodule}'" >> $file
fi
if [ ! -z ${dmrdbuseserver+x} ]; then
if [[ "$dmrdbuseserver" == true ]]; then
m=1
else
m=0
fi
else
m=1
fi
echo "#define DMRIDDB_USE_RLX_SERVER $m" >> $file
if [ ! -z ${dmrdbrefresh+x} ]; then
echo "#define DMRIDDB_REFRESH_RATE $dmrdbrefresh" >> $file
else
echo "#define DMRIDDB_REFRESH_RATE $dmrdbrefresh_d" >> $file
fi
if [ ! -z ${dmrdbpath+x} ]; then
echo "#define DMRIDDB_PATH \"$dmrdbpath\"" >> $file
else
echo "#define DMRIDDB_PATH \"$dmrdbpath_d\"" >> $file
fi
if [[ "$ysflocaldb" == true ]]; then
echo '#define YSF_DB_SUPPORT true' >> $file
echo "#define YSF_DB_NAME \"$ysfdbname\"" >> $file
echo "#define YSF_DB_USER \"$ysfdbuser\"" >> $file
echo "#define YSF_DB_PASSWORD \"$ysfdbpw\"" >> $file
else
echo '#define YSF_DB_SUPPORT false' >> $file
fi
if [ ! -z ${ysfrname+x} ]; then
echo "#define YSF_REFLECTOR_NAME \"$ysfrname\"" >> $file
fi
if [ ! -z ${ysfrdesc+x} ]; then
echo "#define YSF_REFLECTOR_DESCRIPTION \"$ysfrdesc\"" >> $file
fi
if [ ! -z ${tcaddress+x} ]; then
echo "#define TRANSCODER_IP \"${tcaddress}\"" >> $file
echo "#define TRANSCODED_MODULES \"${tcmodules}\"" >> $file
fi
if [ -z ${g3support+x} ]; then
m=${g3support_d}
else
m=${g3support}
fi
if [[ "$m" != true ]]; then
echo "#define NO_G3" >> $file
fi
if [ -n ${dbsupport+x} ]; then
if [ "$dbsupport" == true ]; then
echo "#define DEBUG" >> $file
fi
fi
}
WriteSRCMKFile ()
{
local file
file="$srcm"
echo "# Created on `date`" > $file
[ -z ${ip4addr+x} ] || echo "ipv4 = $ip4addr" >> $file
[ -z ${ip6addr+x} ] || echo "ipv6 = $ip6addr" >> $file
[ -z ${tcaddress+x} ] || echo "tc_ip = $tcaddress" >> $file
if [ -z ${g3support+x} ]; then
echo "use_g3 = $g3support_d" >> $file
else
echo "use_g3 = $g3support" >> $file
fi
if [ -z ${dbsupport+x} ]; then
echo "debug = $dbsupport_d" >> $file
else
echo "debug = $dbsupport" >> $file
fi
if [ -z ${ysflocaldb+x} ]; then
echo "ysf_db = $ysflocaldb_d" >> $file
else
echo "ysf_db = $ysflocaldb" >> $file
fi
}
WriteTranscoderHFile ()
{
local file
file="$tcdh"
echo "// Created on `date`" > $file
if [ -n ${tcaddress+x} ] && [[ "$tcaddress" != "local" ]]; then
echo "#define TRANSCODER_IP \"${tcaddress}\"" >> $file
fi
if [ -n ${tcmodules+x} ]; then
echo "#define TRANSCODED_MODULES \"${tcmodules}\"" >> $file
fi
if [ -z ${dstar_in_gain+x} ]; then
echo "#define DSTAR_IN_GAIN $dstar_in_gain_d" >> $file
else
echo "#define DSTAR_IN_GAIN $dstar_in_gain" >> $file
fi
if [ -z ${dstar_out_gain+x} ]; then
echo "#define DSTAR_OUT_GAIN $dstar_out_gain_d" >> $file
else
echo "#define DSTAR_OUT_GAIN $dstar_out_gain" >> $file
fi
if [ -z ${dmr_in_gain+x} ]; then
echo "#define DMR_IN_GAIN $dmr_in_gain_d" >> $file
else
echo "#define DMR_IN_GAIN $dmr_in_gain" >> $file
fi
if [ -z ${dmr_out_gain+x} ]; then
echo "#define DMR_OUT_GAIN $dmr_out_gain_d" >> $file
else
echo "#define DMR_OUT_GAIN $dmr_out_gain" >> $file
fi
if [ -n ${dbsupport+x} ]; then
if [ "$dbsupport" == true ]; then
echo "#define DEBUG" >> $file
fi
fi
}
WriteTranscoderMKFile ()
{
local file
file="$tcdm"
echo "# created on `date`" > $file
if [ -z ${dbsupport+x} ]; then
echo "debug = $dbsupport_d" >> $file
else
echo "debug = $dbsupport" >> $file
fi
}
WriteCfgPhpFile ()
{
cat << EOF > $ysfs
<?php
define('DB_SERVER', 'localhost');
define('DB_USERNAME', '${ysfdbuser}');
define('DB_PASSWORD', '${ysfdbpw}');
define('DB_NAME', '${ysfdbname}');
// Attempt to connect to MySQL database
\$link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
// Check connection
if (\$link === false) { die("ERROR: Could not connect. " . mysqli_connect_error()); }
?>
EOF
}
WriteDBCreateFile ()
{
cat << EOF > $dbcr
CREATE DATABASE IF NOT EXISTS ${ysfdbname};
USE ${ysfdbname};
CREATE TABLE IF NOT EXISTS ysfnodes (
callsign VARCHAR(7) PRIMARY KEY,
password VARCHAR(255) NOT NULL,
txfreq INT NOT NULL DEFAULT 446500000,
rxfreq INT NOT NULL DEFAULT 446500000,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE USER IF NOT EXISTS '${ysfdbuser}'@'localhost' IDENTIFIED BY '${ysfdbpw}';
GRANT ALL PRIVILEGES ON $ysfdbname . ysfnodes TO '${ysfdbuser}'@'localhost';
FLUSH PRIVILEGES;
EOF
}
WriteCFGFiles ()
{
WriteMemFile
WriteSRCHFile
WriteSRCMKFile
if [ -z ${tcaddress+x} ]; then
rm -f $tcdh $tcdm
else
WriteTranscoderHFile
WriteTranscoderMKFile
fi
if [[ "$ysflocaldb" == true ]]; then
WriteCfgPhpFile
WriteDBCreateFile
else
rm -f $ysfs $dbcr
fi
}
ListCFGFiles ()
{
echo "===========${rcfg}============="
cat $rcfg
echo "===========${srch}============="
cat $srch
echo "===========${srcm}============="
cat $srcm
if [ ! -z ${tcaddress+x} ]; then
echo "===========${tcdh}============="
cat $tcdh
echo "===========${tcdm}============="
cat $tcdm
fi
if [[ "$ysflocaldb" == true ]]; then
echo "===========${ysfs}============="
cat $ysfs
echo "===========${dbcr}============="
cat $dbcr
fi
}
# Execution starts here!
# file locations
rcfg='reflector.cfg'
srch='reflector/configure.h'
srcm='reflector/configure.mk'
tcdh='../tcd/configure.h'
tcdm='../tcd/configure.mk'
ysfs='wiresx/configure.php'
dbcr='configure.sql'
urfserv='/etc/systemd/system/urfd.service'
# expert mode
if [[ "$1" == ex* ]]; then
expertmode="expertMode"
fi
if [ -e reflector.cfg ]; then
source reflector.cfg
else
echo 'No configuration file found...'
sleep 1
fi
# default values
callsign_d='CHNGME'
modules_d='ABCF'
ip4addr_d='none'
ip6addr_d='none'
tcaddress_d='none'
ysfautolink_d=false
ysfmodule_d='D'
g3support_d=false
dbsupport_d=false
dmrdbuseserver_d=true
dmrdbrefresh_d=180
dmrdbpath_d='/usr/local/etc/dmrid.dat'
ysflocaldb_d=false
ysfdbname_d=''
ysfdbuser_d=''
ysfdbpw_d=''
dstar_in_gain_d=0
dstar_out_gain_d=0
dmr_in_gain_d=0
dmr_out_gain_d=0
if [ -z ${callsign+x} ];then
ysfrname_d="$callsign_d"
else
ysfrname_d="$callsign"
fi
ysfrdesc_d='URF Reflector'
if [ -z ${expertmode+x} ]; then
if [ -e $urfserv ]; then
echo -n "You cannot change the configuration right now beacuse there is an "
if [ -e $urfserv ]; then
echo "URFD server running."
fi;
echo "===========${rcfg}============="
cat $rcfg
echo
echo "Please use radmin to uninstall the running server before attempting to modify the configuration."
exit 1
fi
fi
key='x'
# main loop
while [[ "$key" != q* ]]
do
clear
echo
echo " Reflector Configuration, Version #220326"
echo
echo " ******* REFLECTOR ********"
echo -n "cs : Reflector Callsign = "; EvaluateVar callsign{,_d}
echo -n "am : Active Modules = "; EvaluateVar modules{,_d}
echo -n "g3 : Icom G3 Support = "; EvaluateVar g3support{,_d}
echo " ******* ADDRESSES ********"
echo -n "i4 : IPv4 Listen Address = "; EvaluateVar ip4addr{,_d}
echo -n "i6 : IPv6 Listen Address = "; EvaluateVar ip6addr{,_d}
echo " ******* TRANSCODER ********"
echo " The only TC address supported is 'local' or the default 'none'"
echo -n "tc : Transcoder Address = "; EvaluateVar tcaddress{,_d}
if [ ! -z ${tcaddress+x} ]; then
echo -n "tm : Transcoder Modules = "; EvaluateVar tcmodules{,_d}
echo "ag : AMBE Audio Gain Sub-Menu"
if [ ! -z ${dstar_in_gain+x} ]; then
echo -n " D-Star Input gain = "; EvaluateVar dstar_in_gain{,_d}
fi
if [ ! -z ${dstar_out_gain+x} ]; then
echo -n " D-Star Output gain = "; EvaluateVar dstar_out_gain{,_d}
fi
if [ ! -z ${dmr_in_gain+x} ]; then
echo -n " DMR Input gain = "; EvaluateVar dmr_in_gain{,_d}
fi
if [ ! -z ${dmr_out_gain+x} ]; then
echo -n " DMR Output gain = "; EvaluateVar dmr_out_gain{,_d}
fi
fi
echo " ******* DMR Database ********"
echo -n "ds : Use RLX Server = "; EvaluateVar dmrdbuseserver{,_d}
echo -n "dt : Refresh time (in min) = "; EvaluateVar dmrdbrefresh{,_d}
echo -n "dp : Database path = "; EvaluateVar dmrdbpath{,_d}
echo " ******* SYSTEM FUSION ********"
echo -n "ye : YSF Autolink Enable = "; EvaluateVar ysfautolink{,_d}
if [ ! -z ${ysfautolink+x} ]; then
if [[ "$ysfautolink" == true ]]; then
echo -n "ym : YSF Autolink Module = "; EvaluateVar ysfmodule{,_d}
fi
fi
echo -n "yl : YSF Local Database = "; EvaluateVar ysflocaldb{,_d}
if [[ "$ysflocaldb" == true ]]; then
echo -n "yd : YSF Database Name = "; EvaluateVar ysfdbname{,_d}
echo -n "yu : YSF Database User = "; EvaluateVar ysfdbuser{,_d}
echo -n "yp : YSF Database Password = "; EvaluateVar ysfdbpw{,_d}
fi
echo " ******* YSFReflector Registry *******"
echo -n "rn : Registry Name = "; EvaluateVar ysfrname{,_d}
echo -n "rd : Registry Description = "; EvaluateVar ysfrdesc{,_d}
echo " ******* DEBUGGING ********"
echo -n "db : Debugging Support = "; EvaluateVar dbsupport{,_d}
echo
if [[ "$callsign" == URF* ]]; then
echo "w : Write configuration files (overwrites any existing files) and quit"
fi
echo "q : Quit without saving"
echo "u : Unset the value of <key> (revert to the default value)."
echo
read -p "Please input <key> <value> - omit value to toggle a true/false : " key value
if [[ "$key" == cs* && ${value^^} == URF* ]]; then
callsign="${value^^}"
callsign="${callsign:0:6}"
ysfrname_d="${callsign}"
unset tcaddress tcmodules ysf{autolink,module,localdb,dbname,dbuser,dbpw}
elif [[ "$key" == am* ]]; then CheckModules "${value^^}"
elif [[ "$key" == i4* ]]; then ip4addr="$value"
elif [[ "$key" == i6* ]]; then ip6addr="$value"
elif [[ "$key" == tc* ]]; then tcaddress="local"
elif [[ "$key" == tm* ]]; then CheckTranscodedModules "${value^^}"
elif [[ "$key" == ag* ]]; then
AudioGainMenu
key=x
elif [[ "$key" == ds* ]]; then SetBooleanValue dmrdbuseserver "$value"
elif [[ "$key" == dt* ]]; then dmrdbrefresh="$value"
elif [[ "$key" == dp* ]]; then dmrdbpath="$value"
elif [[ "$key" == ye* ]]; then SetBooleanValue ysfautolink "$value"
elif [[ "$key" == ym* ]]; then
ysfmodule="${value^^}"
ysfmodule="${ysfmodule:0:1}"
elif [[ "$key" == g3* ]]; then SetBooleanValue g3support "$value"
elif [[ "$key" == db* ]]; then SetBooleanValue dbsupport "$value"
elif [[ "$key" == yl* ]]; then SetBooleanValue ysflocaldb "$value"
elif [[ "$key" == yd* ]]; then ysfdbname="$value"
elif [[ "$key" == yu* ]]; then ysfdbuser="$value"
elif [[ "$key" == yp* ]]; then ysfdbpw="$value"
elif [[ "$key" == rn* ]]; then ysfrname="${value:0:16}"
elif [[ "$key" == rd* ]]; then ysfrdesc="${value:0:14}"
elif [[ "$key" == w* ]]; then
WriteCFGFiles
ListCFGFiles
exit 0
elif [[ "$key" == u* ]]; then
if [[ "$value" == cs* ]]; then unset callsign
elif [[ "$value" == am* ]]; then unset modules
elif [[ "$value" == i4* ]]; then unset ip4addr
elif [[ "$value" == i6* ]]; then unset ip6addr
elif [[ "$value" == tc* ]]; then unset tcaddress
elif [[ "$value" == tm* ]]; then tcmodules=${modules:0:1}
elif [[ "$value" == ds* ]]; then unset dmrdbuseserver
elif [[ "$value" == dt* ]]; then unset dmrdbrefresh
elif [[ "$value" == dp* ]]; then unset dmrdbpath
elif [[ "$value" == ye* ]]; then unset ysfautolink ysfmodule
elif [[ "$value" == ym* ]]; then unset ysfmodule
elif [[ "$value" == g3* ]]; then unset g3support
elif [[ "$value" == db* ]]; then unset dbsupport
elif [[ "$value" == yl* ]]; then unset ysflocaldb ysfdbname ysfdbuser ysfdbpw
elif [[ "$value" == yd* ]]; then unset ysfdbname
elif [[ "$value" == yu* ]]; then unset ysfdbuser
elif [[ "$value" == yp* ]]; then unset ysfdbpw
elif [[ "$value" == rn* ]]; then unset ysfrname
elif [[ "$value" == rd* ]]; then unset ysfrdesc
fi
fi
done
exit 0

@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string.h> #include <string.h>
#include "Main.h"
#include "BMClient.h" #include "BMClient.h"
@ -43,5 +43,5 @@ CBmClient::CBmClient(const CBmClient &client)
bool CBmClient::IsAlive(void) const bool CBmClient::IsAlive(void) const
{ {
return (m_LastKeepaliveTime.time() < XLX_KEEPALIVE_TIMEOUT); return (m_LastKeepaliveTime.time() < BM_KEEPALIVE_TIMEOUT);
} }

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CBmClient : public CClient class CBmClient : public CClient

@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Reflector.h" #include "Reflector.h"
#include "BMPeer.h" #include "BMPeer.h"
@ -50,7 +50,7 @@ CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, const char *modules,
bool CBmPeer::IsAlive(void) const bool CBmPeer::IsAlive(void) const
{ {
return (m_LastKeepaliveTime.time() < XLX_KEEPALIVE_TIMEOUT); return (m_LastKeepaliveTime.time() < BM_KEEPALIVE_TIMEOUT);
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////

@ -18,18 +18,16 @@
#include <string.h> #include <string.h>
#include "Main.h"
#include "BMPeer.h" #include "BMPeer.h"
#include "BMProtocol.h" #include "BMProtocol.h"
#include "Reflector.h" #include "Global.h"
#include "GateKeeper.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// operation // operation
bool CBMProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6) bool CBMProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{ {
m_HasTranscoder = g_Configure.IsString(g_Keys.modules.tcmodules);
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false; return false;
@ -167,7 +165,7 @@ void CBMProtocol::Task(void)
HandleQueue(); HandleQueue();
// keep alive // keep alive
if ( m_LastKeepaliveTime.time() > XLX_KEEPALIVE_PERIOD ) if ( m_LastKeepaliveTime.time() > BM_KEEPALIVE_PERIOD )
{ {
// handle keep alives // handle keep alives
HandleKeepalives(); HandleKeepalives();
@ -177,7 +175,7 @@ void CBMProtocol::Task(void)
} }
// peer connections // peer connections
if ( m_LastPeersLinkTime.time() > XLX_RECONNECT_PERIOD ) if ( m_LastPeersLinkTime.time() > BM_RECONNECT_PERIOD )
{ {
// handle remote peers connections // handle remote peers connections
HandlePeerLinks(); HandlePeerLinks();
@ -192,11 +190,10 @@ void CBMProtocol::Task(void)
void CBMProtocol::HandleQueue(void) void CBMProtocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// encode it // encode it
CBuffer buffer; CBuffer buffer;
@ -228,11 +225,10 @@ void CBMProtocol::HandleQueue(void)
break; break;
case EProtoRev::ambe: case EProtoRev::ambe:
default: default:
#ifdef TRANSCODED_MODULES if (m_HasTranscoder)
Send(buffer, client->GetIp()); Send(buffer, client->GetIp());
#else else
Send(bufferLegacy, client->GetIp()); Send(bufferLegacy, client->GetIp());
#endif
break; break;
} }
} }
@ -240,7 +236,6 @@ void CBMProtocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -324,7 +319,7 @@ void CBMProtocol::HandlePeerLinks(void)
it->ResolveIp(); it->ResolveIp();
// send connect packet to re-initiate peer link // send connect packet to re-initiate peer link
EncodeConnectPacket(&buffer, it->GetModules()); EncodeConnectPacket(&buffer, it->GetModules());
Send(buffer, it->GetIp(), XLX_PORT); Send(buffer, it->GetIp(), m_Port);
std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl; std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl;
} }
} }

@ -18,6 +18,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Defines.h"
#include "Version.h" #include "Version.h"
#include "Timer.h" #include "Timer.h"
#include "SEProtocol.h" #include "SEProtocol.h"
@ -72,4 +73,6 @@ protected:
// time // time
CTimer m_LastKeepaliveTime; CTimer m_LastKeepaliveTime;
CTimer m_LastPeersLinkTime; CTimer m_LastPeersLinkTime;
// config data;
bool m_HasTranscoder;
}; };

@ -16,6 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <iostream>
#include <string.h> #include <string.h>
#include "Buffer.h" #include "Buffer.h"
@ -175,7 +176,7 @@ CBuffer::operator const char *() const
void CBuffer::DebugDump(std::ofstream &debugout) const void CBuffer::DebugDump(std::ofstream &debugout) const
{ {
// dump an hex line // dump a hex line
for ( unsigned int i = 0; i < m_data.size(); i++ ) for ( unsigned int i = 0; i < m_data.size(); i++ )
{ {
char sz[16]; char sz[16];

@ -20,7 +20,6 @@
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include "Main.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////

@ -23,12 +23,6 @@
#include "Notification.h" #include "Notification.h"
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// class
class CNotificationQueue class CNotificationQueue
{ {
public: public:

@ -27,6 +27,18 @@
#define CALLSIGN_LEN 8 #define CALLSIGN_LEN 8
#define CALLSUFFIX_LEN 4 #define CALLSUFFIX_LEN 4
union UCallsign
{
char c[CALLSIGN_LEN];
uint64_t l;
};
union USuffix
{
char c[CALLSUFFIX_LEN];
uint32_t u;
};
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// class // class
@ -36,7 +48,9 @@ class CCallsign
public: public:
// contructors // contructors
CCallsign(); CCallsign();
CCallsign(const char *, uint32_t = 0, uint16_t = 0); CCallsign(const UCallsign &cs); // no id lookup
CCallsign(const CCallsign &cs);
CCallsign(const std::string &cs, uint32_t dmrid = 0, uint16_t nxdnid = 0);
// status // status
bool IsValid(void) const; bool IsValid(void) const;
@ -44,23 +58,24 @@ public:
bool HasModule(void) const { return m_Module != ' '; } bool HasModule(void) const { return m_Module != ' '; }
// set // set
void SetCallsign(const char *, bool = true); void SetCallsign(const std::string &s, bool updateids = true);
void SetCallsign(const uint8_t *, int, bool = true); void SetCallsign(const uint8_t *, int, bool = true);
void SetDmrid(uint32_t, bool = true); void SetDmrid(uint32_t, bool = true);
void SetDmrid(const uint8_t *, bool = true); void SetDmrid(const uint8_t *, bool = true);
void SetNXDNid(uint16_t, bool = true); void SetNXDNid(uint16_t, bool = true);
void SetNXDNid(const uint8_t *, bool = true); void SetNXDNid(const uint8_t *, bool = true);
void SetCSModule(char); void SetCSModule(char);
void SetSuffix(const char *); void SetSuffix(const std::string &s);
void SetSuffix(const uint8_t *, int); void SetSuffix(const uint8_t *, int);
// modify // modify
void PatchCallsign(int, const char *, int); void PatchCallsign(int, const char *, int);
// get // get
UCallsign GetKey() const;
void GetCallsign(uint8_t *) const; void GetCallsign(uint8_t *) const;
void GetCallsignString(char *) const; void GetCallsignString(char *) const;
const std::string GetCS(unsigned len = 9) const; const std::string GetCS() const;
uint32_t GetDmrid(void) const { return m_uiDmrid; } uint32_t GetDmrid(void) const { return m_uiDmrid; }
uint16_t GetNXDNid(void) const { return m_uiNXDNid; } uint16_t GetNXDNid(void) const { return m_uiNXDNid; }
void GetSuffix(uint8_t *) const; void GetSuffix(uint8_t *) const;
@ -73,6 +88,7 @@ public:
bool HasSameModule(const CCallsign &) const; bool HasSameModule(const CCallsign &) const;
// operators // operators
CCallsign &operator = (const CCallsign &cs);
bool operator ==(const CCallsign &) const; bool operator ==(const CCallsign &) const;
operator const char *() const; operator const char *() const;
@ -92,11 +108,10 @@ protected:
protected: protected:
// data // data
char m_Callsign[CALLSIGN_LEN]; UCallsign m_Callsign;
char m_Suffix[CALLSUFFIX_LEN]; USuffix m_Suffix;
char m_Module; char m_Module;
mutable char m_sz[CALLSIGN_LEN+CALLSUFFIX_LEN+5]; uint32_t m_uiDmrid;
uint32_t m_uiDmrid; uint16_t m_uiNXDNid;
uint16_t m_uiNXDNid; uint64_t m_coded; // M17 encoded callsign
uint64_t m_coded; // M17 encoded callsign
}; };

@ -16,10 +16,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <fstream>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "Main.h"
#include "CallsignList.h" #include "CallsignList.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -27,14 +28,13 @@
CCallsignList::CCallsignList() CCallsignList::CCallsignList()
{ {
m_Filename = nullptr;
memset(&m_LastModTime, 0, sizeof(time_t)); memset(&m_LastModTime, 0, sizeof(time_t));
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// file io // file io
bool CCallsignList::LoadFromFile(const char *filename) bool CCallsignList::LoadFromFile(const std::string &filename)
{ {
bool ok = false; bool ok = false;
char sz[256]; char sz[256];
@ -77,7 +77,7 @@ bool CCallsignList::LoadFromFile(const char *filename)
file.close(); file.close();
// keep file path // keep file path
m_Filename = filename; m_Filename.assign(filename);
// update time // update time
GetLastModTime(&m_LastModTime); GetLastModTime(&m_LastModTime);
@ -99,7 +99,7 @@ bool CCallsignList::ReloadFromFile(void)
{ {
bool ok = false; bool ok = false;
if ( m_Filename != nullptr ) if (! m_Filename.empty())
{ {
ok = LoadFromFile(m_Filename); ok = LoadFromFile(m_Filename);
} }
@ -210,10 +210,10 @@ bool CCallsignList::GetLastModTime(time_t *time)
{ {
bool ok = false; bool ok = false;
if ( m_Filename != nullptr ) if (! m_Filename.empty())
{ {
struct stat fileStat; struct stat fileStat;
if( ::stat(m_Filename, &fileStat) != -1 ) if( ::stat(m_Filename.c_str(), &fileStat) != -1 )
{ {
*time = fileStat.st_mtime; *time = fileStat.st_mtime;
ok = true; ok = true;

@ -17,7 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include "Main.h"
#include <list>
#include <mutex>
#include "CallsignListItem.h" #include "CallsignListItem.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -29,15 +32,12 @@ public:
// constructor // constructor
CCallsignList(); CCallsignList();
// destructor
virtual ~CCallsignList() {}
// locks // locks
void Lock(void) { m_Mutex.lock(); } void Lock(void) const { m_Mutex.lock(); }
void Unlock(void) { m_Mutex.unlock(); } void Unlock(void) const { m_Mutex.unlock(); }
// file io // file io
virtual bool LoadFromFile(const char *); virtual bool LoadFromFile(const std::string &str);
bool ReloadFromFile(void); bool ReloadFromFile(void);
bool NeedReload(void); bool NeedReload(void);
@ -60,8 +60,8 @@ protected:
char *TrimWhiteSpaces(char *); char *TrimWhiteSpaces(char *);
// data // data
std::mutex m_Mutex; mutable std::mutex m_Mutex;
const char * m_Filename; std::string m_Filename;
time_t m_LastModTime; time_t m_LastModTime;
std::list<CCallsignListItem> m_Callsigns; std::list<CCallsignListItem> m_Callsigns;
}; };

@ -17,7 +17,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string.h> #include <string.h>
#include "Main.h"
#include "Global.h"
#include "CallsignListItem.h" #include "CallsignListItem.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -31,6 +33,7 @@ CCallsignListItem::CCallsignListItem()
CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, const char *modules) CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, const char *modules)
{ {
const std::string mods(g_Configure.GetString(g_Keys.modules.modules));
m_Callsign = callsign; m_Callsign = callsign;
memset(m_szUrl, 0, sizeof(m_szUrl)); memset(m_szUrl, 0, sizeof(m_szUrl));
m_Ip = ip; m_Ip = ip;
@ -39,14 +42,14 @@ CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, c
memset(m_Modules, 0, sizeof(m_Modules)); memset(m_Modules, 0, sizeof(m_Modules));
if ( modules[0] == '*' ) if ( modules[0] == '*' )
{ {
memcpy(m_Modules, ACTIVE_MODULES, sizeof(ACTIVE_MODULES)); memcpy(m_Modules, mods.c_str(), mods.size());
} }
else else
{ {
int n = MIN(::strlen(modules), sizeof(m_Modules)-1); int n = MIN(::strlen(modules), sizeof(m_Modules)-1);
for (int i=0, j=0; i<n; i++) for (int i=0, j=0; i<n; i++)
{ {
if (strchr(ACTIVE_MODULES, modules[i])) if (std::string::npos != mods.find(modules[i]))
{ {
m_Modules[j++] = modules[i]; m_Modules[j++] = modules[i];
} }
@ -57,6 +60,7 @@ CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, c
CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url, const char *modules) CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url, const char *modules)
{ {
const std::string mods(g_Configure.GetString(g_Keys.modules.modules));
m_Callsign = callsign; m_Callsign = callsign;
::strncpy(m_szUrl, url, URL_MAXLEN); ::strncpy(m_szUrl, url, URL_MAXLEN);
m_Ip = CIp(m_szUrl); m_Ip = CIp(m_szUrl);
@ -65,14 +69,14 @@ CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url,
memset(m_Modules, 0, sizeof(m_Modules)); memset(m_Modules, 0, sizeof(m_Modules));
if ( modules[0] == '*' ) if ( modules[0] == '*' )
{ {
memcpy(m_Modules, ACTIVE_MODULES, sizeof(ACTIVE_MODULES)); memcpy(m_Modules, mods.c_str(), mods.size());
} }
else else
{ {
int n = MIN(::strlen(modules), sizeof(m_Modules)-1); int n = MIN(::strlen(modules), sizeof(m_Modules)-1);
for (int i=0, j=0; i<n; i++) for (int i=0, j=0; i<n; i++)
{ {
if (strchr(ACTIVE_MODULES, modules[i])) if (std::string::npos != mods.find(modules[i]))
{ {
m_Modules[j++] = modules[i]; m_Modules[j++] = modules[i];
} }

@ -18,7 +18,7 @@
#pragma once #pragma once
#include "Main.h"
#include "Callsign.h" #include "Callsign.h"
#include "IP.h" #include "IP.h"

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Client.h" #include "Client.h"
@ -96,21 +96,14 @@ void CClient::WriteXml(std::ofstream &xmlFile)
xmlFile << "</NODE>" << std::endl; xmlFile << "</NODE>" << std::endl;
} }
void CClient::GetJsonObject(char *Buffer) void CClient::JsonReport(nlohmann::json &report)
{ {
char sz[512]; nlohmann::json jclient;
char mbstr[100]; jclient["Callsign"] = m_Callsign.GetCS();
char cs[16]; jclient["OnModule"] = std::string(1, m_ReflectorModule);
jclient["Protocol"] = GetProtocolName();
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) char s[100];
{ if (std::strftime(s, sizeof(s), "%FT%TZ", std::gmtime(&m_ConnectTime)))
m_Callsign.GetCallsignString(cs); jclient["ConnectTime"] = s;
report["Clients"].push_back(jclient);
::sprintf(sz, "{\"callsign\":\"%s\",\"module\":\"%c\",\"linkedto\":\"%c\",\"time\":\"%s\"}",
cs,
m_Callsign.GetCSModule(),
m_ReflectorModule,
mbstr);
::strcat(Buffer, sz);
}
} }

@ -18,6 +18,9 @@
#pragma once #pragma once
#include <nlohmann/json.hpp>
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "IP.h" #include "IP.h"
#include "Callsign.h" #include "Callsign.h"
@ -44,13 +47,13 @@ public:
const CCallsign &GetCallsign(void) const { return m_Callsign; } const CCallsign &GetCallsign(void) const { return m_Callsign; }
const CIp &GetIp(void) const { return m_Ip; } const CIp &GetIp(void) const { return m_Ip; }
bool HasModule(void) const { return m_Callsign.HasModule(); } bool HasModule(void) const { return m_Callsign.HasModule(); }
char GetCSModule(void) const { return m_Callsign.GetCSModule(); } char GetCSModule(void) const { return m_Callsign.GetCSModule(); }
bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; } bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; }
char GetReflectorModule(void) const { return m_ReflectorModule; } char GetReflectorModule(void) const { return m_ReflectorModule; }
// set // set
void SetCSModule(char c) { m_Callsign.SetCSModule(c); } void SetCSModule(char c) { m_Callsign.SetCSModule(c); }
void SetReflectorModule(char c) { m_ReflectorModule = c; } void SetReflectorModule(char c) { m_ReflectorModule = c; }
// identity // identity
virtual EProtocol GetProtocol(void) const { return EProtocol::none; } virtual EProtocol GetProtocol(void) const { return EProtocol::none; }
@ -71,7 +74,7 @@ public:
// reporting // reporting
virtual void WriteXml(std::ofstream &); virtual void WriteXml(std::ofstream &);
virtual void GetJsonObject(char *); void JsonReport(nlohmann::json &report);
protected: protected:
// data // data

@ -16,15 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "Reflector.h"
#include "Clients.h"
#include "Global.h"
#include "Clients.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor // constructor
CClients::CClients() CClients::CClients()
{ {
} }

@ -16,29 +16,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "CodecStream.h"
#include "DVFramePacket.h" #include "DVFramePacket.h"
#include "Reflector.h" #include "PacketStream.h"
#include "CodecStream.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor // constructor
CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16_t streamid, ECodecType type, std::shared_ptr<CUnixDgramReader> reader) CCodecStream::CCodecStream(CPacketStream *PacketStream, char module) : m_CSModule(module), m_IsOpen(false)
{ {
keep_running = true;
m_uiStreamId = streamid;
m_uiPid = 0;
m_eCodecIn = type;
m_RTMin = -1;
m_RTMax = -1;
m_RTSum = 0;
m_RTCount = 0;
m_uiTotalPackets = 0;
m_PacketStream = PacketStream; m_PacketStream = PacketStream;
m_TCReader = reader;
InitCodecStream();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -52,7 +41,27 @@ CCodecStream::~CCodecStream()
{ {
m_Future.get(); m_Future.get();
} }
// and close the socket
m_TCReader.Close();
}
void CCodecStream::ResetStats(uint16_t streamid, ECodecType type)
{
m_IsOpen = true;
keep_running = true;
m_uiStreamId = streamid;
m_uiPid = 0;
m_eCodecIn = type;
m_RTMin = -1;
m_RTMax = -1;
m_RTSum = 0;
m_RTCount = 0;
m_uiTotalPackets = 0;
}
void CCodecStream::ReportStats()
{
m_IsOpen = false;
// display stats // display stats
if (m_RTCount > 0) if (m_RTCount > 0)
{ {
@ -61,7 +70,7 @@ CCodecStream::~CCodecStream()
double ave = 1000.0 * m_RTSum / double(m_RTCount); double ave = 1000.0 * m_RTSum / double(m_RTCount);
auto prec = std::cout.precision(); auto prec = std::cout.precision();
std::cout.precision(1); std::cout.precision(1);
std::cout << std::fixed << "TC round-trip time(ms): " << min << "/" << ave << "/" << max << ", " << m_RTCount << " total packets" << std::endl; std::cout << std::fixed << "TC round-trip time(ms): " << min << '/' << ave << '/' << max << ", " << m_RTCount << " total packets" << std::endl;
std::cout.precision(prec); std::cout.precision(prec);
} }
} }
@ -69,11 +78,26 @@ CCodecStream::~CCodecStream()
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// initialization // initialization
void CCodecStream::InitCodecStream(void) bool CCodecStream::InitCodecStream()
{ {
m_TCWriter.SetUp(REF2TC); m_TCWriter.SetUp(REF2TC);
std::string name(TC2REF);
name.append(1, m_CSModule);
if (m_TCReader.Open(name.c_str()))
return true;
std::cout << "Initialized CodecStream receive socket " << name << std::endl;
keep_running = true; keep_running = true;
m_Future = std::async(std::launch::async, &CCodecStream::Thread, this); try
{
m_Future = std::async(std::launch::async, &CCodecStream::Thread, this);
}
catch(const std::exception& e)
{
std::cerr << "Could not start Codec processing on module '" << m_CSModule << "': " << e.what() << std::endl;
m_TCReader.Close();
return true;
}
return false;
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -92,36 +116,38 @@ void CCodecStream::Task(void)
STCPacket pack; STCPacket pack;
// any packet from transcoder // any packet from transcoder
if (m_TCReader->Receive(&pack, 5)) if (m_TCReader.Receive(&pack, 5))
{ {
// update statistics // update statistics
double rt = pack.rt_timer.time(); // the round-trip time double rt = pack.rt_timer.time(); // the round-trip time
if ( m_RTMin == -1 ) if (0 == m_RTCount)
{ {
m_RTMin = rt; m_RTMin = rt;
m_RTMax = rt; m_RTMax = rt;
} }
else else
{ {
m_RTMin = MIN(m_RTMin, rt); if (rt < m_RTMin)
m_RTMax = MAX(m_RTMax, rt); m_RTMin = rt;
else if (rt > m_RTMax)
m_RTMax = rt;
} }
m_RTSum += rt; m_RTSum += rt;
m_RTCount++; m_RTCount++;
if ( m_LocalQueue.empty() ) if ( m_LocalQueue.IsEmpty() )
{ {
std::cout << "Unexpected transcoded packet received from transcoder" << std::endl; std::cout << "Unexpected transcoded packet received from transcoder: Module='" << pack.module << "' StreamID=" << std::hex << std::showbase << ntohs(pack.streamid) << std::endl;
} }
else else if (m_IsOpen)
{ {
// pop the original packet // pop the original packet
auto Packet = m_LocalQueue.pop(); auto Packet = m_LocalQueue.Pop();
auto Frame = (CDvFramePacket *)Packet.get(); auto Frame = (CDvFramePacket *)Packet.get();
// do things look okay? // do things look okay?
if (pack.module != m_CSModule)
std::cerr << "CodecStream '" << m_CSModule << "' received a transcoded packet from module '" << pack.module << "'" << std::dec << std::noshowbase << std::endl;
if (pack.sequence != Frame->GetCodecPacket()->sequence) if (pack.sequence != Frame->GetCodecPacket()->sequence)
std::cerr << "Sequence mismatch: this voice frame=" << Frame->GetCodecPacket()->sequence << " returned transcoder packet=" << pack.sequence << std::endl; std::cerr << "Sequence mismatch: this voice frame=" << Frame->GetCodecPacket()->sequence << " returned transcoder packet=" << pack.sequence << std::endl;
if (pack.streamid != Frame->GetCodecPacket()->streamid) if (pack.streamid != Frame->GetCodecPacket()->streamid)
@ -137,31 +163,35 @@ void CCodecStream::Task(void)
} }
// and push it back to client // and push it back to client
m_PacketStream->Lock(); m_PacketStream->ReturnPacket(std::move(Packet));
m_PacketStream->push(Packet); }
m_PacketStream->Unlock(); else
{
std::cout << "Transcoder packet received but CodecStream[" << m_CSModule << "] is closed: Module='" << pack.module << "' StreamID=" << std::hex << std::showbase << ntohs(pack.streamid) << std::endl;
} }
} }
// anything in our queue // anything in our queue
while ( !empty() ) auto Packet = m_Queue.Pop();
while (Packet)
{ {
// yes, pop it from queue
auto Packet = pop();
// we need a CDvFramePacket pointer to access Frame stuff // we need a CDvFramePacket pointer to access Frame stuff
auto Frame = (CDvFramePacket *)Packet.get(); auto Frame = (CDvFramePacket *)Packet.get();
// push to our local queue so it can wait for the transcoder if (m_IsOpen)
m_LocalQueue.push(Packet); {
// update important stuff in Frame->m_TCPack for the transcoder
// sets the packet counter, stream id, last_packet, module and start the trip timer
Frame->SetTCParams(m_uiTotalPackets++);
// update important stuff in Frame->m_TCPack for the transcoder // now send to transcoder
Frame->SetTCParams(m_uiTotalPackets++); m_TCWriter.Send(Frame->GetCodecPacket());
// push to our local queue where it can wait for the transcoder
m_LocalQueue.Push(std::move(Packet));
}
// now send to transcoder // get the next packet, if there is one
// this assume that thread pushing the Packet Packet = m_Queue.Pop();
// have verified that the CodecStream is connected
// and that the packet needs transcoding
m_TCWriter.Send(Frame->GetCodecPacket());
} }
} }

@ -18,19 +18,27 @@
#pragma once #pragma once
#include <atomic>
#include <future>
#include "DVFramePacket.h"
#include "UnixDgramSocket.h" #include "UnixDgramSocket.h"
#include "PacketQueue.h" #include "SafePacketQueue.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// class // class
class CPacketStream; class CPacketStream;
class CCodecStream : public CPacketQueue class CCodecStream
{ {
public: public:
// constructor // constructor
CCodecStream(CPacketStream *packetstream, uint16_t streamid, ECodecType codectype, std::shared_ptr<CUnixDgramReader> reader); CCodecStream(CPacketStream *packetstream, char module);
bool InitCodecStream();
void ResetStats(uint16_t streamid, ECodecType codectype);
void ReportStats();
// destructor // destructor
virtual ~CCodecStream(); virtual ~CCodecStream();
@ -42,9 +50,14 @@ public:
void Thread(void); void Thread(void);
void Task(void); void Task(void);
// pass-thru
void Push(std::unique_ptr<CPacket> p) { m_Queue.Push(std::move(p)); }
protected: protected:
// initialization // identity
void InitCodecStream(void); const char m_CSModule;
// state
std::atomic<bool> m_IsOpen;
// data // data
uint16_t m_uiStreamId; uint16_t m_uiStreamId;
uint16_t m_uiPort; uint16_t m_uiPort;
@ -52,12 +65,14 @@ protected:
ECodecType m_eCodecIn; ECodecType m_eCodecIn;
// sockets // sockets
std::shared_ptr<CUnixDgramReader> m_TCReader; CUnixDgramReader m_TCReader;
CUnixDgramWriter m_TCWriter; CUnixDgramWriter m_TCWriter;
// associated packet stream // associated packet stream
CPacketStream *m_PacketStream; CPacketStream *m_PacketStream;
CPacketQueue m_LocalQueue;
// queues
CSafePacketQueue<std::unique_ptr<CPacket>> m_LocalQueue, m_Queue;
// thread // thread
std::atomic<bool> keep_running; std::atomic<bool> keep_running;

@ -0,0 +1,988 @@
/*
* Copyright (c) 2023 by Thomas A. Early N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/stat.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>
#include <vector>
#include <list>
#include <algorithm>
#include <regex>
#include "Global.h"
#include "CurlGet.h"
// ini file keywords
#define JAUTOLINKMODULE "AutoLinkModule"
#define JBLACKLISTPATH "BlacklistPath"
#define JBRANDMEISTER "Brandmeister"
#define JCALLSIGN "Callsign"
#define JCOUNTRY "Country"
#define JDCS "DCS"
#define JDEFAULTID "DefaultId"
#define JDEFAULTRXFREQ "DefaultRxFreq"
#define JDEFAULTTXFREQ "DefaultTxFreq"
#define JDESCRIPTION "Description"
#define JDEXTRA "DExtra"
#define JDMRIDDB "DMR ID DB"
#define JDMRPLUS "DMRPlus"
#define JDPLUS "DPlus"
#define JENABLE "Enable"
#define JFILES "Files"
#define JFILEPATH "FilePath"
#define JG3 "G3"
#define JG3TERMINALPATH "G3TerminalPath"
#define JINTERLINKPATH "InterlinkPath"
#define JIPADDRESS "IPAddress"
#define JIPADDRESSES "IP Addresses"
#define JIPV4BINDING "IPv4Binding"
#define JIPV4EXTERNAL "IPv4External"
#define JIPV6BINDING "IPv6Binding"
#define JIPV6EXTERNAL "IPv6External"
#define JJSONPATH "JsonPath"
#define JM17 "M17"
#define JMMDVM "MMDVM"
#define JMODE "Mode"
#define JMODULE "Module"
#define JMODULES "Modules"
#define JNAMES "Names"
#define JNXDNIDDB "NXDN ID DB"
#define JNXDN "NXDN"
#define JP25 "P25"
#define JPIDPATH "PidPath"
#define JPORT "Port"
#define JREFLECTORID "ReflectorID"
#define JREFRESHMIN "RefreshMin"
#define JREGISTRATIONDESCRIPTION "RegistrationDescription"
#define JREGISTRATIONID "RegistrationID"
#define JREGISTRATIONNAME "RegistrationName"
#define JRXPORT "RxPort"
#define JSPONSOR "Sponsor"
#define JSYSOPEMAIL "SysopEmail"
#define JTRANSCODED "Transcoded"
#define JTRANSCODER "Transcoder"
#define JTXPORT "TxPort"
#define JURF "URF"
#define JURL "URL"
#define JUSRP "USRP"
#define JWHITELISTPATH "WhitelistPath"
#define JXMLPATH "XmlPath"
#define JYSF "YSF"
#define JYSFTXRXDB "YSF TX/RX DB"
static inline void split(const std::string &s, char delim, std::vector<std::string> &v)
{
std::istringstream iss(s);
std::string item;
while (std::getline(iss, item, delim))
v.push_back(item);
}
// trim from start (in place)
static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// trim from both ends (in place)
static inline void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
CConfigure::CConfigure()
{
IPv4RegEx = std::regex("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3,3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]){1,1}$", std::regex::extended);
IPv6RegEx = std::regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,1}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){1,1}(:[0-9a-fA-F]{1,4}){1,6}|:((:[0-9a-fA-F]{1,4}){1,7}|:))$", std::regex::extended);
}
bool CConfigure::ReadData(const std::string &path)
// returns true on failure
{
bool rval = false;
ESection section = ESection::none;
counter = 0;
SJsonKeys::DB *pdb;
//data.ysfalmodule = 0;
//data.DPlusPort = data.DCSPort = data.DExtraPort = data.BMPort = data.DMRPlusPort = 0;
std::ifstream cfgfile(path.c_str(), std::ifstream::in);
if (! cfgfile.is_open()) {
std::cerr << "ERROR: '" << path << "' was not found!" << std::endl;
return true;
}
std::string ipv4, ipv6;
{
CCurlGet curl;
std::stringstream ss;
if (CURLE_OK == curl.GetURL("https://ipv4.icanhazip.com", ss))
{
ipv4.assign(ss.str());
trim(ipv4);
}
ss.str(std::string());
if (CURLE_OK == curl.GetURL("https://ipv6.icanhazip.com", ss))
{
ipv6.assign(ss.str());
trim(ipv6);
}
}
std::string line;
while (std::getline(cfgfile, line))
{
counter++;
trim(line);
if (3 > line.size())
continue; // can't be anything
if ('#' == line.at(0))
continue; // skip comments
// check for next section
if ('[' == line.at(0))
{
std::string hname(line.substr(1));
auto pos = hname.find(']');
if (std::string::npos != pos)
hname.resize(pos);
section = ESection::none;
if (0 == hname.compare(JNAMES))
section = ESection::names;
else if (0 == hname.compare(JIPADDRESSES))
section = ESection::ip;
else if (0 == hname.compare(JMODULES))
section = ESection::modules;
else if (0 == hname.compare(JDPLUS))
section = ESection::dplus;
else if (0 == hname.compare(JDEXTRA))
section = ESection::dextra;
else if (0 == hname.compare(JG3))
section = ESection::g3;
else if (0 == hname.compare(JDMRPLUS))
section = ESection::dmrplus;
else if (0 == hname.compare(JMMDVM))
section = ESection::mmdvm;
else if (0 == hname.compare(JNXDN))
section = ESection::nxdn;
else if (0 == hname.compare(JBRANDMEISTER))
section = ESection::bm;
else if (0 == hname.compare(JYSF))
section = ESection::ysf;
else if (0 == hname.compare(JDCS))
section = ESection::dcs;
else if (0 == hname.compare(JP25))
section = ESection::p25;
else if (0 == hname.compare(JM17))
section = ESection::m17;
else if (0 == hname.compare(JUSRP))
section = ESection::usrp;
else if (0 == hname.compare(JURF))
section = ESection::urf;
else if (0 == hname.compare(JDMRIDDB))
section = ESection::dmrid;
else if (0 == hname.compare(JNXDNIDDB))
section = ESection::nxdnid;
else if (0 == hname.compare(JYSFTXRXDB))
section = ESection::ysffreq;
else if (0 == hname.compare(JFILES))
section = ESection::files;
else
{
std::cerr << "WARNING: unknown ini file section: " << line << std::endl;
}
continue;
}
std::vector<std::string> tokens;
split(line, '=', tokens);
// check value for end-of-line comment
if (2 > tokens.size())
{
std::cout << "WARNING: line #" << counter << ": '" << line << "' does not contain an equal sign, skipping" << std::endl;
continue;
}
auto pos = tokens[1].find('#');
if (std::string::npos != pos)
{
tokens[1].assign(tokens[1].substr(0, pos));
rtrim(tokens[1]); // whitespace between the value and the end-of-line comment
}
// trim whitespace from around the '='
rtrim(tokens[0]);
ltrim(tokens[1]);
const std::string key(tokens[0]);
const std::string value(tokens[1]);
if (key.empty() || value.empty())
{
std::cout << "WARNING: line #" << counter << ": missing key or value: '" << line << "'" << std::endl;
continue;
}
switch (section)
{
case ESection::names:
if (0 == key.compare(JCALLSIGN))
data[g_Keys.names.callsign] = value;
else if (0 == key.compare(JSYSOPEMAIL))
data[g_Keys.names.email] = value;
else if (0 == key.compare(JCOUNTRY))
data[g_Keys.names.country] = value.substr(0,2);
else if (0 == key.compare(JSPONSOR))
data[g_Keys.names.sponsor] = value;
else
badParam(key);
break;
case ESection::ip:
if (0 == key.compare(JIPV4BINDING))
{
data[g_Keys.ip.ipv4bind] = value;
}
else if (0 == key.compare(JIPV6BINDING))
{
data[g_Keys.ip.ipv6bind] = value;
}
else if (0 == key.compare(JIPV4EXTERNAL))
{
data[g_Keys.ip.ipv4address] = value;
}
else if (0 == key.compare(JIPV6EXTERNAL))
{
data[g_Keys.ip.ipv6address] = value;
}
else if (0 == key.compare(JTRANSCODER))
{
if (value.compare("local"))
{
std::cout << "WARNING: Line #" << counter << ": malformed transcoder address, '" << value << "', resetting..." << std::endl;
}
data[g_Keys.ip.transcoder] = "local";
}
else
badParam(key);
break;
case ESection::modules:
if (0 == key.compare(JMODULES))
{
std::string m(value);
if (checkModules(m))
{
std::cerr << "ERROR: line #" << counter << ": no letters found in Modules: '" << m << "'" << std::endl;
rval = true;
} else
data[g_Keys.modules.modules] = m;
}
else if (0 == key.compare(JTRANSCODED))
{
std::string m(value);
if (checkModules(m))
{
std::cerr << "ERROR: line #" << counter << ": no letters found in Transcoded: '" << m << "'" << std::endl;
rval = true;
} else
data[g_Keys.modules.tcmodules] = m;
}
else if (0 == key.compare(0, 11, "Description"))
{
if (12 == key.size() && isupper(key[11]))
data[key] = value;
else
badParam(key);
}
else
badParam(key);
break;
case ESection::bm:
if (0 == key.compare(JPORT))
data[g_Keys.bm.port] = getUnsigned(value, "Brandmeister Port", 1024, 65535, 10002);
else if (0 == key.compare(JENABLE))
data[g_Keys.bm.enable] = IS_TRUE(value[0]);
else
badParam(key);
break;
case ESection::dcs:
if (0 == key.compare(JPORT))
data[g_Keys.dcs.port] = getUnsigned(value, "DCS Port", 1024, 65535, 30051);
else
badParam(key);
break;
case ESection::dextra:
if (0 == key.compare(JPORT))
data[g_Keys.dextra.port] = getUnsigned(value, "DExtra Port", 1024, 65535, 30001);
else
badParam(key);
break;
case ESection::g3:
if (0 == key.compare(JENABLE))
data[g_Keys.g3.enable] = IS_TRUE(value[0]);
else
badParam(key);
break;
case ESection::dmrplus:
if (0 == key.compare(JPORT))
data[g_Keys.dmrplus.port] = getUnsigned(value, "DMRPlus Port", 1024, 65535, 8880);
else
badParam(key);
break;
case ESection::dplus:
if (0 == key.compare(JPORT))
data[g_Keys.dplus.port] = getUnsigned(value, "DPlus Port", 1024, 65535, 20001);
else
badParam(key);
break;
case ESection::m17:
if (0 == key.compare(JPORT))
data[g_Keys.m17.port] = getUnsigned(value, "M17 Port", 1024, 65535, 17000);
else
badParam(key);
break;
case ESection::mmdvm:
if (0 == key.compare(JPORT))
data[g_Keys.mmdvm.port] = getUnsigned(value, "MMDVM Port", 1024, 65535, 62030);
else if (0 == key.compare(JDEFAULTID))
data[g_Keys.mmdvm.defaultid] = getUnsigned(value, "MMDVM DefaultID", 0, 9999999, 0);
else
badParam(key);
break;
case ESection::nxdn:
if (0 == key.compare(JPORT))
data[g_Keys.nxdn.port] = getUnsigned(value, "NDXN Port", 1024, 65535, 41400);
else if (0 == key.compare(JAUTOLINKMODULE))
setAutolink(JNXDN, g_Keys.nxdn.autolinkmod, value);
else if (0 == key.compare(JREFLECTORID))
data[g_Keys.nxdn.reflectorid] = getUnsigned(value, "NXDN ReflectorID", 0, 65535, 0);
else
badParam(key);
break;
case ESection::p25:
if (0 == key.compare(JPORT))
data[g_Keys.p25.port] = getUnsigned(value, "P25 Port", 1024, 65535, 41000);
else if (0 == key.compare(JAUTOLINKMODULE))
setAutolink(JP25, g_Keys.p25.autolinkmod, value);
else if (0 == key.compare(JREFLECTORID))
data[g_Keys.p25.reflectorid] = getUnsigned(value, "P25 ReflectorID", 0, 16777215, 0);
else
badParam(key);
break;
case ESection::urf:
if (0 == key.compare(JPORT))
data[g_Keys.urf.port] = getUnsigned(value, "URF Port", 1024, 65535, 10017);
else
badParam(key);
break;
case ESection::usrp:
if (0 == key.compare(JENABLE))
data[g_Keys.usrp.enable] = IS_TRUE(value[0]);
else if (0 == key.compare(JIPADDRESS))
data[g_Keys.usrp.ip] = value;
else if (0 == key.compare(JTXPORT))
data[g_Keys.usrp.txport] = getUnsigned(value, "USRP TxPort", 1024, 65535, 32000);
else if (0 == key.compare(JRXPORT))
data[g_Keys.usrp.rxport] = getUnsigned(value, "USRP RxPort", 1024, 65535, 34000);
else if (0 == key.compare(JMODULE))
data[g_Keys.usrp.module] = value.substr(0, 1);
else if (0 == key.compare(JCALLSIGN))
data[g_Keys.usrp.callsign] = value;
else if (0 == key.compare(JFILEPATH))
data[g_Keys.usrp.filepath] = value;
else
badParam(key);
break;
case ESection::ysf:
if (0 == key.compare(JPORT))
data[g_Keys.ysf.port] = getUnsigned(value, "YSF Port", 1024, 65535, 42000);
else if (0 == key.compare(JAUTOLINKMODULE))
setAutolink(JYSF, g_Keys.ysf.autolinkmod, value);
else if (0 == key.compare(JDEFAULTTXFREQ))
data[g_Keys.ysf.defaulttxfreq] = getUnsigned(value, "YSF DefaultTxFreq", 40000000, 2600000000, 439000000);
else if (0 == key.compare(JDEFAULTRXFREQ))
data[g_Keys.ysf.defaultrxfreq] = getUnsigned(value, "YSF DefaultRxFreq", 40000000, 2600000000, 439000000);
else if (0 == key.compare(JREGISTRATIONID))
data[g_Keys.ysf.ysfreflectordb.id] = getUnsigned(value, "YSF RegistrationID", 0, 9999999, 0);
else if (0 == key.compare(JREGISTRATIONNAME))
{
std::string name(value);
if (name.size() > 13) name.resize(13);
data[g_Keys.ysf.ysfreflectordb.name] = name;
}
else if (0 == key.compare(JREGISTRATIONDESCRIPTION))
{
std::string desc(value);
if (desc.size() > 16) desc.resize(16);
data[g_Keys.ysf.ysfreflectordb.description] = desc;
}
else
badParam(key);
break;
case ESection::dmrid:
case ESection::nxdnid:
case ESection::ysffreq:
switch (section)
{
case ESection::dmrid: pdb = &g_Keys.dmriddb; break;
case ESection::nxdnid: pdb = &g_Keys.nxdniddb; break;
case ESection::ysffreq: pdb = &g_Keys.ysftxrxdb; break;
}
if (0 == key.compare(JURL))
data[pdb->url] = value;
else if (0 == key.compare(JMODE))
{
if ((0==value.compare("file")) || (0==value.compare("http")) || (0==value.compare("both")))
data[pdb->mode] = value;
else
{
std::cout << "WARNING: line #" << counter << ": Mode, '" << value << "' not recognized. Setting to 'http'" << std::endl;
data[pdb->mode] = "http";
}
}
else if (0 == key.compare(JREFRESHMIN))
data[pdb->refreshmin] = getUnsigned(value, JREFRESHMIN, 15, 14400, 180);
else if (0 == key.compare(JFILEPATH))
data[pdb->filepath] = value;
else
badParam(key);
break;
case ESection::files:
if (0 == key.compare(JPIDPATH))
data[g_Keys.files.pid] = value;
else if (0 == key.compare(JXMLPATH))
data[g_Keys.files.xml] = value;
else if (0 == key.compare(JJSONPATH))
data[g_Keys.files.json] = value;
else if (0 == key.compare(JWHITELISTPATH))
data[g_Keys.files.white] = value;
else if (0 == key.compare(JBLACKLISTPATH))
data[g_Keys.files.black] = value;
else if (0 == key.compare(JINTERLINKPATH))
data[g_Keys.files.interlink] = value;
else if (0 == key.compare(JG3TERMINALPATH))
data[g_Keys.files.terminal] = value;
else
badParam(key);
break;
default:
std::cout << "WARNING: parameter '" << line << "' defined befor any [section]" << std::endl;
}
}
cfgfile.close();
////////////////////////////// check the input
// Names section
if (isDefined(ErrorLevel::fatal, JNAMES, JCALLSIGN, g_Keys.names.callsign, rval))
{
const auto cs = data[g_Keys.names.callsign].get<std::string>();
auto RefRegEx = std::regex("^URF([A-Z0-9]){3,3}$", std::regex::extended);
if (! std::regex_match(cs, RefRegEx))
{
std::cerr << "ERROR: [" << JNAMES << ']' << JCALLSIGN << " '" << cs << "' is malformed" << std::endl;
rval = true;
}
}
isDefined(ErrorLevel::mild, JNAMES, JSYSOPEMAIL, g_Keys.names.email, rval);
isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, g_Keys.names.country, rval);
isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, g_Keys.names.sponsor, rval);
// IP Address section
// ipv4 bind and external
if (isDefined(ErrorLevel::fatal, JIPADDRESSES, JIPV4BINDING, g_Keys.ip.ipv4bind, rval))
{
if (std::regex_match(data[g_Keys.ip.ipv4bind].get<std::string>(), IPv4RegEx))
{
if (data.contains(g_Keys.ip.ipv4address))
{
auto v4 = data[g_Keys.ip.ipv4address].get<std::string>();
if (std::regex_match(v4, IPv4RegEx))
{
if (ipv4.compare(v4))
std::cout << "WARNING: specified IPv4 external address, " << v4 << ", is different than detected address, " << ipv4 << std::endl;
}
else
{
std::cerr << "ERROR: specifed IPv4 external address, " << v4 << ", is malformed" << std::endl;
rval = true;
}
}
else
{
// make sure curl worked!
if (std::regex_match(ipv4, IPv4RegEx))
data[g_Keys.ip.ipv4address] = ipv4;
else
{
std::cerr << "ERROR: could not detect IPv4 address at this time" << std::endl;
rval = true;
}
}
}
else
{
std::cerr << "ERROR: IPv4 binding address, " << data[g_Keys.ip.ipv4address].get<std::string>() << ", is malformed" << std::endl;
rval = true;
}
}
// ipv6 bind and external
if (data.contains(g_Keys.ip.ipv6bind))
{
if (std::regex_match(data[g_Keys.ip.ipv6bind].get<std::string>(), IPv6RegEx))
{
if (data.contains(g_Keys.ip.ipv6address))
{
auto v6 = data[g_Keys.ip.ipv6address].get<std::string>();
if (std::regex_match(v6, IPv6RegEx))
{
if (ipv6.compare(v6))
std::cout << "WARNING: specified IPv6 external address [" << v6 << "], is different than detected address [" << ipv6 << ']' << std::endl;
}
else
{
std::cerr << "ERROR: the specifed IPv6 address [" << v6 << "] is malformed" << std::endl;
rval = true;
}
}
else
{
// make sure curl worked!
if (std::regex_match(ipv6, IPv6RegEx))
data[g_Keys.ip.ipv6address] = ipv6;
else
{
std::cerr << "ERROR: could not detect IPv6 address at this time" << std::endl;
rval = true;
}
}
}
else
{
std::cerr << "ERROR: IPv6 binding address, " << data[g_Keys.ip.ipv6address].get<std::string>() << ", is malformed" << std::endl;
rval = true;
}
}
else
{
data[g_Keys.ip.ipv6bind] = nullptr;
data[g_Keys.ip.ipv6address] = nullptr;
}
// Modules section
if (isDefined(ErrorLevel::fatal, JMODULES, JMODULES, g_Keys.modules.modules, rval))
{
const auto mods(data[g_Keys.modules.modules].get<std::string>());
if (data.contains(g_Keys.modules.tcmodules))
{
const auto tcmods(data[g_Keys.modules.tcmodules].get<std::string>());
// how many transcoded modules
auto size = tcmods.size();
if (3 != size && 1 != size)
std::cout << "WARNING: [" << JMODULES << ']' << JTRANSCODED << " doesn't define one (or three) modules" << std::endl;
// make sure each transcoded module is configured
for (auto c : tcmods)
{
if (std::string::npos == mods.find(c))
{
std::cerr << "ERROR: transcoded module '" << c << "' not found in defined modules" << std::endl;
rval = true;
}
}
}
else
data[g_Keys.modules.tcmodules] = nullptr;
// finally, check the module descriptions
for (unsigned i=0; i<26; i++)
{
if (std::string::npos == mods.find('A'+i))
{
if (data.contains(g_Keys.modules.descriptor[i]))
{
std::cout << "WARNING: " << g_Keys.modules.descriptor[i] << " defined for an unconfigured module. Deleting..." << std::endl;
data.erase(g_Keys.modules.descriptor[i]);
}
}
else
{
if (! data.contains(g_Keys.modules.descriptor[i]))
{
std::string value("Module ");
value.append(1, 'A'+i);
std::cout << "WARNING: " << g_Keys.modules.descriptor[i] << " not found. Setting to " << value << std::endl;
data[g_Keys.modules.descriptor[i]] = value;
}
}
}
}
// "simple" protocols with only a Port
isDefined(ErrorLevel::fatal, JDCS, JPORT, g_Keys.dcs.port, rval);
isDefined(ErrorLevel::fatal, JDEXTRA, JPORT, g_Keys.dextra.port, rval);
isDefined(ErrorLevel::fatal, JDMRPLUS, JPORT, g_Keys.dmrplus.port, rval);
isDefined(ErrorLevel::fatal, JDPLUS, JPORT, g_Keys.dplus.port, rval);
isDefined(ErrorLevel::fatal, JM17, JPORT, g_Keys.m17.port, rval);
isDefined(ErrorLevel::fatal, JURF, JPORT, g_Keys.urf.port, rval);
// BM
if (isDefined(ErrorLevel::fatal, JBRANDMEISTER, JENABLE, g_Keys.bm.enable, rval))
{
if (GetBoolean(g_Keys.bm.enable))
{
isDefined(ErrorLevel::fatal, JBRANDMEISTER, JPORT, g_Keys.bm.port, rval);
}
}
// G3
isDefined(ErrorLevel::fatal, JG3, JENABLE, g_Keys.g3.enable, rval);
// MMDVM
isDefined(ErrorLevel::fatal, JMMDVM, JPORT, g_Keys.mmdvm.port, rval);
isDefined(ErrorLevel::fatal, JMMDVM, JDEFAULTID, g_Keys.mmdvm.defaultid, rval);
// NXDN
isDefined(ErrorLevel::fatal, JNXDN, JPORT, g_Keys.nxdn.port, rval);
checkAutoLink(JNXDN, JAUTOLINKMODULE, g_Keys.nxdn.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JNXDN, JREFLECTORID, g_Keys.nxdn.reflectorid, rval);
// P25
isDefined(ErrorLevel::fatal, JP25, JPORT, g_Keys.p25.port, rval);
checkAutoLink(JP25, JAUTOLINKMODULE, g_Keys.p25.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JP25, JREFLECTORID, g_Keys.p25.reflectorid, rval);
// USRP
if (isDefined(ErrorLevel::fatal, JUSRP, JENABLE, g_Keys.usrp.enable, rval))
{
if (GetBoolean(g_Keys.usrp.enable))
{
if (IsString(g_Keys.modules.tcmodules))
{
if (isDefined(ErrorLevel::fatal, JUSRP, JMODULE, g_Keys.usrp.module, rval))
{
if (std::string::npos == GetString(g_Keys.modules.tcmodules).find(GetString(g_Keys.usrp.module).at(0)))
{
std::cerr << "ERROR: [" << JUSRP << ']' << JMODULE << " is not a transcoded module" << std::endl;
rval = true;
}
}
if (isDefined(ErrorLevel::fatal, JUSRP, JIPADDRESS, g_Keys.usrp.ip, rval))
{
// check for syntax
if (! std::regex_match(data[g_Keys.usrp.ip].get<std::string>(), IPv4RegEx))
{
std::cerr << "ERROR: [" << JUSRP << ']' << JIPADDRESS " '" << data[g_Keys.usrp.ip] << "' is malformed" << std::endl;
rval = true;
}
}
isDefined(ErrorLevel::fatal, JUSRP, JTXPORT, g_Keys.usrp.txport, rval);
isDefined(ErrorLevel::fatal, JUSRP, JRXPORT, g_Keys.usrp.rxport, rval);
isDefined(ErrorLevel::fatal, JUSRP, JCALLSIGN, g_Keys.usrp.callsign, rval);
//if (isDefined(ErrorLevel::fatal, JUSRP, JFILEPATH, g_Keys.usrp.filepath, rval))
if (data.contains(g_Keys.usrp.filepath))
checkFile(JUSRP, JFILEPATH, data[g_Keys.usrp.filepath]);
}
else
{
std::cerr << "ERROR: " << JUSRP << " requires a transoder" << std::endl;
rval = true;
}
}
}
// YSF
isDefined(ErrorLevel::fatal, JYSF, JPORT, g_Keys.ysf.port, rval);
checkAutoLink(JYSF, JAUTOLINKMODULE, g_Keys.ysf.autolinkmod, rval);
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTRXFREQ, g_Keys.ysf.defaultrxfreq, rval);
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTTXFREQ, g_Keys.ysf.defaulttxfreq, rval);
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONID, g_Keys.ysf.ysfreflectordb.id, rval);
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONNAME, g_Keys.ysf.ysfreflectordb.name, rval);
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONDESCRIPTION, g_Keys.ysf.ysfreflectordb.description, rval);
// Databases
std::list<std::pair<const std::string, const struct SJsonKeys::DB *>> dbs = {
{ JDMRIDDB, &g_Keys.dmriddb },
{ JNXDNIDDB, &g_Keys.nxdniddb },
{ JYSFTXRXDB, &g_Keys.ysftxrxdb }
};
for ( auto &item : dbs )
{
if (isDefined(ErrorLevel::fatal, item.first, JMODE, item.second->mode, rval))
{
if (ERefreshType::file != GetRefreshType(item.second->mode))
{
isDefined(ErrorLevel::fatal, item.first, JURL, item.second->url, rval);
isDefined(ErrorLevel::fatal, item.first, JREFRESHMIN, item.second->refreshmin, rval);
}
if (ERefreshType::http != GetRefreshType(item.second->mode))
{
if (isDefined(ErrorLevel::fatal, item.first, JFILEPATH, item.second->filepath, rval))
checkFile(item.first, JFILEPATH, data[item.second->filepath]);
}
}
}
// Other files
isDefined(ErrorLevel::fatal, JFILES, JPIDPATH, g_Keys.files.pid, rval);
isDefined(ErrorLevel::fatal, JFILES, JXMLPATH, g_Keys.files.xml, rval);
if (isDefined(ErrorLevel::fatal, JFILES, JWHITELISTPATH, g_Keys.files.white, rval))
checkFile(JFILES, JWHITELISTPATH, data[g_Keys.files.white]);
if (isDefined(ErrorLevel::fatal, JFILES, JBLACKLISTPATH, g_Keys.files.black, rval))
checkFile(JFILES, JBLACKLISTPATH, data[g_Keys.files.black]);
if (isDefined(ErrorLevel::fatal, JFILES, JINTERLINKPATH, g_Keys.files.interlink, rval))
checkFile(JFILES, JINTERLINKPATH, data[g_Keys.files.interlink]);
if (data.contains(g_Keys.g3.enable) && GetBoolean(g_Keys.g3.enable))
{
if (isDefined(ErrorLevel::fatal, JFILES, JG3TERMINALPATH, g_Keys.files.terminal, rval))
checkFile(JFILES, JG3TERMINALPATH, data[g_Keys.files.terminal]);
}
return rval;
}
bool CConfigure::isDefined(ErrorLevel level, const std::string &section, const std::string &pname, const std::string &key, bool &rval)
{
if (data.contains(key))
return true;
if (ErrorLevel::mild == level)
{
std::cout << "WARNING: [" << section << ']' << pname << " is not defined" << std::endl;
data[key] = nullptr;
}
else
{
std::cerr << "ERROR: [" << section << ']' << pname << " is not defined" << std::endl;
rval = true;
}
return false;
}
void CConfigure::checkAutoLink(const std::string &section, const std::string &pname, const std::string &key, bool &rval)
{
if (data.contains(key))
{
auto ismods = data.contains(g_Keys.modules.modules);
const auto mods(ismods ? data[g_Keys.modules.modules].get<std::string>() : "");
const auto c = data[key].get<std::string>().at(0);
if (std::string::npos == mods.find(c))
{
std::cerr << "ERROR: [" << section << ']' << pname << " module '" << c << "' not a configured module" << std::endl;
rval = true;
}
}
else
data[key] = nullptr;
}
std::string CConfigure::getDataRefreshType(ERefreshType type) const
{
if (ERefreshType::both == type)
return std::string("both");
else if (ERefreshType::file == type)
return std::string("file");
else
return std::string("http");
}
unsigned CConfigure::getUnsigned(const std::string &valuestr, const std::string &label, unsigned min, unsigned max, unsigned def) const
{
auto i = unsigned(std::stoul(valuestr.c_str()));
if ( i < min || i > max )
{
std::cout << "WARNING: line #" << counter << ": " << label << " is out of range. Reset to " << def << std::endl;
i = def;
}
return (unsigned)i;
}
void CConfigure::badParam(const std::string &key) const
{
std::cout << "WARNING: line #" << counter << ": Unexpected parameter: '" << key << "'" << std::endl;
}
bool CConfigure::checkModules(std::string &m) const
{
bool rval = false; // return true on error
for(unsigned i=0; i<m.size(); i++)
if (islower(m[i]))
m[i] = toupper(m[i]);
const std::string all("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
std::string found;
for (auto c : all)
if (std::string::npos != m.find(c) && std::string::npos == found.find(c))
found.append(1, c);
m.assign(found);
return m.empty();
}
void CConfigure::setAutolink(const std::string &section, const std::string &key, const std::string &value)
{
auto c = toupper(value.at(0));
if (isupper(c))
data[key] = std::string(1, c);
else
std::cout << "WARNING: line #" << counter << ": " << section << " AutoLinkModule is invalid: '" << value.substr(0, 1) << "'" << std::endl;
}
void CConfigure::checkFile(const std::string &section, const std::string &key, const std::string &filepath) const
{
struct stat sstat;
auto rval = stat(filepath.c_str(), &sstat);
if (rval)
{
std::cout << "WARNING: [" << section << ']' << key << " \"" << filepath << "\": " << strerror(errno) << std::endl;
}
}
void CConfigure::Dump(bool justpublic) const
{
nlohmann::json tmpjson = data;
if (justpublic)
{
for (auto &it : data.items())
{
if (islower(it.key().at(0)))
tmpjson.erase(it.key());
}
}
std::cout << tmpjson.dump(4) << std::endl;
}
ERefreshType CConfigure::GetRefreshType(const std::string &key) const
{
ERefreshType type = ERefreshType::http;
if (data.contains(key))
{
if (data[key].is_string())
{
auto s = data[key].get<std::string>();
if (0 == s.compare("both"))
type = ERefreshType::both;
else if (0 == s.compare("file"))
type = ERefreshType::file;
else
type = ERefreshType::http;
}
}
return type;
}
bool CConfigure::Contains(const std::string &key) const
{
return data.contains(key);
}
std::string CConfigure::GetString(const std::string &key) const
{
std::string str;
if (data.contains(key))
{
if (data[key].is_null())
{
// null is the same thing as an empty string
return str;
}
else if (data[key].is_string())
{
str.assign(data[key].get<std::string>());
}
else
std::cerr << "ERROR: GetString(): '" << key << "' is not a string" << std::endl;
}
else
{
std::cerr << "ERROR: GetString(): item at '" << key << "' is not defined" << std::endl;
}
return str;
}
unsigned CConfigure::GetUnsigned(const std::string &key) const
{
unsigned u = 0;
if (data.contains(key))
{
if (data[key].is_number_unsigned())
{
u = data[key].get<unsigned>();
}
else
std::cerr << "ERROR: GetUnsigned(): '" << key << "' is not an unsigned value" << std::endl;
}
else
{
std::cerr << "ERROR: GetUnsigned(): item at '" << key << "' is not defined" << std::endl;
}
return u;
}
bool CConfigure::GetBoolean(const std::string &key) const
{
if (data[key].is_boolean())
return data[key];
else
return false;
}
char CConfigure::GetAutolinkModule(const std::string &key) const
{
char c = 0;
if (data.contains(key))
{
if (data[key].is_string())
{
c = data[key].get<std::string>().at(0);
}
}
return c;
}
bool CConfigure::IsString(const std::string &key) const
{
if (data.contains(key))
return data[key].is_string();
return false;
}
#ifdef INICHECK
SJsonKeys g_Keys;
int main(int argc, char *argv[])
{
if (3 == argc && strlen(argv[1]) > 1 && '-' == argv[1][0])
{
CConfigure d;
auto rval = d.ReadData(argv[2]);
if ('q' != argv[1][1])
d.Dump(('n' == argv[1][1]) ? true : false);
return rval ? EXIT_FAILURE : EXIT_SUCCESS;
}
std::cerr << "Usage: " << argv[0] << " -(q|n|v) FILENAME\nWhere:\n\t-q just prints warnings and errors.\n\t-n also prints keys that begin with an uppercase letter.\n\t-v prints all keys, warnings and errors." << std::endl;
return EXIT_SUCCESS;
}
#endif

@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 by Thomas A. Early N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#pragma once
#include <cstdint>
#include <string>
#include <regex>
#include <nlohmann/json.hpp>
enum class ErrorLevel { fatal, mild };
enum class ERefreshType { file, http, both };
enum class ESection { none, names, ip, modules, urf, dplus, dextra, dcs, g3, dmrplus, mmdvm, nxdn, bm, ysf, p25, m17, usrp, dmrid, nxdnid, ysffreq, files };
#define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1')
class CConfigure
{
public:
CConfigure();
bool ReadData(const std::string &path);
bool Contains(const std::string &key) const;
void Dump(bool justpublic) const;
std::string GetString(const std::string &key) const;
unsigned GetUnsigned(const std::string &key) const;
bool GetBoolean(const std::string &key) const;
ERefreshType GetRefreshType(const std::string &key) const;
bool IsString(const std::string &key) const;
char GetAutolinkModule(const std::string &key) const;
const nlohmann::json &GetData() { return data; }
private:
// CFGDATA data;
unsigned counter;
nlohmann::json data;
std::regex IPv4RegEx, IPv6RegEx;
std::string getDataRefreshType(ERefreshType t) const;
unsigned getUnsigned(const std::string &value, const std::string &label, unsigned min, unsigned max, unsigned defaultvalue) const;
void badParam(const std::string &param) const;
bool checkModules(std::string &m) const;
void checkFile(const std::string &section, const std::string &key, const std::string &filepath) const;
void setAutolink(const std::string &section, const std::string &key, const std::string &value);
bool isDefined(ErrorLevel level, const std::string &section, const std::string &pname, const std::string &key, bool &rval);
void checkAutoLink(const std::string &section, const std::string &pname, const std::string &key, bool &rval);
};

@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 by Thomas A. Early N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "CurlGet.h"
CCurlGet::CCurlGet()
{
curl_global_init(CURL_GLOBAL_ALL);
}
CCurlGet::~CCurlGet()
{
curl_global_cleanup();
}
// callback function writes data to a std::ostream
size_t CCurlGet::data_write(void* buf, size_t size, size_t nmemb, void* userp)
{
if(userp)
{
std::ostream& os = *static_cast<std::ostream*>(userp);
std::streamsize len = size * nmemb;
if(os.write(static_cast<char*>(buf), len))
return len;
}
return 0;
}
CURLcode CCurlGet::GetURL(const std::string &url, std::stringstream &ss, long timeout)
{
CURLcode code(CURLE_FAILED_INIT);
CURL* curl = curl_easy_init();
if(curl)
{
if(CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &data_write))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &ss))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str())))
{
code = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
}
if (code != CURLE_OK)
{
std::cout << "ERROR: was not able retrieve data at '" << url << "'\nCurl returned: " << code << std::endl;
}
return code;
}

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 by Thomas A. Early N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#pragma once
#include <curl/curl.h>
#include <iostream>
#include <string>
class CCurlGet
{
public:
CCurlGet();
~CCurlGet();
// the contents of the URL will be appended to the stringstream.
CURLcode GetURL(const std::string &url, std::stringstream &ss, long timeout = 30);
private:
static size_t data_write(void* buf, size_t size, size_t nmemb, void* userp);
};

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "DCSClient.h" #include "DCSClient.h"

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CDcsClient : public CClient class CDcsClient : public CClient

@ -16,12 +16,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Global.h"
#include "DCSClient.h" #include "DCSClient.h"
#include "DCSProtocol.h" #include "DCSProtocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// operation // operation
@ -219,11 +218,10 @@ void CDcsProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
void CDcsProtocol::HandleQueue(void) void CDcsProtocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto module = packet->GetPacketModule(); const auto module = packet->GetPacketModule();
@ -270,7 +268,6 @@ void CDcsProtocol::HandleQueue(void)
} }
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"

@ -16,10 +16,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "DExtraClient.h" #include "DExtraClient.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructors // constructors

@ -18,15 +18,9 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
////////////////////////////////////////////////////////////////////////////////////////
// define
////////////////////////////////////////////////////////////////////////////////////////
// class
class CDextraClient : public CClient class CDextraClient : public CClient
{ {
public: public:

@ -1,67 +0,0 @@
// Created by Antony Chazapis (SV9OAN) on 25/2/2018.
// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h>
#include "Reflector.h"
#include "DExtraPeer.h"
#include "DExtraClient.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CDextraPeer::CDextraPeer()
{
}
CDextraPeer::CDextraPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version)
: CPeer(callsign, ip, modules, version)
{
std::cout << "Adding DExtra peer" << std::endl;
// and construct the DExtra clients
for ( unsigned i = 0; i < ::strlen(modules); i++ )
{
// create and append to list
m_Clients.push_back(std::make_shared<CDextraClient>(callsign, ip, modules[i], EProtoRev::ambe));
}
}
////////////////////////////////////////////////////////////////////////////////////////
// status
bool CDextraPeer::IsAlive(void) const
{
for ( auto it=cbegin(); it!=cend(); it++ )
{
if (! (*it)->IsAlive())
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////
// revision helper
int CDextraPeer::GetProtocolRevision(const CVersion &version)
{
return version.GetMajor();
}

@ -1,42 +0,0 @@
// Created by Antony Chazapis (SV9OAN) on 25/2/2018.
// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#pragma once
#include "Peer.h"
#include "DExtraClient.h"
class CDextraPeer : public CPeer
{
public:
// constructors
CDextraPeer();
CDextraPeer(const CCallsign &, const CIp &, const char *, const CVersion &);
CDextraPeer(const CDextraPeer &) = delete;
// status
bool IsAlive(void) const;
// identity
EProtocol GetProtocol(void) const { return EProtocol::dextra; }
const char *GetProtocolName(void) const { return "DExtra"; }
// revision helper
static int GetProtocolRevision(const CVersion &);
};

@ -16,14 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "DExtraPeer.h"
#include "Global.h"
#include "DExtraClient.h" #include "DExtraClient.h"
#include "DExtraProtocol.h" #include "DExtraProtocol.h"
#include "Reflector.h" #
#include "GateKeeper.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// operation // operation
@ -36,7 +35,6 @@ bool CDextraProtocol::Initialize(const char *type, const EProtocol ptype, const
// update time // update time
m_LastKeepaliveTime.start(); m_LastKeepaliveTime.start();
m_LastPeersLinkTime.start();
// done // done
return true; return true;
@ -103,36 +101,13 @@ void CDextraProtocol::Task(void)
// valid module ? // valid module ?
if ( g_Reflector.IsValidModule(ToLinkModule) ) if ( g_Reflector.IsValidModule(ToLinkModule) )
{ {
// is this an ack for a link request? // acknowledge the request
CPeerCallsignList *list = g_GateKeeper.GetPeerList(); EncodeConnectAckPacket(&Buffer, ProtRev);
CCallsignListItem *item = list->FindListItem(Callsign); Send(Buffer, Ip);
if ( item != nullptr && Callsign.GetCSModule() == item->GetModules()[1] && ToLinkModule == item->GetModules()[0] )
{
std::cout << "DExtra ack packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
// already connected ?
CPeers *peers = g_Reflector.GetPeers();
if ( peers->FindPeer(Callsign, Ip, EProtocol::dextra) == nullptr )
{
// create the new peer
// this also create one client per module
// append the peer to reflector peer list
// this also add all new clients to reflector client list
peers->AddPeer(std::make_shared<CDextraPeer>(Callsign, Ip, std::string(1, ToLinkModule).c_str(), CVersion(2, 0, 0)));
}
g_Reflector.ReleasePeers();
}
else
{
// acknowledge the request
EncodeConnectAckPacket(&Buffer, ProtRev);
Send(Buffer, Ip);
// create the client and append // create the client and append
g_Reflector.GetClients()->AddClient(std::make_shared<CDextraClient>(Callsign, Ip, ToLinkModule, ProtRev)); g_Reflector.GetClients()->AddClient(std::make_shared<CDextraClient>(Callsign, Ip, ToLinkModule, ProtRev));
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
}
g_GateKeeper.ReleasePeerList();
} }
else else
{ {
@ -211,16 +186,6 @@ void CDextraProtocol::Task(void)
// update time // update time
m_LastKeepaliveTime.start(); m_LastKeepaliveTime.start();
} }
// peer connections
if ( m_LastPeersLinkTime.time() > DEXTRA_RECONNECT_PERIOD )
{
// handle remote peers connections
HandlePeerLinks();
// update time
m_LastPeersLinkTime.start();
}
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -228,11 +193,10 @@ void CDextraProtocol::Task(void)
void CDextraProtocol::HandleQueue(void) void CDextraProtocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty() )
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// encode it // encode it
CBuffer buffer; CBuffer buffer;
@ -258,7 +222,6 @@ void CDextraProtocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -342,58 +305,6 @@ void CDextraProtocol::HandleKeepalives(void)
g_Reflector.ReleasePeers(); g_Reflector.ReleasePeers();
} }
////////////////////////////////////////////////////////////////////////////////////////
// Peers helpers
void CDextraProtocol::HandlePeerLinks(void)
{
CBuffer buffer;
// get the list of peers
CPeerCallsignList *list = g_GateKeeper.GetPeerList();
CPeers *peers = g_Reflector.GetPeers();
// check if all our connected peers are still listed by gatekeeper
// if not, disconnect
auto pit = peers->begin();
std::shared_ptr<CPeer>peer = nullptr;
while ( (peer = peers->FindNextPeer(EProtocol::dextra, pit)) != nullptr )
{
if ( list->FindListItem(peer->GetCallsign()) == nullptr )
{
// send disconnect packet
EncodeDisconnectPacket(&buffer, peer->GetReflectorModules()[0]);
Send(buffer, peer->GetIp());
std::cout << "Sending disconnect packet to XRF peer " << peer->GetCallsign() << " at " << peer->GetIp() << std::endl;
// remove client
peers->RemovePeer(peer);
}
}
// check if all ours peers listed by gatekeeper are connected
// if not, connect or reconnect
for ( auto it=list->begin(); it!=list->end(); it++ )
{
if ( !it->GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) )
continue;
if ( strlen(it->GetModules()) != 2 )
continue;
if ( peers->FindPeer(it->GetCallsign(), EProtocol::dextra) == nullptr )
{
// resolve again peer's IP in case it's a dynamic IP
it->ResolveIp();
// send connect packet to re-initiate peer link
EncodeConnectPacket(&buffer, it->GetModules());
Send(buffer, it->GetIp(), DEXTRA_PORT);
std::cout << "Sending connect packet to XRF peer " << it->GetCallsign() << " @ " << it->GetIp() << " for module " << it->GetModules()[1] << " (module " << it->GetModules()[0] << ")" << std::endl;
}
}
// done
g_Reflector.ReleasePeers();
g_GateKeeper.ReleasePeerList();
}
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// streams helpers // streams helpers

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "SEProtocol.h" #include "SEProtocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"
@ -61,7 +62,6 @@ protected:
void HandleQueue(void); void HandleQueue(void);
// keepalive helpers // keepalive helpers
void HandlePeerLinks(void);
void HandleKeepalives(void); void HandleKeepalives(void);
// stream helpers // stream helpers
@ -84,8 +84,6 @@ protected:
bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer &) const; bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer &) const;
bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer &) const; bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer &) const;
protected:
// time // time
CTimer m_LastKeepaliveTime; CTimer m_LastKeepaliveTime;
CTimer m_LastPeersLinkTime;
}; };

@ -1,145 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include "Main.h"
#include "Reflector.h"
#include "DMRIdDir.h"
#include "DMRIdDirFile.h"
#include "DMRIdDirHttp.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor
CDmridDir::CDmridDir()
{
keep_running = true;
}
CDmridDir::~CDmridDir()
{
Close();
}
////////////////////////////////////////////////////////////////////////////////////////
// init & close
bool CDmridDir::Init(void)
{
// load content
Reload();
// reset run flag
keep_running = true;
// start thread;
m_Future = std::async(std::launch::async, &CDmridDir::Thread, this);
return true;
}
void CDmridDir::Close(void)
{
keep_running = false;
if ( m_Future.valid() )
{
m_Future.get();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// thread
void CDmridDir::Thread()
{
while (keep_running)
{
// Wait DMRIDDB_REFRESH_RATE minutes
for (int i=0; i<30*DMRIDDB_REFRESH_RATE && keep_running; i++)
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// have lists files changed ?
if ( NeedReload() )
{
Reload();
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
// Reload
bool CDmridDir::Reload(void)
{
CBuffer buffer;
bool ok = false;
if ( LoadContent(&buffer) )
{
Lock();
{
ok = RefreshContent(buffer);
}
Unlock();
}
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// find
const CCallsign *CDmridDir::FindCallsign(uint32_t dmrid)
{
auto found = m_CallsignMap.find(dmrid);
if ( found != m_CallsignMap.end() )
{
return &(found->second);
}
return nullptr;
}
uint32_t CDmridDir::FindDmrid(const CCallsign &callsign)
{
auto found = m_DmridMap.find(callsign);
if ( found != m_DmridMap.end() )
{
return (found->second);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////
// syntax helpers
bool CDmridDir::IsValidDmrid(const char *sz)
{
bool ok = false;
size_t n = ::strlen(sz);
if ( (n > 0) && (n <= 8) )
{
ok = true;
for ( size_t i = 0; (i < n) && ok; i++ )
{
ok &= ::isdigit(sz[i]);
}
}
return ok;
}

@ -1,159 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "Main.h"
#include "DMRIdDirFile.h"
#if (DMRIDDB_USE_RLX_SERVER == 0)
CDmridDirFile g_DmridDir;
#endif
////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor
CDmridDirFile::CDmridDirFile()
{
memset(&m_LastModTime, 0, sizeof(time_t));
}
////////////////////////////////////////////////////////////////////////////////////////
// init & close
bool CDmridDirFile::Init(void)
{
return CDmridDir::Init();
}
////////////////////////////////////////////////////////////////////////////////////////
// refresh
bool CDmridDirFile::NeedReload(void)
{
bool needReload = false;
time_t time;
if ( GetLastModTime(&time) )
{
needReload = time != m_LastModTime;
}
return needReload;
}
bool CDmridDirFile::LoadContent(CBuffer *buffer)
{
bool ok = false;
std::ifstream file;
std::streampos size;
// open file
file.open(DMRIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
if ( file.is_open() )
{
// read file
size = file.tellg();
if ( size > 0 )
{
// read file into buffer
buffer->resize((int)size+1);
file.seekg (0, std::ios::beg);
file.read((char *)buffer->data(), (int)size);
// close file
file.close();
// update time
GetLastModTime(&m_LastModTime);
// done
ok = true;
}
}
// done
return ok;
}
bool CDmridDirFile::RefreshContent(const CBuffer &buffer)
{
bool ok = false;
// clear directory
m_CallsignMap.clear();
m_DmridMap.clear();
// scan buffer
if ( buffer.size() > 0 )
{
// crack it
char *ptr1 = (char *)buffer.data();
char *ptr2;
// get next line
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
{
*ptr2 = 0;
// get items
char *dmrid;
char *callsign;
if ( ((dmrid = ::strtok(ptr1, ";")) != nullptr) && IsValidDmrid(dmrid) )
{
if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) )
{
// new entry
uint32_t ui = atoi(dmrid);
CCallsign cs(callsign, ui);
if ( cs.IsValid() )
{
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(ui, cs));
m_DmridMap.insert(std::pair<CCallsign,uint32_t>(cs,ui));
}
}
}
// next line
ptr1 = ptr2+1;
}
// done
ok = true;
}
// report
std::cout << "Read " << m_DmridMap.size() << " DMR ids from file " << DMRIDDB_PATH << std::endl;
// done
return ok;
}
bool CDmridDirFile::GetLastModTime(time_t *time)
{
bool ok = false;
struct stat fileStat;
if( ::stat(DMRIDDB_PATH, &fileStat) != -1 )
{
*time = fileStat.st_mtime;
ok = true;
}
return ok;
}

@ -1,47 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#pragma once
#include "DMRIdDir.h"
class CDmridDirFile : public CDmridDir
{
public:
// constructor
CDmridDirFile();
// destructor
~CDmridDirFile() {}
// init & close
bool Init(void);
// refresh
bool LoadContent(CBuffer *);
bool RefreshContent(const CBuffer &);
protected:
// reload helpers
bool NeedReload(void);
bool GetLastModTime(time_t *);
protected:
// data
time_t m_LastModTime;
};

@ -1,177 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include "Main.h"
#include "Reflector.h"
#include "DMRIdDirHttp.h"
#if (DMRIDDB_USE_RLX_SERVER == 1)
CDmridDirHttp g_DmridDir;
#endif
////////////////////////////////////////////////////////////////////////////////////////
// refresh
bool CDmridDirHttp::LoadContent(CBuffer *buffer)
{
// get file from xlxapi server
return HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, buffer);
}
bool CDmridDirHttp::RefreshContent(const CBuffer &buffer)
{
bool ok = false;
// clear directory
m_CallsignMap.clear();
m_DmridMap.clear();
// scan file
if ( buffer.size() > 0 )
{
char *ptr1 = (char *)buffer.data();
char *ptr2;
// get next line
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
{
*ptr2 = 0;
// get items
char *dmrid;
char *callsign;
if ( ((dmrid = ::strtok(ptr1, ";")) != nullptr) && IsValidDmrid(dmrid) )
{
if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) )
{
// new entry
uint32_t ui = atoi(dmrid);
CCallsign cs(callsign, ui);
if ( cs.IsValid() )
{
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(ui, cs));
m_DmridMap.insert(std::pair<CCallsign,uint32_t>(cs,ui));
}
}
}
// next line
ptr1 = ptr2+1;
}
// done
ok = true;
}
// report
std::cout << "Read " << m_DmridMap.size() << " DMR ids from xlxapi.rlx.lu database " << std::endl;
// done
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// httpd helpers
#define DMRID_HTTPGET_SIZEMAX (256)
bool CDmridDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
{
bool ok = false;
int sock_id;
// open socket
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
{
// get hostname address
struct sockaddr_in servaddr;
struct hostent *hp;
memset(&servaddr,0,sizeof(servaddr));
if( (hp = gethostbyname(hostname)) != nullptr )
{
// dns resolved
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
servaddr.sin_port = htons(port);
servaddr.sin_family = AF_INET;
// connect
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
{
// send the GET request
char request[DMRID_HTTPGET_SIZEMAX];
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n",
filename, (const char *)g_Reflector.GetCallsign());
::write(sock_id, request, strlen(request));
// config receive timeouts
fd_set read_set;
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
FD_ZERO(&read_set);
FD_SET(sock_id, &read_set);
// get the reply back
buffer->clear();
bool done = false;
do
{
char buf[1440];
ssize_t len = 0;
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
//if ( ret >= 0 )
//{
usleep(5000);
len = read(sock_id, buf, 1440);
if ( len > 0 )
{
buffer->Append((uint8_t *)buf, (int)len);
ok = true;
}
//}
done = (len <= 0);
}
while (!done);
buffer->Append((uint8_t)0);
// and disconnect
close(sock_id);
}
else
{
std::cout << "Cannot establish connection with host " << hostname << std::endl;
}
}
else
{
std::cout << "Host " << hostname << " not found" << std::endl;
}
}
else
{
std::cout << "Failed to open wget socket" << std::endl;
}
// done
return ok;
}

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "DMRMMDVMClient.h" #include "DMRMMDVMClient.h"

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CDmrmmdvmClient : public CClient class CDmrmmdvmClient : public CClient

@ -16,18 +16,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Global.h"
#include "DMRMMDVMClient.h" #include "DMRMMDVMClient.h"
#include "DMRMMDVMProtocol.h" #include "DMRMMDVMProtocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include "BPTC19696.h" #include "BPTC19696.h"
#include "RS129.h" #include "RS129.h"
#include "Golay2087.h" #include "Golay2087.h"
#include "QR1676.h" #include "QR1676.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// define // define
@ -49,6 +48,7 @@ static uint8_t g_DmrSyncMSData[] = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 };
bool CDmrmmdvmProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6) bool CDmrmmdvmProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{ {
m_DefaultId = g_Configure.GetUnsigned(g_Keys.mmdvm.defaultid);
// base class // base class
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false; return false;
@ -346,12 +346,10 @@ void CDmrmmdvmProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Hea
void CDmrmmdvmProtocol::HandleQueue(void) void CDmrmmdvmProtocol::HandleQueue(void)
{ {
while (! m_Queue.IsEmpty())
m_Queue.Lock();
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto mod = packet->GetPacketModule(); const auto mod = packet->GetPacketModule();
@ -419,7 +417,6 @@ void CDmrmmdvmProtocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -860,14 +857,14 @@ void CDmrmmdvmProtocol::EncodeMMDVMPacket(const CDvHeaderPacket &Header, const C
uint8_t tag[] = { 'D','M','R','D' }; uint8_t tag[] = { 'D','M','R','D' };
Buffer->Set(tag, sizeof(tag)); Buffer->Set(tag, sizeof(tag));
uint8_t cs[12]; uint8_t cs[12];
// DMR header // DMR header
// uiSeqId // uiSeqId
Buffer->Append((uint8_t)seqid); Buffer->Append((uint8_t)seqid);
// uiSrcId // uiSrcId
uint32_t uiSrcId = Header.GetMyCallsign().GetDmrid(); uint32_t uiSrcId = Header.GetMyCallsign().GetDmrid();
DvFrame0.GetMyCallsign().GetCallsign(cs); DvFrame0.GetMyCallsign().GetCallsign(cs);
if(uiSrcId == 0){ if(uiSrcId == 0){
uiSrcId = DvFrame0.GetMyCallsign().GetDmrid(); uiSrcId = DvFrame0.GetMyCallsign().GetDmrid();
} }
@ -878,9 +875,9 @@ void CDmrmmdvmProtocol::EncodeMMDVMPacket(const CDvHeaderPacket &Header, const C
uiSrcId = DvFrame2.GetMyCallsign().GetDmrid(); uiSrcId = DvFrame2.GetMyCallsign().GetDmrid();
} }
if(uiSrcId == 0){ if(uiSrcId == 0){
uiSrcId = DMRMMDVM_DEFAULTID; uiSrcId = m_DefaultId;
} }
AppendDmrIdToBuffer(Buffer, uiSrcId); AppendDmrIdToBuffer(Buffer, uiSrcId);
// uiDstId = TG9 // uiDstId = TG9
uint32_t uiDstId = 9; // ModuleToDmrDestId(Header.GetRpt2Module()); uint32_t uiDstId = 9; // ModuleToDmrDestId(Header.GetRpt2Module());
@ -982,7 +979,7 @@ char CDmrmmdvmProtocol::DmrDstIdToModule(uint32_t tg) const
if (tg > 4000 && tg < 4027) if (tg > 4000 && tg < 4027)
{ {
const char mod = 'A' + (tg - 4001U); const char mod = 'A' + (tg - 4001U);
if (strchr(ACTIVE_MODULES, mod)) if (g_Reflector.IsValidModule(mod))
{ {
return mod; return mod;
} }

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"
@ -117,4 +118,7 @@ protected:
// for authentication // for authentication
uint32_t m_uiAuthSeed; uint32_t m_uiAuthSeed;
// config data
unsigned m_DefaultId;
}; };

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "DMRPlusClient.h" #include "DMRPlusClient.h"

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CDmrplusClient : public CClient class CDmrplusClient : public CClient

@ -16,13 +16,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Global.h"
#include "DMRPlusClient.h" #include "DMRPlusClient.h"
#include "DMRPlusProtocol.h" #include "DMRPlusProtocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include "DMRIdDir.h"
#include "BPTC19696.h" #include "BPTC19696.h"
#include "RS129.h" #include "RS129.h"
#include "Golay2087.h" #include "Golay2087.h"
@ -219,12 +218,10 @@ void CDmrplusProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Head
void CDmrplusProtocol::HandleQueue(void) void CDmrplusProtocol::HandleQueue(void)
{ {
while (! m_Queue.IsEmpty())
m_Queue.Lock();
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto mod = packet->GetPacketModule(); const auto mod = packet->GetPacketModule();
@ -286,7 +283,6 @@ void CDmrplusProtocol::HandleQueue(void)
//buffer.DebugDump(g_Reflector.m_DebugFile); //buffer.DebugDump(g_Reflector.m_DebugFile);
} }
} }
m_Queue.Unlock();
} }
void CDmrplusProtocol::SendBufferToClients(const CBuffer &buffer, uint8_t module) void CDmrplusProtocol::SendBufferToClients(const CBuffer &buffer, uint8_t module)
@ -642,7 +638,7 @@ char CDmrplusProtocol::DmrDstIdToModule(uint32_t tg) const
// is it a 4xxx ? // is it a 4xxx ?
if (tg > 4000 && tg < 4027) { if (tg > 4000 && tg < 4027) {
char mod = 'A' + (tg - 4001U); char mod = 'A' + (tg - 4001U);
if (strchr(ACTIVE_MODULES, mod)) if (g_Reflector.IsValidModule(mod))
{ {
return mod; return mod;
} }

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "DPlusClient.h" #include "DPlusClient.h"

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CDplusClient : public CClient class CDplusClient : public CClient

@ -16,13 +16,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "Global.h"
#include "DPlusClient.h" #include "DPlusClient.h"
#include "DPlusProtocol.h" #include "DPlusProtocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// operation // operation
@ -229,11 +228,10 @@ void CDplusProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header
void CDplusProtocol::HandleQueue(void) void CDplusProtocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto mod = packet->GetPacketModule(); const auto mod = packet->GetPacketModule();
@ -293,7 +291,6 @@ void CDplusProtocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
void CDplusProtocol::SendDvHeader(CDvHeaderPacket *packet, CDplusClient *client) void CDplusProtocol::SendDvHeader(CDvHeaderPacket *packet, CDplusClient *client)

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "SEProtocol.h" #include "SEProtocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h" #include <iostream>
#include <string.h> #include <string.h>
#include "DVFramePacket.h" #include "DVFramePacket.h"
@ -167,10 +167,15 @@ CDvFramePacket::CDvFramePacket(const int16_t *usrp, uint16_t streamid, bool isla
m_TCPack.codec_in = ECodecType::usrp; m_TCPack.codec_in = ECodecType::usrp;
} }
std::unique_ptr<CPacket> CDvFramePacket::Copy(void)
{
return std::unique_ptr<CPacket>(new CDvFramePacket(*this));
}
// Network // Network
unsigned int CDvFramePacket::GetNetworkSize() unsigned int CDvFramePacket::GetNetworkSize()
{ {
return CPacket::GetNetworkSize() + 4 + 3 + 7 + 14 + 9 + 9 + 16; return CPacket::GetNetworkSize() + 4 + 3 + 7 + 14 + 9 + 9 + 16 + 11 + 320;
} }
CDvFramePacket::CDvFramePacket(const CBuffer &buf) : CPacket(buf) CDvFramePacket::CDvFramePacket(const CBuffer &buf) : CPacket(buf)
@ -189,6 +194,8 @@ CDvFramePacket::CDvFramePacket(const CBuffer &buf) : CPacket(buf)
memcpy(m_TCPack.dstar, data+off, 9); off += 9; memcpy(m_TCPack.dstar, data+off, 9); off += 9;
memcpy(m_TCPack.dmr, data+off, 9); off += 9; memcpy(m_TCPack.dmr, data+off, 9); off += 9;
memcpy(m_TCPack.m17, data+off, 16); off += 16; memcpy(m_TCPack.m17, data+off, 16); off += 16;
memcpy(m_TCPack.p25, data+off, 11); off += 11;
memcpy(m_TCPack.usrp, data+off, 320);
SetTCParams(seq); SetTCParams(seq);
} }
else else
@ -210,15 +217,9 @@ void CDvFramePacket::EncodeInterlinkPacket(CBuffer &buf) const
memcpy(data+off, m_Nonce, 14); off += 14; memcpy(data+off, m_Nonce, 14); off += 14;
memcpy(data+off, m_TCPack.dstar, 9); off += 9; memcpy(data+off, m_TCPack.dstar, 9); off += 9;
memcpy(data+off, m_TCPack.dmr, 9); off += 9; memcpy(data+off, m_TCPack.dmr, 9); off += 9;
memcpy(data+off, m_TCPack.m17, 16); memcpy(data+off, m_TCPack.m17, 16); off += 16;
} memcpy(data+off, m_TCPack.p25, 11); off += 11;
memcpy(data+off, m_TCPack.usrp, 320);
////////////////////////////////////////////////////////////////////////////////////////
// virtual duplication
std::unique_ptr<CPacket> CDvFramePacket::Duplicate(void) const
{
return std::unique_ptr<CPacket>(new CDvFramePacket(*this));
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -265,15 +266,3 @@ void CDvFramePacket::SetTCParams(uint32_t seq)
m_TCPack.module = m_cModule; m_TCPack.module = m_cModule;
m_TCPack.rt_timer.start(); m_TCPack.rt_timer.start();
} }
////////////////////////////////////////////////////////////////////////////////////////
// operators
bool CDvFramePacket::operator ==(const CDvFramePacket &DvFrame) const
{
return ( (memcmp(m_TCPack.dstar, DvFrame.m_TCPack.dstar, 9) == 0)
&& (memcmp(m_uiDvData, DvFrame.m_uiDvData, 3) == 0)
&& (memcmp(m_TCPack.dmr, DvFrame.m_TCPack.dmr, 9) == 0)
&& (memcmp(m_uiDvSync, DvFrame.m_uiDvSync, 7) == 0)
);
}

@ -62,10 +62,8 @@ public:
static unsigned int GetNetworkSize(); static unsigned int GetNetworkSize();
void EncodeInterlinkPacket(CBuffer &buf) const; void EncodeInterlinkPacket(CBuffer &buf) const;
// virtual duplication
std::unique_ptr<CPacket> Duplicate(void) const;
// identity // identity
std::unique_ptr<CPacket> Copy(void);
bool IsDvHeader(void) const { return false; } bool IsDvHeader(void) const { return false; }
bool IsDvFrame(void) const { return true; } bool IsDvFrame(void) const { return true; }
@ -82,9 +80,6 @@ public:
void SetCodecData(const STCPacket *pack); void SetCodecData(const STCPacket *pack);
void SetTCParams(uint32_t seq); void SetTCParams(uint32_t seq);
// operators
bool operator ==(const CDvFramePacket &) const;
protected: protected:
// data (dstar) // data (dstar)
uint8_t m_uiDvData[3]; uint8_t m_uiDvData[3];

@ -16,12 +16,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include <cstdio> #include <iostream>
#include "DMRIdDir.h"
#include "DVHeaderPacket.h"
#include "Defines.h"
#include "DVHeaderPacket.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor // constructor
@ -90,7 +90,7 @@ CDvHeaderPacket::CDvHeaderPacket(const struct dstar_header *buffer, uint16_t sid
m_uiFlag2 = buffer->Flag2; m_uiFlag2 = buffer->Flag2;
m_uiFlag3 = buffer->Flag3; m_uiFlag3 = buffer->Flag3;
m_csUR.SetCallsign(buffer->UR, CALLSIGN_LEN); m_csUR.SetCallsign(buffer->UR, CALLSIGN_LEN);
if((buffer->RPT1)[7] == 0x20){ if((buffer->RPT1)[7] == 0x20){
char rptr1[8]; char rptr1[8];
memcpy(rptr1, buffer->RPT1, 8); memcpy(rptr1, buffer->RPT1, 8);
@ -100,7 +100,7 @@ CDvHeaderPacket::CDvHeaderPacket(const struct dstar_header *buffer, uint16_t sid
else{ else{
m_csRPT1.SetCallsign(buffer->RPT1, CALLSIGN_LEN); m_csRPT1.SetCallsign(buffer->RPT1, CALLSIGN_LEN);
} }
m_csRPT1.SetCallsign(buffer->RPT1, CALLSIGN_LEN); m_csRPT1.SetCallsign(buffer->RPT1, CALLSIGN_LEN);
m_csRPT2.SetCallsign(buffer->RPT2, CALLSIGN_LEN); m_csRPT2.SetCallsign(buffer->RPT2, CALLSIGN_LEN);
m_csMY.SetCallsign(buffer->MY, CALLSIGN_LEN); m_csMY.SetCallsign(buffer->MY, CALLSIGN_LEN);
@ -165,10 +165,7 @@ CDvHeaderPacket::CDvHeaderPacket(const CM17Packet &m17) : CPacket(m17)
m_csRPT1.SetCSModule('G'); m_csRPT1.SetCSModule('G');
} }
//////////////////////////////////////////////////////////////////////////////////////// std::unique_ptr<CPacket> CDvHeaderPacket::Copy(void)
// virtual duplication
std::unique_ptr<CPacket> CDvHeaderPacket::Duplicate(void) const
{ {
return std::unique_ptr<CPacket>(new CDvHeaderPacket(*this)); return std::unique_ptr<CPacket>(new CDvHeaderPacket(*this));
} }
@ -204,34 +201,3 @@ bool CDvHeaderPacket::IsValid(void) const
return valid; return valid;
} }
////////////////////////////////////////////////////////////////////////////////////////
// operators
bool CDvHeaderPacket::operator ==(const CDvHeaderPacket &Header) const
{
return ( (m_uiFlag1 == Header.m_uiFlag1) &&
(m_uiFlag2 == Header.m_uiFlag2) &&
(m_uiFlag3 == Header.m_uiFlag3) &&
(m_csUR == Header.m_csUR) &&
(m_csRPT1 == Header.m_csRPT1) &&
(m_csRPT2 == Header.m_csRPT2) &&
(m_csMY == Header.m_csMY) );
}
#ifdef IMPLEMENT_CDVHEADERPACKET_CONST_CHAR_OPERATOR
CDvHeaderPacket::operator const char *() const
{
char *sz = (char *)(const char *)m_sz;
std::sprintf(sz, "%02X %02X %02X\n%s\n%s\n%s\n%s",
m_uiFlag1, m_uiFlag2, m_uiFlag3,
(const char *)m_csUR,
(const char *)m_csRPT1,
(const char *)m_csRPT2,
(const char *)m_csMY);
return m_sz;
}
#endif

@ -20,7 +20,7 @@
#include "Callsign.h" #include "Callsign.h"
#include "Packet.h" #include "Packet.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// implementation details // implementation details
@ -65,10 +65,8 @@ public:
static unsigned int GetNetworkSize(); static unsigned int GetNetworkSize();
void EncodeInterlinkPacket(CBuffer &buf) const; void EncodeInterlinkPacket(CBuffer &buf) const;
// virtual duplication
std::unique_ptr<CPacket> Duplicate(void) const;
// identity // identity
std::unique_ptr<CPacket> Copy(void);
bool IsDvHeader(void) const { return true; } bool IsDvHeader(void) const { return true; }
bool IsDvFrame(void) const { return false; } bool IsDvFrame(void) const { return false; }
@ -94,9 +92,6 @@ public:
void SetRpt2Callsign(const CCallsign &cs) { m_csRPT2 = cs; } void SetRpt2Callsign(const CCallsign &cs) { m_csRPT2 = cs; }
void SetRpt2Module(char c) { m_csRPT2.SetCSModule(c); } void SetRpt2Module(char c) { m_csRPT2.SetCSModule(c); }
// operators
bool operator ==(const CDvHeaderPacket &) const;
protected: protected:
// data // data
uint8_t m_uiFlag1; uint8_t m_uiFlag1;

@ -0,0 +1,143 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#pragma once
#include <atomic>
#include <vector>
#include <array>
#include <list>
#include <map>
#include <unordered_map>
#include <queue>
#include <chrono>
#include <future>
#include <mutex>
#include <memory>
#include <atomic>
#include <condition_variable>
#include <ctime>
#include <cctype>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <arpa/inet.h>
////////////////////////////////////////////////////////////////////////////////////////
// defines
//// Module configuration
#define DSTAR_IPV4 true
#define DMR_IPV4 true
#define YSF_IPV4 true
#define XLX_IPV4 true
#define M17_IPV4 true
#define P25_IPV4 true
#define NXDN_IPV4 true
#define USRP_IPV4 true
#define URF_IPV4 true
#define DSTAR_IPV6 true // QnetGateway can use IPv6
#define DMR_IPV6 false
#define YSF_IPV6 false
#define XLX_IPV6 false
#define M17_IPV6 true
#define P25_IPV6 false
#define NXDN_IPV6 false
#define USRP_IPV6 false
#define URF_IPV6 true
// protocols ---------------------------------------------------
enum class EProtocol { any, none, dextra, dplus, dcs, g3, bm, urf, dmrplus, dmrmmdvm, nxdn, p25, usrp, ysf, m17 };
// DExtra
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds
// DPlus
#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds
#define DPLUS_KEEPALIVE_TIMEOUT (DPLUS_KEEPALIVE_PERIOD*10) // in seconds
#define DPLUS_DEFAULT_RPTR1_SUFFIX 'Y'
// DCS
#define DCS_KEEPALIVE_PERIOD 1 // in seconds
#define DCS_KEEPALIVE_TIMEOUT (DCS_KEEPALIVE_PERIOD*30) // in seconds
// XLX, used for BM
#define BM_KEEPALIVE_PERIOD 1 // in seconds
#define BM_KEEPALIVE_TIMEOUT (BM_KEEPALIVE_PERIOD*30) // in seconds
#define BM_RECONNECT_PERIOD 5 // in seconds
// URF
#define URF_KEEPALIVE_PERIOD 1 // in seconds
#define URF_KEEPALIVE_TIMEOUT (URF_KEEPALIVE_PERIOD*30) // in seconds
#define URF_RECONNECT_PERIOD 5 // in seconds
// DMRPlus (dongle)
#define DMRPLUS_KEEPALIVE_PERIOD 1 // in seconds
#define DMRPLUS_KEEPALIVE_TIMEOUT (DMRPLUS_KEEPALIVE_PERIOD*10) // in seconds
#define DMRPLUS_REFLECTOR_SLOT DMR_SLOT2
#define DMRPLUS_REFLECTOR_COLOUR 1
// DMRMmdvm
#define DMRMMDVM_KEEPALIVE_PERIOD 10 // in seconds
#define DMRMMDVM_KEEPALIVE_TIMEOUT (DMRMMDVM_KEEPALIVE_PERIOD*10) // in seconds
#define DMRMMDVM_REFLECTOR_SLOT DMR_SLOT2
#define DMRMMDVM_REFLECTOR_COLOUR 1
// YSF
#define YSF_KEEPALIVE_PERIOD 3 // in seconds
#define YSF_KEEPALIVE_TIMEOUT (YSF_KEEPALIVE_PERIOD*10) // in seconds
// M17
#define M17_KEEPALIVE_PERIOD 3
#define M17_KEEPALIVE_TIMEOUT (M17_KEEPALIVE_PERIOD*10)
// P25
#define P25_KEEPALIVE_PERIOD 3 // in seconds
#define P25_KEEPALIVE_TIMEOUT (P25_KEEPALIVE_PERIOD*10) // in seconds
// NXDN
#define NXDN_KEEPALIVE_PERIOD 3 // in seconds
#define NXDN_KEEPALIVE_TIMEOUT (NXDN_KEEPALIVE_PERIOD*10) // in seconds
// USRP
#define USRP_KEEPALIVE_PERIOD 1 // in seconds
#define USRP_KEEPALIVE_TIMEOUT (USRP_KEEPALIVE_PERIOD*10) // in seconds
// G3 Terminal
#define G3_PRESENCE_PORT 12346 // UDP port
#define G3_CONFIG_PORT 12345 // UDP port
#define G3_DV_PORT 40000 // UDP port
#define G3_KEEPALIVE_PERIOD 10 // in seconds
#define G3_KEEPALIVE_TIMEOUT 3600 // in seconds, 1 hour
////////////////////////////////////////////////////////////////////////////////////////
// macros
#define MIN(a,b) ((a)<(b))?(a):(b)
#define MAKEWORD(low, high) ((uint16_t)(((uint8_t)(low)) | (((uint16_t)((uint8_t)(high))) << 8)))
#define MAKEDWORD(low, high) ((uint32_t)(((uint16_t)(low)) | (((uint32_t)((uint16_t)(high))) << 16)))
#define LOBYTE(w) ((uint8_t)(uint16_t)(w & 0x00FF))
#define HIBYTE(w) ((uint8_t)((((uint16_t)(w)) >> 8) & 0xFF))
#define LOWORD(dw) ((uint16_t)(uint32_t)(dw & 0x0000FFFF))
#define HIWORD(dw) ((uint16_t)((((uint32_t)(dw)) >> 16) & 0xFFFF))

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "G3Client.h" #include "G3Client.h"

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CG3Client : public CClient class CG3Client : public CClient

@ -16,16 +16,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h" #include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "Global.h"
#include "G3Client.h" #include "G3Client.h"
#include "G3Protocol.h" #include "G3Protocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -34,6 +32,10 @@
bool CG3Protocol::Initialize(const char */*type*/, const EProtocol /*type*/, const uint16_t /*port*/, const bool /*has_ipv4*/, const bool /*has_ipv6*/) bool CG3Protocol::Initialize(const char */*type*/, const EProtocol /*type*/, const uint16_t /*port*/, const bool /*has_ipv4*/, const bool /*has_ipv6*/)
// everything is hard coded until ICOM gets their act together and start supporting IPv6 // everything is hard coded until ICOM gets their act together and start supporting IPv6
{ {
//config data
m_TerminalPath.assign(g_Configure.GetString(g_Keys.files.terminal));
const std::string ipv4address(g_Configure.GetString(g_Keys.ip.ipv4bind));
ReadOptions(); ReadOptions();
// init reflector apparent callsign // init reflector apparent callsign
@ -46,7 +48,7 @@ bool CG3Protocol::Initialize(const char */*type*/, const EProtocol /*type*/, con
//m_ReflectorCallsign.PatchCallsign(0, "XLX", 3); //m_ReflectorCallsign.PatchCallsign(0, "XLX", 3);
// create our sockets // create our sockets
CIp ip(AF_INET, G3_DV_PORT, LISTEN_IPV4); CIp ip(AF_INET, G3_DV_PORT, ipv4address.c_str());
if ( ip.IsSet() ) if ( ip.IsSet() )
{ {
if (! m_Socket4.Open(ip)) if (! m_Socket4.Open(ip))
@ -440,14 +442,13 @@ void CG3Protocol::Task(void)
void CG3Protocol::HandleQueue(void) void CG3Protocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// supress host checks // supress host checks
m_LastKeepaliveTime.start(); m_LastKeepaliveTime.start();
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// encode it // encode it
CBuffer buffer; CBuffer buffer;
@ -473,7 +474,6 @@ void CG3Protocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -672,7 +672,7 @@ void CG3Protocol::NeedReload(void)
{ {
struct stat fileStat; struct stat fileStat;
if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) if (::stat(m_TerminalPath.c_str(), &fileStat) != -1)
{ {
if (m_LastModTime != fileStat.st_mtime) if (m_LastModTime != fileStat.st_mtime)
{ {
@ -701,7 +701,7 @@ void CG3Protocol::ReadOptions(void)
int opts = 0; int opts = 0;
std::ifstream file(TERMINALOPTIONS_PATH); std::ifstream file(m_TerminalPath.c_str());
if (file.is_open()) if (file.is_open())
{ {
m_GwAddress = 0u; m_GwAddress = 0u;
@ -742,12 +742,12 @@ void CG3Protocol::ReadOptions(void)
} }
} }
} }
std::cout << "G3 handler loaded " << opts << " options from file " << TERMINALOPTIONS_PATH << std::endl; std::cout << "G3 handler loaded " << opts << " options from file " << m_TerminalPath << std::endl;
file.close(); file.close();
struct stat fileStat; struct stat fileStat;
if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) if (::stat(m_TerminalPath.c_str(), &fileStat) != -1)
{ {
m_LastModTime = fileStat.st_mtime; m_LastModTime = fileStat.st_mtime;
} }

@ -19,6 +19,8 @@
#pragma once #pragma once
#include <string> #include <string>
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "SEProtocol.h" #include "SEProtocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"
@ -117,4 +119,5 @@ protected:
uint32_t m_GwAddress; uint32_t m_GwAddress;
std::string m_Modules; std::string m_Modules;
time_t m_LastModTime; time_t m_LastModTime;
std::string m_TerminalPath;
}; };

@ -16,14 +16,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "Timer.h"
#include "GateKeeper.h"
////////////////////////////////////////////////////////////////////////////////////////
CGateKeeper g_GateKeeper;
#include "Timer.h"
#include "Global.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// constructor // constructor
@ -49,9 +44,9 @@ bool CGateKeeper::Init(void)
{ {
// load lists from files // load lists from files
m_NodeWhiteList.LoadFromFile(WHITELIST_PATH); m_NodeWhiteList.LoadFromFile(g_Configure.GetString(g_Keys.files.white));
m_NodeBlackList.LoadFromFile(BLACKLIST_PATH); m_NodeBlackList.LoadFromFile(g_Configure.GetString(g_Keys.files.black));
m_PeerList.LoadFromFile(INTERLINKLIST_PATH); m_PeerList.LoadFromFile(g_Configure.GetString(g_Keys.files.interlink));
// reset run flag // reset run flag
keep_running = true; keep_running = true;
@ -92,9 +87,7 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol pr
case EProtocol::p25: case EProtocol::p25:
case EProtocol::usrp: case EProtocol::usrp:
case EProtocol::nxdn: case EProtocol::nxdn:
#ifndef NO_G3
case EProtocol::g3: case EProtocol::g3:
#endif
// first check is IP & callsigned listed OK // first check is IP & callsigned listed OK
ok &= IsNodeListedOk(callsign, ip); ok &= IsNodeListedOk(callsign, ip);
// todo: then apply any protocol specific authorisation for the operation // todo: then apply any protocol specific authorisation for the operation
@ -141,9 +134,7 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, const EP
case EProtocol::p25: case EProtocol::p25:
case EProtocol::nxdn: case EProtocol::nxdn:
case EProtocol::usrp: case EProtocol::usrp:
#ifndef NO_G3
case EProtocol::g3: case EProtocol::g3:
#endif
// first check is IP & callsigned listed OK // first check is IP & callsigned listed OK
ok = ok && IsNodeListedOk(callsign, ip, module); ok = ok && IsNodeListedOk(callsign, ip, module);
// todo: then apply any protocol specific authorisation for the operation // todo: then apply any protocol specific authorisation for the operation
@ -213,17 +204,20 @@ bool CGateKeeper::IsNodeListedOk(const CCallsign &callsign, const CIp &ip, char
{ {
// first check if callsign is in white list // first check if callsign is in white list
// note if white list is empty, everybody is authorized // note if white list is empty, everybody is authorized
const_cast<CCallsignList &>(m_NodeWhiteList).Lock(); m_NodeWhiteList.Lock();
if ( !m_NodeWhiteList.empty() ) if ( !m_NodeWhiteList.empty() )
{ {
ok = m_NodeWhiteList.IsCallsignListedWithWildcard(callsign, module); ok = m_NodeWhiteList.IsCallsignListedWithWildcard(callsign, module);
} }
const_cast<CCallsignList &>(m_NodeWhiteList).Unlock(); m_NodeWhiteList.Unlock();
// then check if not blacklisted // then check if not blacklisted
const_cast<CCallsignList &>(m_NodeBlackList).Lock(); if (ok)
ok &= !m_NodeBlackList.IsCallsignListedWithWildcard(callsign); {
const_cast<CCallsignList &>(m_NodeBlackList).Unlock(); m_NodeBlackList.Lock();
ok = !m_NodeBlackList.IsCallsignListedWithWildcard(callsign);
m_NodeBlackList.Unlock();
}
} }
// done // done
@ -241,12 +235,12 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char
if ( ok ) if ( ok )
{ {
// look for an exact match in the list // look for an exact match in the list
const_cast<CPeerCallsignList &>(m_PeerList).Lock(); m_PeerList.Lock();
if ( !m_PeerList.empty() ) if ( !m_PeerList.empty() )
{ {
ok = m_PeerList.IsCallsignListed(callsign, module); ok = m_PeerList.IsCallsignListed(callsign, module);
} }
const_cast<CPeerCallsignList &>(m_PeerList).Unlock(); m_PeerList.Unlock();
} }
// done // done
@ -263,12 +257,12 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char
if ( ok ) if ( ok )
{ {
// look for an exact match in the list // look for an exact match in the list
const_cast<CPeerCallsignList &>(m_PeerList).Lock(); m_PeerList.Lock();
if ( !m_PeerList.empty() ) if ( !m_PeerList.empty() )
{ {
ok = m_PeerList.IsCallsignListed(callsign, modules); ok = m_PeerList.IsCallsignListed(callsign, modules);
} }
const_cast<CPeerCallsignList &>(m_PeerList).Unlock(); m_PeerList.Unlock();
} }
// done // done
@ -300,10 +294,8 @@ const std::string CGateKeeper::ProtocolName(const EProtocol p) const
return "USRP"; return "USRP";
case EProtocol::bm: case EProtocol::bm:
return "Brandmeister"; return "Brandmeister";
#ifndef NO_G3
case EProtocol::g3: case EProtocol::g3:
return "Icom G3"; return "Icom G3";
#endif
default: default:
return "NONE"; return "NONE";
} }

@ -19,7 +19,7 @@
#pragma once #pragma once
#include "Main.h" #include "Defines.h"
#include "Callsign.h" #include "Callsign.h"
#include "IP.h" #include "IP.h"
#include "CallsignList.h" #include "CallsignList.h"

@ -1,7 +1,5 @@
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector // urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE // Copyright © 2023 Thomas A. Early N7TAE
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -16,25 +14,20 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #include "Reflector.h"
#include "GateKeeper.h"
#include "YSFNodeDir.h" #include "Configure.h"
#include "Version.h"
class CYsfNodeDirHttp : public CYsfNodeDir #include "LookupDmr.h"
{ #include "LookupNxdn.h"
public: #include "LookupYsf.h"
// constructor #include "JsonKeys.h"
CYsfNodeDirHttp() {}
// destructor
~CYsfNodeDirHttp() {}
// refresh
bool LoadContent(CBuffer *);
bool RefreshContent(const CBuffer &);
protected: extern CReflector g_Reflector;
// reload helpers extern CGateKeeper g_GateKeeper;
bool NeedReload(void) { return true; } extern CConfigure g_Configure;
bool HttpGet(const char *, const char *, int, CBuffer *); extern CVersion g_Version;
}; extern CLookupDmr g_LDid;
extern CLookupNxdn g_LNid;
extern CLookupYsf g_LYtr;
extern SJsonKeys g_Keys;

@ -0,0 +1,71 @@
/*
* Copyright (c) 2023 by Thomas A. Early N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string>
#pragma once
// configuration key names
struct SJsonKeys {
struct PORTONLY { const std::string port; }
dcs { "DCSPort" },
dextra { "DExtraPort" },
dmrplus { "DMRPlusPort" },
dplus { "DPlusPort" },
m17 { "M17Port" },
urf { "URFPort" };
struct G3 { const std::string enable; }
g3 { "G3Enable" };
struct BM { const std::string enable, port; }
bm { "bmEnable", "bmPort" };
struct MMDVM { const std::string port, defaultid; }
mmdvm { "MMDVMPort", "mmdvmdefaultid" };
struct NAMES { const std::string callsign, email, country, sponsor; }
names { "Callsign", "SysopEmail", "Country", "Sponsor" };
struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; }
ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" };
struct MODULES { const std::string modules, tcmodules, descriptor[26]; }
modules { "Modules", "TranscodedModules",
"DescriptionA", "DescriptionB", "DescriptionC", "DescriptionD", "DescriptionE", "DescriptionF", "DescriptionG", "DescriptionH", "DescriptionI", "DescriptionJ", "DescriptionK", "DescriptionL", "DescriptionM", "DescriptionN", "DescriptionO", "DescriptionP", "DescriptionQ", "DescriptionR", "DescriptionS", "DescriptionT", "DescriptionU", "DescriptionV", "DescriptionW", "DescriptionX", "DescriptionY", "DescriptionZ" };
struct USRP { const std::string enable, ip, txport, rxport, module, callsign, filepath; }
usrp { "usrpEnable", "usrpIpAddress", "urspTxPort", "usrpRxPort", "usrpModule", "usrpCallsign", "usrpFilePath" };
struct P25NXDN { const std::string port, autolinkmod, reflectorid; }
p25 { "P25Port", "P25AutolinkMod", "P25ReflectorID" },
nxdn { "NXDNPort", "NXDNAutolinkMod", "NXDNReflectorID" };
struct YSF { const std::string port, autolinkmod, defaulttxfreq, defaultrxfreq;
struct YSLREG { const std::string id, name, description; } ysfreflectordb; }
ysf { "YSFPort", "YSFAutoLinkMod", "YSFDefaultTxFreq", "YSFDefaultRxFreq",
{ "ysfrefdbid", "ysfrefdbname", "ysfrefdbdesc" } };
struct DB { const std::string url, mode, refreshmin, filepath; }
dmriddb { "dmrIdDbUrl", "dmrIdDbMode", "dmrIdDbRefresh", "dmrIdDbFilePath" },
nxdniddb { "nxdnIdDbUrl", "nxdnIdDbMode", "nxdnIdDbRefresh", "nxdnIdDbFilePath" },
ysftxrxdb { "ysfIdDbUrl", "ysfIdDbMode", "ysfIdDbRefresh", "ysfIdDbFilePath" };
struct FILES { const std::string pid, xml, json, white, black, interlink, terminal; }
files { "pidFilePath", "xmlFilePath", "jsonFilePath", "whitelistFilePath", "blacklistFilePath", "interlinkFilePath", "g3TerminalFilePath" };
};

@ -0,0 +1,125 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2023 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <fstream>
#include <unordered_map>
#include <thread>
#include <sys/stat.h>
#include "CurlGet.h"
#include "Lookup.h"
void CLookup::LookupClose()
{
keep_running = false;
if (m_Future.valid())
m_Future.get();
}
std::time_t CLookup::GetLastModTime()
{
struct stat fileStat;
if(0 == stat(m_Path.c_str(), &fileStat))
{
return fileStat.st_mtime;
}
return 0;
}
void CLookup::LookupInit()
{
LoadParameters();
m_Future = std::async(std::launch::async, &CLookup::Thread, this);
}
void CLookup::Thread()
{
const unsigned long wait_cycles = m_Refresh * 6u; // the number of while loops in m_Refresh
unsigned long count = 0;
while (keep_running)
{
std::stringstream ss;
bool http_loaded = false;
bool file_loaded = false;
// load http section first, if configured and m_Refresh minutes have lapsed
// on the first pass through this while loop (count == 0)
if (ERefreshType::file != m_Type && 0ul == count++ % wait_cycles)
{
// if SIG_INT was received at this point in time,
// in might take a bit more than 10 seconds to soft close
http_loaded = LoadContentHttp(ss);
}
// load the file if http was loaded or if we haven't loaded since the last mod time
if (ERefreshType::http != m_Type)
{
if (http_loaded || m_LastLoadTime < GetLastModTime())
{
file_loaded = LoadContentFile(ss);
time(&m_LastLoadTime);
}
}
// now update the map(s) if anything was loaded
if (http_loaded || file_loaded)
{
Lock();
// if m_Type == ERefreshType::both, and if something was deleted from the file,
// it won't be purged from the map(s) until http is loaded
// It would be a lot of work (iterating on an unordered_map) to do otherwise!
if (http_loaded || ERefreshType::file == m_Type)
ClearContents();
UpdateContent(ss, Eaction::normal);
Unlock();
}
// now wait for 10 seconds
std::this_thread::sleep_for(std::chrono::seconds(10));
}
}
bool CLookup::LoadContentHttp(std::stringstream &ss)
{
CCurlGet get;
auto code = get.GetURL(m_Url, ss);
return CURLE_OK == code;
}
bool CLookup::LoadContentFile(std::stringstream &ss)
{
bool rval = false;
std::ifstream file(m_Path);
if ( file )
{
ss << file.rdbuf();
file.close();
rval = true;
}
return rval;
}
bool CLookup::Utility(Eaction action, Esource source)
{
std::stringstream ss;
LoadParameters();
auto rval = (Esource::http == source) ? LoadContentHttp(ss) : LoadContentFile(ss);
if (rval)
UpdateContent(ss, action);
return rval;
}

@ -1,7 +1,7 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector // urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE // Copyright © 2023 Thomas A. Early N7TAE
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -18,68 +18,68 @@
#pragma once #pragma once
#include <sys/types.h> #include <atomic>
#include <sys/socket.h> #include <future>
#include <netinet/in.h> #include <iostream>
#include <netdb.h>
#include "Buffer.h"
#include "Callsign.h" #include "Callsign.h"
#include "Configure.h"
enum class Eaction { normal, parse, error_only };
enum class Esource { http, file };
// compare function for std::map::find // compare function for std::map::find
struct CDmridDirCallsignCompare struct CCallsignHash
{
std::size_t operator() (const UCallsign &ucs) const
{
std::hash<uint64_t> hash;
return hash(ucs.l);
}
};
struct CCallsignEqual
{ {
bool operator() (const CCallsign &cs1, const CCallsign &cs2) const bool operator() (const UCallsign &ucs1, const UCallsign &ucs2) const
{ return cs1.HasLowerCallsign(cs2);} {
return ucs1.l == ucs2.l;
}
}; };
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
class CDmridDir class CLookup
{ {
public: public:
// constructor // constructor
CDmridDir(); CLookup() : keep_running(true), m_LastLoadTime(0) {}
// destructor
~CDmridDir();
// init & close void LookupInit();
virtual bool Init(void); void LookupClose();
virtual void Close(void);
// locks // locks
void Lock(void) { m_Mutex.lock(); } void Lock(void) { m_Mutex.lock(); }
void Unlock(void) { m_Mutex.unlock(); } void Unlock(void) { m_Mutex.unlock(); }
bool Utility(Eaction action, Esource source);
// refresh
virtual bool LoadContent(CBuffer *) { return false; }
virtual bool RefreshContent(const CBuffer &) { return false; }
// find
const CCallsign *FindCallsign(uint32_t);
uint32_t FindDmrid(const CCallsign &);
protected: protected:
// thread std::time_t GetLastModTime();
virtual void LoadParameters() = 0;
virtual void ClearContents() = 0;
void Thread(); void Thread();
// reload helpers // refresh
bool Reload(void); bool LoadContentHttp(std::stringstream &ss);
virtual bool NeedReload(void) { return false; } bool LoadContentFile(std::stringstream &ss);
bool IsValidDmrid(const char *); virtual void UpdateContent(std::stringstream &ss, Eaction action) = 0;
protected:
// data
std::map <uint32_t, CCallsign> m_CallsignMap;
std::map <CCallsign, uint32_t, CDmridDirCallsignCompare> m_DmridMap;
// Lock()
std::mutex m_Mutex; std::mutex m_Mutex;
ERefreshType m_Type;
unsigned m_Refresh;
std::string m_Path, m_Url;
std::time_t m_LastLoadTime;
// thread
std::atomic<bool> keep_running; std::atomic<bool> keep_running;
std::future<void> m_Future; std::future<void> m_Future;
}; };

@ -0,0 +1,103 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2023 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netdb.h>
#include "Global.h"
void CLookupDmr::ClearContents()
{
m_CallsignMap.clear();
m_DmridMap.clear();
}
void CLookupDmr::LoadParameters()
{
m_Type = g_Configure.GetRefreshType(g_Keys.dmriddb.mode);
m_Refresh = g_Configure.GetUnsigned(g_Keys.dmriddb.refreshmin);
m_Path.assign(g_Configure.GetString(g_Keys.dmriddb.filepath));
m_Url.assign(g_Configure.GetString(g_Keys.dmriddb.url));
}
uint32_t CLookupDmr::FindDmrid(const UCallsign &ucs) const
{
auto found = m_DmridMap.find(ucs);
if ( found != m_DmridMap.end() )
{
return (found->second);
}
return 0;
}
const UCallsign *CLookupDmr::FindCallsign(const uint32_t dmrid) const
{
auto found = m_CallsignMap.find(dmrid);
if ( found != m_CallsignMap.end() )
{
return &(found->second);
}
return nullptr;
}
void CLookupDmr::UpdateContent(std::stringstream &ss, Eaction action)
{
std::string line;
while (std::getline(ss, line))
{
bool failed = true;
auto l = atol(line.c_str()); // no throw guarantee
if (0L < l && l <= 9999999L)
{
auto id = uint32_t(l);
auto p1 = line.find(';');
if (std::string::npos != p1)
{
auto p2 = line.find(';', ++p1);
if (std::string::npos != p2)
{
const auto cs_str(line.substr(p1, p2-p1));
CCallsign cs;
cs.SetCallsign(cs_str, false);
if (cs.IsValid())
{
failed = false;
if (Eaction::normal == action)
{
auto key = cs.GetKey();
m_DmridMap[key] = id;
m_CallsignMap[id] = key;
}
else if (Eaction::parse == action)
{
std::cout << id << ';' << cs_str << ";\n";
}
}
}
}
}
if (Eaction::error_only == action && failed)
{
std::cout << line << '\n';
}
}
if (Eaction::normal == action)
std::cout << "DMR Id database size: " << m_DmridMap.size() << std::endl;
}

@ -1,7 +1,7 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector // urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE // Copyright © 2023 Thomas A. Early N7TAE
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -18,23 +18,21 @@
#pragma once #pragma once
#include "NXDNIdDir.h" #include "Lookup.h"
class CNXDNidDirHttp : public CNXDNidDir class CLookupDmr : public CLookup
{ {
public: public:
// constructor ~CLookupDmr() {}
CNXDNidDirHttp() {} uint32_t FindDmrid(const UCallsign &ucs) const;
const UCallsign *FindCallsign(uint32_t dmrid) const;
// destructor
~CNXDNidDirHttp() {}
// refresh
bool LoadContent(CBuffer *);
bool RefreshContent(const CBuffer &);
protected: protected:
// reload helpers void ClearContents();
bool NeedReload(void) { return true; } void LoadParameters();
bool HttpGet(const char *, const char *, int, CBuffer *); void UpdateContent(std::stringstream &ss, Eaction action);
private:
std::unordered_map<uint32_t, UCallsign> m_CallsignMap;
std::unordered_map<UCallsign, uint32_t, CCallsignHash, CCallsignEqual> m_DmridMap;
}; };

@ -0,0 +1,103 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2023 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include "Global.h"
void CLookupNxdn::ClearContents()
{
m_CallsignMap.clear();
m_NxdnidMap.clear();
}
void CLookupNxdn::LoadParameters()
{
m_Type = g_Configure.GetRefreshType(g_Keys.nxdniddb.mode);
m_Refresh = g_Configure.GetUnsigned(g_Keys.nxdniddb.refreshmin);
m_Path.assign(g_Configure.GetString(g_Keys.nxdniddb.filepath));
m_Url.assign(g_Configure.GetString(g_Keys.nxdniddb.url));
}
const UCallsign *CLookupNxdn::FindCallsign(uint16_t nxdnid) const
{
auto found = m_CallsignMap.find(nxdnid);
if ( found != m_CallsignMap.end() )
{
return &(found->second);
}
return nullptr;
}
uint16_t CLookupNxdn::FindNXDNid(const UCallsign &ucs) const
{
auto found = m_NxdnidMap.find(ucs);
if ( found != m_NxdnidMap.end() )
{
return found->second;
}
return 0;
}
void CLookupNxdn::UpdateContent(std::stringstream &ss, Eaction action)
{
std::string line;
while (std::getline(ss, line))
{
bool failed = true;
auto l = atol(line.c_str()); // no throw guarantee
if (0 < l && l < 0x10000)
{
auto id = uint32_t(l);
auto p1 = line.find(',');
if (std::string::npos != p1)
{
auto p2 = line.find(',', ++p1);
if (std::string::npos != p2)
{
const auto cs_str = line.substr(p1, p2-p1);
CCallsign cs;
cs.SetCallsign(cs_str, false);
if (cs.IsValid())
{
failed = false;
if (Eaction::normal == action)
{
auto key = cs.GetKey();
m_NxdnidMap[key] = id;
m_CallsignMap[id] = key;
}
else if (Eaction::parse == action)
{
std::cout << id << ',' << cs_str << ",\n";
}
}
}
}
}
if (Eaction::error_only == action && failed)
{
std::cout << line << '\n';
}
}
if (Eaction::normal == action)
std::cout << "NXDN Id database size: " << m_NxdnidMap.size() << std::endl;
}

@ -1,7 +1,7 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector // urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE // Copyright © 2023 Thomas A. Early N7TAE
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -18,23 +18,19 @@
#pragma once #pragma once
#include "DMRIdDir.h" #include "Lookup.h"
class CDmridDirHttp : public CDmridDir class CLookupNxdn : public CLookup
{ {
public: public:
// constructor uint16_t FindNXDNid(const UCallsign &ucs) const;
CDmridDirHttp() {} const UCallsign *FindCallsign(const uint16_t id) const;
// destructor
~CDmridDirHttp() {}
// refresh
bool LoadContent(CBuffer *);
bool RefreshContent(const CBuffer &);
protected: protected:
// reload helpers void ClearContents();
bool NeedReload(void) { return true; } void LoadParameters();
bool HttpGet(const char *, const char *, int, CBuffer *); void UpdateContent(std::stringstream &ss, Eaction action);
private:
std::unordered_map <uint32_t, UCallsign> m_CallsignMap;
std::unordered_map <UCallsign, uint32_t, CCallsignHash, CCallsignEqual> m_NxdnidMap;
}; };

@ -0,0 +1,89 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2023 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netdb.h>
#include "Global.h"
void CLookupYsf::ClearContents()
{
m_map.clear();
}
void CLookupYsf::LoadParameters()
{
m_Type = g_Configure.GetRefreshType(g_Keys.ysftxrxdb.mode);
m_Refresh = g_Configure.GetUnsigned(g_Keys.ysftxrxdb.refreshmin);
m_Path.assign(g_Configure.GetString(g_Keys.ysftxrxdb.filepath));
m_Url.assign(g_Configure.GetString(g_Keys.ysftxrxdb.url));
m_DefaultTx = g_Configure.GetUnsigned(g_Keys.ysf.defaulttxfreq);
m_DefaultRx = g_Configure.GetUnsigned(g_Keys.ysf.defaultrxfreq);
}
void CLookupYsf::UpdateContent(std::stringstream &ss, Eaction action)
{
std::string line;
while (std::getline(ss, line))
{
CCallsign cs;
std::string cs_str, tx_str, rx_str;
std::istringstream iss(line);
std::getline(iss, cs_str, ';');
std::getline(iss, tx_str, ';');
std::getline(iss, rx_str, ';');
cs.SetCallsign(cs_str, false);
auto ltx = atoll(tx_str.c_str());
auto lrx = atoll(rx_str.c_str());
unsigned x = sizeof(lrx);
if (ltx > 40000000 && ltx < 0x100000000 && lrx > 40000000 && lrx < 0x100000000 && cs.IsValid())
{
if (Eaction::parse == action)
{
std::cout << cs_str << ';' << tx_str << ';' << rx_str << ";\n";
}
else if (Eaction::normal == action)
{
m_map[cs.GetKey()] = CYsfNode(ltx, lrx);
}
}
else if (Eaction::error_only == action)
{
std::cout << line << '\n';
}
}
if (Eaction::normal == action)
std::cout << "YSF frequency database size now is " << m_map.size() << std::endl;
}
void CLookupYsf::FindFrequencies(const CCallsign &cs, uint32_t &txfreq, uint32_t &rxfreq)
{
auto found = m_map.find(cs.GetKey());
if (found != m_map.end())
{
txfreq = found->second.GetTxFrequency();
rxfreq = found->second.GetRxFrequency();
}
else
{
txfreq = m_DefaultTx;
rxfreq = m_DefaultRx;
}
}

@ -1,7 +1,7 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector // urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE // Copyright © 2023 Thomas A. Early N7TAE
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -18,30 +18,23 @@
#pragma once #pragma once
#include "NXDNIdDir.h" #include "YSFNode.h"
#include "Lookup.h"
class CNXDNidDirFile : public CNXDNidDir using CsNodeMap = std::unordered_map<UCallsign, CYsfNode, CCallsignHash, CCallsignEqual>;
class CLookupYsf : public CLookup
{ {
public: public:
// constructor void FindFrequencies(const CCallsign &, uint32_t &, uint32_t &);
CNXDNidDirFile();
// destructor
~CNXDNidDirFile() {}
// init & close
bool Init(void);
// refresh
bool LoadContent(CBuffer *);
bool RefreshContent(const CBuffer &);
protected: protected:
// reload helpers void ClearContents();
bool NeedReload(void); void LoadParameters();
bool GetLastModTime(time_t *); void UpdateContent(std::stringstream &ss, Eaction action);
protected: private:
// data CsNodeMap m_map;
time_t m_LastModTime;
unsigned m_DefaultTx, m_DefaultRx;
}; };

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "M17Client.h" #include "M17Client.h"

@ -16,15 +16,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Defines.h"
#include "Client.h" #include "Client.h"
////////////////////////////////////////////////////////////////////////////////////////
// define
////////////////////////////////////////////////////////////////////////////////////////
// class
class CM17Client : public CClient class CM17Client : public CClient
{ {
public: public:

@ -84,11 +84,6 @@ void CM17Packet::SetCRC(uint16_t crc)
m17.crc = htons(crc); m17.crc = htons(crc);
} }
std::unique_ptr<CM17Packet> CM17Packet::Duplicate(void) const
{
return std::unique_ptr<CM17Packet>(new CM17Packet(*this));
}
bool CM17Packet::IsLastPacket() const bool CM17Packet::IsLastPacket() const
{ {
return ((0x8000u & ntohs(m17.framenumber)) == 0x8000u); return ((0x8000u & ntohs(m17.framenumber)) == 0x8000u);

@ -69,7 +69,6 @@ public:
uint16_t GetStreamId() const; uint16_t GetStreamId() const;
uint16_t GetCRC() const; uint16_t GetCRC() const;
void SetCRC(uint16_t crc); void SetCRC(uint16_t crc);
std::unique_ptr<CM17Packet> Duplicate(void) const;
bool IsLastPacket() const; bool IsLastPacket() const;
private: private:

@ -16,13 +16,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "M17Client.h" #include "M17Client.h"
#include "M17Protocol.h" #include "M17Protocol.h"
#include "M17Packet.h" #include "M17Packet.h"
#include "Reflector.h" #include "Global.h"
#include "GateKeeper.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// operation // operation
@ -222,11 +221,10 @@ void CM17Protocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
void CM17Protocol::HandleQueue(void) void CM17Protocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto module = packet->GetPacketModule(); const auto module = packet->GetPacketModule();
@ -271,7 +269,6 @@ void CM17Protocol::HandleQueue(void)
m_StreamsCache[module].m_iSeqCounter++; m_StreamsCache[module].m_iSeqCounter++;
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////

@ -18,6 +18,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"

@ -16,53 +16,56 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "Reflector.h"
#include <sys/stat.h> #include <sys/stat.h>
#include "Global.h"
#ifndef UTILITY
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// global objects // global objects
SJsonKeys g_Keys;
CReflector g_Reflector; CReflector g_Reflector;
CGateKeeper g_GateKeeper;
CConfigure g_Configure;
CVersion g_Version(3,0,1); // The major byte should only change if the interlink packet changes!
CLookupDmr g_LDid;
CLookupNxdn g_LNid;
CLookupYsf g_LYtr;
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// function declaration
#include "Users.h"
int main() int main(int argc, char *argv[])
{ {
const std::string cs(CALLSIGN); if (argc != 2)
if ((cs.size() != 6) || cs.compare(0, 3, "URF"))
{ {
std::cerr << "Malformed reflector callsign: '" << cs << "', aborting!" << std::endl; std::cerr << "No configuration file specifed! Usage: " << argv[0] << " /pathname/to/configuration/file" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (g_Configure.ReadData(argv[1]))
return EXIT_FAILURE;
std::cout << "IPv4 binding address is '" << g_Configure.GetString(g_Keys.ip.ipv4bind) << "'" << std::endl;
// remove pidfile // remove pidfile
remove(PIDFILE_PATH); const std::string pidpath(g_Configure.GetString(g_Keys.files.pid));
const std::string callsign(g_Configure.GetString(g_Keys.names.callsign));
remove(pidpath.c_str());
// splash // splash
std::cout << "Starting " << cs << " " << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_REVISION << std::endl << std::endl; std::cout << "Starting " << callsign << " " << g_Version << std::endl;
#ifdef TRANSCODER_IS_REMOTE
g_Reflector.SetTranscoderIp(TRANSCODER_IP, INET6_ADDRSTRLEN);
#endif
// and let it run // and let it run
if ( !g_Reflector.Start() ) if (g_Reflector.Start())
{ {
std::cout << "Error starting reflector" << std::endl; std::cout << "Error starting reflector" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "Reflector " << g_Reflector.GetCallsign() << "started and listening" << std::endl; std::cout << "Reflector " << callsign << " started and listening" << std::endl;
// write new pid file // write new pid file
std::ofstream ofs(PIDFILE_PATH, std::ofstream::out); std::ofstream ofs(pidpath, std::ofstream::out);
ofs << getpid() << std::endl; ofs << getpid() << std::endl;
ofs.close(); ofs.close();
@ -74,3 +77,132 @@ int main()
// done // done
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#else // UTILITY is defined
////////////////////////////////////////////////////////////////////////////////////////
// global objects
SJsonKeys g_Keys;
CConfigure g_Configure;
CLookupDmr g_LDid;
CLookupNxdn g_LNid;
CLookupYsf g_LYtr;
static void usage(std::ostream &os, const char *name)
{
os << "\nUsage: " << name << " DATABASE SOURCE ACTION INIFILE\n";
os << "DATABASE (choose one)\n"
" dmr : The DmrId <==> Callsign databases.\n"
" nxdn : The NxdnId <==> Callsign databases.\n"
" ysf : The Callsign => Tx/Rx frequency database.\n"
"SOURCE (choose one)\n"
" file : The file specified by the FilePath ini parameter.\n"
" http : The URL specified by the URL ini paramater.\n"
"ACTION (choose one)\n"
" print : Print all lines from the SOURCE that are syntactically correct.\n"
" error : Print only the lines with failed syntax.\n"
"INIFILE : an error-free urfd ini file (check it first with inicheck).\n\n"
"Only the first character of DATABASE, SOURCE and ACTION is read.\n"
"Example: " << name << " y f e urfd.ini # Check your YSF Tx/Rx database file specifed in urfd.ini for syntax errors.\n\n";
}
enum class Edb { none, dmr, nxdn, ysf };
int main(int argc, char *argv[])
{
Edb db;
Eaction action;
Esource source;
if (5 != argc)
{
usage(std::cerr, argv[0]);
return EXIT_FAILURE;
}
switch (argv[1][0])
{
case 'd':
case 'D':
db = Edb::dmr;
break;
case 'n':
case 'N':
db = Edb::nxdn;
break;
case 'y':
case 'Y':
db = Edb::ysf;
break;
default:
std::cout << "Unrecognized DATABASE: " << argv[1];
db = Edb::none;
break;
}
switch (argv[2][0])
{
case 'h':
case 'H':
source = Esource::http;
break;
case 'f':
case 'F':
source = Esource::file;
break;
default:
std::cerr << "Unrecognized SOURCE: " << argv[2] << std::endl;
db = Edb::none;
break;
}
switch (argv[3][0])
{
case 'p':
case 'P':
action = Eaction::parse;
break;
case 'e':
case 'E':
action = Eaction::error_only;
break;
default:
std::cerr << "Unrecognized ACTION: " << argv[3] << std::endl;
db = Edb::none;
break;
}
if (db == Edb::none)
{
usage(std::cerr, argv[0]);
return EXIT_FAILURE;
}
if (g_Configure.ReadData(argv[4]))
return EXIT_FAILURE;
switch (db)
{
case Edb::dmr:
g_LDid.Utility(action, source);
break;
case Edb::nxdn:
g_LNid.Utility(action, source);
break;
case Edb::ysf:
g_LYtr.Utility(action, source);
break;
}
return EXIT_SUCCESS;
}
#endif

@ -18,78 +18,56 @@
# if you change these locations, make sure the sgs.service file is updated! # if you change these locations, make sure the sgs.service file is updated!
# you will also break hard coded paths in the dashboard file, index.php. # you will also break hard coded paths in the dashboard file, index.php.
include configure.mk EXE = urfd
# if you make changed in these two variable, you'll need to change things INICHECK = inicheck
# in the main.h file as well as the systemd service file.
BINDIR = /usr/local/bin
CFGDIR = /usr/local/etc
CC = g++ DBUTIL = dbutil
include urfd.mk
ifeq ($(debug), true) ifeq ($(debug), true)
CFLAGS = -ggdb3 -W -Werror -Wno-psabi -c -std=c++11 -MMD -MD -c CFLAGS = -ggdb3 -DDEBUG -W -Werror -std=c++11 -MMD -MD
else else
CFLAGS = -c -W -Werror -Wno-psabi -std=c++11 -MMD -MD -c CFLAGS = -W -Werror -std=c++11 -MMD -MD
endif endif
LDFLAGS=-pthread LDFLAGS=-pthread -lcurl
URFSRCS = Buffer.cpp Callsign.cpp CallsignList.cpp CallsignListItem.cpp Client.cpp Clients.cpp DCSClient.cpp DCSProtocol.cpp DExtraClient.cpp DExtraPeer.cpp DExtraProtocol.cpp DPlusClient.cpp DPlusProtocol.cpp DVFramePacket.cpp DVHeaderPacket.cpp GateKeeper.cpp IP.cpp Notification.cpp Packet.cpp PacketStream.cpp PeerCallsignList.cpp Peer.cpp Peers.cpp Protocol.cpp Protocols.cpp Reflector.cpp SEProtocol.cpp UDPSocket.cpp User.cpp Users.cpp Version.cpp Main.cpp BMClient.cpp BMPeer.cpp BMProtocol.cpp BPTC19696.cpp CRC.cpp DMRIdDir.cpp DMRIdDirFile.cpp DMRIdDirHttp.cpp NXDNIdDir.cpp NXDNIdDirFile.cpp NXDNIdDirHttp.cpp DMRMMDVMClient.cpp DMRMMDVMProtocol.cpp DMRPlusClient.cpp DMRPlusProtocol.cpp Golay2087.cpp Golay24128.cpp Hamming.cpp M17Client.cpp M17CRC.cpp M17Packet.cpp M17Client.cpp M17Protocol.cpp NXDNClient.cpp NXDNProtocol.cpp P25Client.cpp P25Protocol.cpp QR1676.cpp RS129.cpp Semaphore.cpp USRPClient.cpp USRPProtocol.cpp Utils.cpp WiresXCmd.cpp WiresXCmdHandler.cpp WiresXInfo.cpp URFClient.cpp URFProtocol.cpp URFPeer.cpp YSFClient.cpp YSFConvolution.cpp YSFFich.cpp YSFNode.cpp YSFNodeDir.cpp YSFNodeDirFile.cpp YSFNodeDirHttp.cpp YSFPayload.cpp YSFProtocol.cpp YSFUtils.cpp
G3SRCS = G3Client.cpp G3Protocol.cpp RawSocket.cpp UDPMsgSocket.cpp
SRCS = $(URFSRCS)
ifeq ($(use_g3), true)
SRCS += $(G3SRCS)
endif
ifeq ($(ysf_db), true)
LDFLAGS += `mysql_config --libs`
endif
ifdef tc_ip
SRCS += CodecStream.cpp UnixDgramSocket.cpp
endif
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o) OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d) DEPS = $(SRCS:.cpp=.d)
DBUTILOBJS = Configure.o CurlGet.o Lookup.o LookupDmr.o LookupNxdn.o LookupYsf.o YSFNode.o Callsign.o
EXE=urfd all : $(EXE) $(INICHECK) $(DBUTIL)
all : $(EXE)
$(EXE) : $(OBJS) $(EXE) : $(OBJS)
$(CC) $^ -o $@ $(LDFLAGS) $(CXX) $^ -o $@ $(LDFLAGS)
$(INICHECK) : Configure.cpp CurlGet.o
$(CXX) -DINICHECK $(CFLAGS) $< CurlGet.o -o $@ -lcurl
$(DBUTIL) : Main.cpp $(DBUTILOBJS)
$(CXX) -DUTILITY $(CFLAGS) $< $(DBUTILOBJS) -o $@ -pthread -lcurl
%.o : %.cpp %.o : %.cpp
g++ $(CFLAGS) $< -o $@ $(CXX) $(CFLAGS) -c $< -o $@
clean : clean :
$(RM) *.o *.d urfd $(RM) *.o *.d $(EXE) $(INICHECK) $(DBUTIL)
-include $(DEPS) -include $(DEPS)
install : install :
ln -f -s $(shell pwd)/$(EXE).blacklist $(CFGDIR)/$(EXE).blacklist cp -f $(EXE).service /etc/systemd/system/
ln -f -s $(shell pwd)/$(EXE).whitelist $(CFGDIR)/$(EXE).whitelist
ln -f -s $(shell pwd)/$(EXE).interlink $(CFGDIR)/$(EXE).interlink
ifeq ($(use_g3), true)
ln -f -s $(shell pwd)/$(EXE).terminal $(CFGDIR)/$(EXE).terminal
endif
cp -f ../systemd/$(EXE).service /etc/systemd/system/
cp -f $(EXE) $(BINDIR) cp -f $(EXE) $(BINDIR)
systemctl enable $(EXE).service systemctl enable $(EXE).service
systemctl daemon-reload systemctl daemon-reload
systemctl start $(EXE) systemctl start $(EXE)
uninstall : uninstall :
rm -f $(CFGDIR)/$(EXE).blacklist
rm -f $(CFGDIR)/$(EXE).whitelist
rm -f $(CFGDIR)/$(EXE).interlink
rm -f $(CFGDIR)/$(EXE).terminal
systemctl stop $(EXE).service systemctl stop $(EXE).service
rm -f $(CFGDIR)/dmrid.dat
systemctl disable $(EXE).service systemctl disable $(EXE).service
rm -f /etc/systemd/system/$(EXE).service $(RM) /etc/systemd/system/$(EXE).service
systemctl daemon-reload systemctl daemon-reload
$(RM) $(BINDIR)/$(EXE)

@ -17,7 +17,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "NXDNClient.h" #include "NXDNClient.h"

@ -19,7 +19,9 @@
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CNXDNClient : public CClient class CNXDNClient : public CClient
{ {
public: public:

@ -1,145 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include "Main.h"
#include "Reflector.h"
#include "NXDNIdDir.h"
#include "NXDNIdDirFile.h"
#include "NXDNIdDirHttp.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor
CNXDNidDir::CNXDNidDir()
{
keep_running = true;
}
CNXDNidDir::~CNXDNidDir()
{
Close();
}
////////////////////////////////////////////////////////////////////////////////////////
// init & close
bool CNXDNidDir::Init(void)
{
// load content
Reload();
// reset run flag
keep_running = true;
// start thread;
m_Future = std::async(std::launch::async, &CNXDNidDir::Thread, this);
return true;
}
void CNXDNidDir::Close(void)
{
keep_running = false;
if ( m_Future.valid() )
{
m_Future.get();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// thread
void CNXDNidDir::Thread()
{
while (keep_running)
{
// Wait DMRIDDB_REFRESH_RATE minutes
for (int i=0; i<30*NXDNIDDB_REFRESH_RATE && keep_running; i++)
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// have lists files changed ?
if ( NeedReload() )
{
Reload();
}
}
}
////////////////////////////////////////////////////////////////////////////////////////
// Reload
bool CNXDNidDir::Reload(void)
{
CBuffer buffer;
bool ok = false;
if ( LoadContent(&buffer) )
{
Lock();
{
ok = RefreshContent(buffer);
}
Unlock();
}
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// find
const CCallsign *CNXDNidDir::FindCallsign(uint16_t nxdnid)
{
auto found = m_CallsignMap.find(nxdnid);
if ( found != m_CallsignMap.end() )
{
return &(found->second);
}
return nullptr;
}
uint16_t CNXDNidDir::FindNXDNid(const CCallsign &callsign)
{
auto found = m_NXDNidMap.find(callsign);
if ( found != m_NXDNidMap.end() )
{
return (found->second);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////
// syntax helpers
bool CNXDNidDir::IsValidNXDNid(const char *sz)
{
bool ok = false;
size_t n = ::strlen(sz);
if ( (n > 0) && (n <= 5) )
{
ok = true;
for ( size_t i = 0; (i < n) && ok; i++ )
{
ok &= ::isdigit(sz[i]);
}
}
return ok;
}

@ -1,86 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
//
// 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 <https://www.gnu.org/licenses/>.
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "Buffer.h"
#include "Callsign.h"
// compare function for std::map::find
struct CNXDNidDirCallsignCompare
{
bool operator() (const CCallsign &cs1, const CCallsign &cs2) const
{ return cs1.HasLowerCallsign(cs2);}
};
////////////////////////////////////////////////////////////////////////////////////////
class CNXDNidDir
{
public:
// constructor
CNXDNidDir();
// destructor
~CNXDNidDir();
// init & close
virtual bool Init(void);
virtual void Close(void);
// locks
void Lock(void) { m_Mutex.lock(); }
void Unlock(void) { m_Mutex.unlock(); }
// refresh
virtual bool LoadContent(CBuffer *) { return false; }
virtual bool RefreshContent(const CBuffer &) { return false; }
// find
const CCallsign *FindCallsign(uint16_t);
uint16_t FindNXDNid(const CCallsign &);
protected:
// thread
void Thread();
// reload helpers
bool Reload(void);
virtual bool NeedReload(void) { return false; }
bool IsValidNXDNid(const char *);
protected:
// data
std::map <uint16_t, CCallsign> m_CallsignMap;
std::map <CCallsign, uint16_t, CNXDNidDirCallsignCompare> m_NXDNidMap;
// Lock()
std::mutex m_Mutex;
// thread
std::atomic<bool> keep_running;
std::future<void> m_Future;
};

@ -1,159 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "Main.h"
#include "NXDNIdDirFile.h"
#if (NXDNIDDB_USE_RLX_SERVER == 0)
CNXDNidDirFile g_NXDNidDir;
#endif
////////////////////////////////////////////////////////////////////////////////////////
// constructor & destructor
CNXDNidDirFile::CNXDNidDirFile()
{
memset(&m_LastModTime, 0, sizeof(time_t));
}
////////////////////////////////////////////////////////////////////////////////////////
// init & close
bool CNXDNidDirFile::Init(void)
{
return CNXDNidDir::Init();
}
////////////////////////////////////////////////////////////////////////////////////////
// refresh
bool CNXDNidDirFile::NeedReload(void)
{
bool needReload = false;
time_t time;
if ( GetLastModTime(&time) )
{
needReload = time != m_LastModTime;
}
return needReload;
}
bool CNXDNidDirFile::LoadContent(CBuffer *buffer)
{
bool ok = false;
std::ifstream file;
std::streampos size;
// open file
file.open(NXDNIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
if ( file.is_open() )
{
// read file
size = file.tellg();
if ( size > 0 )
{
// read file into buffer
buffer->resize((int)size+1);
file.seekg (0, std::ios::beg);
file.read((char *)buffer->data(), (int)size);
// close file
file.close();
// update time
GetLastModTime(&m_LastModTime);
// done
ok = true;
}
}
// done
return ok;
}
bool CNXDNidDirFile::RefreshContent(const CBuffer &buffer)
{
bool ok = false;
// clear directory
m_CallsignMap.clear();
m_NXDNidMap.clear();
// scan buffer
if ( buffer.size() > 0 )
{
// crack it
char *ptr1 = (char *)buffer.data();
char *ptr2;
// get next line
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
{
*ptr2 = 0;
// get items
char *nxdnid;
char *callsign;
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNXDNid(nxdnid) )
{
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
{
// new entry
uint16_t us = atoi(nxdnid);
CCallsign cs(callsign, 0, us);
if ( cs.IsValid() )
{
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(us, cs));
m_NXDNidMap.insert(std::pair<CCallsign,uint32_t>(cs,us));
}
}
}
// next line
ptr1 = ptr2+1;
}
// done
ok = true;
}
// report
std::cout << "Read " << m_NXDNidMap.size() << " NXDN ids from file " << NXDNIDDB_PATH << std::endl;
// done
return ok;
}
bool CNXDNidDirFile::GetLastModTime(time_t *time)
{
bool ok = false;
struct stat fileStat;
if( ::stat(NXDNIDDB_PATH, &fileStat) != -1 )
{
*time = fileStat.st_mtime;
ok = true;
}
return ok;
}

@ -1,177 +0,0 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
//
// 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 <https://www.gnu.org/licenses/>.
#include <string.h>
#include "Main.h"
#include "Reflector.h"
#include "NXDNIdDirHttp.h"
#if (NXDNIDDB_USE_RLX_SERVER == 1)
CNXDNidDirHttp g_NXDNidDir;
#endif
////////////////////////////////////////////////////////////////////////////////////////
// refresh
bool CNXDNidDirHttp::LoadContent(CBuffer *buffer)
{
// get file from xlxapi server
return HttpGet("www.dudetronics.com", "ar-dns/NXDN.csv", 80, buffer);
}
bool CNXDNidDirHttp::RefreshContent(const CBuffer &buffer)
{
bool ok = false;
// clear directory
m_CallsignMap.clear();
m_NXDNidMap.clear();
// scan file
if ( buffer.size() > 0 )
{
char *ptr1 = (char *)buffer.data();
char *ptr2;
// get next line
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
{
std::cout << "newline: " << std::string(ptr2) << std::endl;
*ptr2 = 0;
// get items
char *nxdnid;
char *callsign;
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNXDNid(nxdnid) )
{
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
{
// new entry
uint16_t us = atoi(nxdnid);
CCallsign cs(callsign, 0, us);
if ( cs.IsValid() )
{
m_CallsignMap.insert(std::pair<uint16_t,CCallsign>(us, cs));
m_NXDNidMap.insert(std::pair<CCallsign,uint16_t>(cs,us));
}
}
}
// next line
ptr1 = ptr2+1;
}
// done
ok = true;
}
// report
std::cout << "Read " << m_NXDNidMap.size() << " NXDN ids from xlxapi.rlx.lu database " << std::endl;
// done
return ok;
}
////////////////////////////////////////////////////////////////////////////////////////
// httpd helpers
#define NXDNID_HTTPGET_SIZEMAX (256)
bool CNXDNidDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
{
bool ok = false;
int sock_id;
// open socket
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
{
// get hostname address
struct sockaddr_in servaddr;
struct hostent *hp;
memset(&servaddr,0,sizeof(servaddr));
if( (hp = gethostbyname(hostname)) != nullptr )
{
// dns resolved
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
servaddr.sin_port = htons(port);
servaddr.sin_family = AF_INET;
// connect
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
{
// send the GET request
char request[NXDNID_HTTPGET_SIZEMAX];
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n",
filename, (const char *)g_Reflector.GetCallsign());
::write(sock_id, request, strlen(request));
// config receive timeouts
fd_set read_set;
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
FD_ZERO(&read_set);
FD_SET(sock_id, &read_set);
// get the reply back
buffer->clear();
bool done = false;
do
{
char buf[1440];
ssize_t len = 0;
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
//if ( ret >= 0 )
//{
usleep(5000);
len = read(sock_id, buf, 1440);
if ( len > 0 )
{
buffer->Append((uint8_t *)buf, (int)len);
ok = true;
}
//}
done = (len <= 0);
}
while (!done);
buffer->Append((uint8_t)0);
// and disconnect
close(sock_id);
}
else
{
std::cout << "Cannot establish connection with host " << hostname << std::endl;
}
}
else
{
std::cout << "Host " << hostname << " not found" << std::endl;
}
}
else
{
std::cout << "Failed to open wget socket" << std::endl;
}
// done
return ok;
}

@ -17,14 +17,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "NXDNClient.h" #include "NXDNClient.h"
#include "NXDNProtocol.h" #include "NXDNProtocol.h"
#include "YSFDefines.h" #include "YSFDefines.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include "Golay24128.h" #include "Golay24128.h"
#include "Global.h"
const uint8_t NXDN_LICH_RFCT_RDCH = 2U; const uint8_t NXDN_LICH_RFCT_RDCH = 2U;
const uint8_t NXDN_LICH_USC_SACCH_NS = 0U; const uint8_t NXDN_LICH_USC_SACCH_NS = 0U;
@ -53,6 +52,10 @@ CNXDNProtocol::CNXDNProtocol()
bool CNXDNProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6) bool CNXDNProtocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{ {
// config value
m_ReflectorId = g_Configure.GetUnsigned(g_Keys.nxdn.reflectorid);
m_AutolinkModule = g_Configure.GetAutolinkModule(g_Keys.nxdn.autolinkmod);
// base class // base class
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false; return false;
@ -130,9 +133,8 @@ void CNXDNProtocol::Task(void)
auto newclient = std::make_shared<CNXDNClient>(Callsign, Ip); auto newclient = std::make_shared<CNXDNClient>(Callsign, Ip);
// aautolink, if enabled // aautolink, if enabled
#if NXDN_AUTOLINK_ENABLE if (m_AutolinkModule)
newclient->SetReflectorModule(NXDN_AUTOLINK_MODULE); newclient->SetReflectorModule(m_AutolinkModule);
#endif
// and append // and append
clients->AddClient(newclient); clients->AddClient(newclient);
@ -141,7 +143,7 @@ void CNXDNProtocol::Task(void)
{ {
client->Alive(); client->Alive();
} }
// acknowledge the request -- NXDNReflector simply echoes the packet // acknowledge the request -- NXDNReflector simply echoes the packet
Send(Buffer, Ip); Send(Buffer, Ip);
// and done // and done
@ -244,11 +246,10 @@ void CNXDNProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
void CNXDNProtocol::HandleQueue(void) void CNXDNProtocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto mod = packet->GetPacketModule(); const auto mod = packet->GetPacketModule();
@ -308,7 +309,6 @@ void CNXDNProtocol::HandleQueue(void)
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }
} }
m_Queue.Unlock();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -408,24 +408,24 @@ bool CNXDNProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer, s
rpt1.SetCSModule(NXDN_MODULE_ID); rpt1.SetCSModule(NXDN_MODULE_ID);
rpt2.SetCSModule(' '); rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, false)); header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, false));
if ( g_GateKeeper.MayTransmit(header->GetMyCallsign(), Ip, EProtocol::nxdn, header->GetRpt2Module()) ) if ( g_GateKeeper.MayTransmit(header->GetMyCallsign(), Ip, EProtocol::nxdn, header->GetRpt2Module()) )
{ {
OnDvHeaderPacketIn(header, Ip); OnDvHeaderPacketIn(header, Ip);
} }
} }
// get DV frames // get DV frames
uint8_t ambe49[7]; uint8_t ambe49[7];
uint8_t ambe0[9]; uint8_t ambe0[9];
uint8_t ambe1[9]; uint8_t ambe1[9];
uint8_t ambe2[9]; uint8_t ambe2[9];
uint8_t ambe3[9]; uint8_t ambe3[9];
memcpy(ambe49, Buffer.data() + 15, 7); memcpy(ambe49, Buffer.data() + 15, 7);
encode(ambe49, ambe0); encode(ambe49, ambe0);
uint8_t t[7]; uint8_t t[7];
const uint8_t *d = &(Buffer.data()[21]); const uint8_t *d = &(Buffer.data()[21]);
for(int i = 0; i < 6; ++i){ for(int i = 0; i < 6; ++i){
@ -436,10 +436,10 @@ bool CNXDNProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer, s
memcpy(ambe49, t, 7); memcpy(ambe49, t, 7);
encode(ambe49, ambe1); encode(ambe49, ambe1);
memcpy(ambe49, Buffer.data() + 29, 7); memcpy(ambe49, Buffer.data() + 29, 7);
encode(ambe49, ambe2); encode(ambe49, ambe2);
d = &(Buffer.data()[35]); d = &(Buffer.data()[35]);
for(int i = 0; i < 6; ++i){ for(int i = 0; i < 6; ++i){
t[i] = d[i] << 1; t[i] = d[i] << 1;
@ -479,15 +479,15 @@ bool CNXDNProtocol::EncodeNXDNHeaderPacket(const CDvHeaderPacket &Header, CBuffe
{ {
Buffer.resize(43); Buffer.resize(43);
uint16_t NXDNId = Header.GetMyCallsign().GetNXDNid(); uint16_t NXDNId = Header.GetMyCallsign().GetNXDNid();
uint16_t RptrId = NXDN_REFID; uint16_t RptrId = m_ReflectorId;
memcpy(Buffer.data(), "NXDND", 5); memcpy(Buffer.data(), "NXDND", 5);
Buffer.data()[5U] = (NXDNId >> 8) & 0xFFU; Buffer.data()[5U] = (NXDNId >> 8) & 0xFFU;
Buffer.data()[6U] = (NXDNId >> 0) & 0xFFU; Buffer.data()[6U] = (NXDNId >> 0) & 0xFFU;
Buffer.data()[7U] = (RptrId >> 8) & 0xFFU; Buffer.data()[7U] = (RptrId >> 8) & 0xFFU;
Buffer.data()[8U] = (RptrId >> 0) & 0xFFU; Buffer.data()[8U] = (RptrId >> 0) & 0xFFU;
Buffer.data()[9U] = 0x01U; Buffer.data()[9U] = 0x01U;
const uint8_t idle[3U] = {0x10, 0x00, 0x00}; const uint8_t idle[3U] = {0x10, 0x00, 0x00};
m_lich = 0; m_lich = 0;
memset(m_sacch, 0, 5U); memset(m_sacch, 0, 5U);
@ -514,7 +514,7 @@ bool CNXDNProtocol::EncodeNXDNHeaderPacket(const CDvHeaderPacket &Header, CBuffe
set_layer3_blks(0U); set_layer3_blks(0U);
memcpy(&Buffer.data()[15U], m_layer3, 14U); memcpy(&Buffer.data()[15U], m_layer3, 14U);
memcpy(&Buffer.data()[29U], m_layer3, 14U); memcpy(&Buffer.data()[29U], m_layer3, 14U);
if (Buffer.data()[10U] == 0x81U || Buffer.data()[10U] == 0x83U) { if (Buffer.data()[10U] == 0x81U || Buffer.data()[10U] == 0x83U) {
Buffer.data()[9U] |= Buffer.data()[15U] == 0x01U ? 0x04U : 0x00U; Buffer.data()[9U] |= Buffer.data()[15U] == 0x01U ? 0x04U : 0x00U;
Buffer.data()[9U] |= Buffer.data()[15U] == 0x08U ? 0x08U : 0x00U; Buffer.data()[9U] |= Buffer.data()[15U] == 0x08U ? 0x08U : 0x00U;
@ -526,7 +526,7 @@ bool CNXDNProtocol::EncodeNXDNHeaderPacket(const CDvHeaderPacket &Header, CBuffe
Buffer.data()[9U] |= Buffer.data()[12U] == 0x08U ? 0x08U : 0x00U; Buffer.data()[9U] |= Buffer.data()[12U] == 0x08U ? 0x08U : 0x00U;
} }
} }
return true; return true;
} }
@ -535,15 +535,15 @@ bool CNXDNProtocol::EncodeNXDNPacket(const CDvHeaderPacket &Header, uint32_t seq
uint8_t ambe[28]; uint8_t ambe[28];
Buffer.resize(43); Buffer.resize(43);
uint16_t NXDNId = Header.GetMyCallsign().GetNXDNid(); uint16_t NXDNId = Header.GetMyCallsign().GetNXDNid();
uint16_t RptrId = NXDN_REFID; uint16_t RptrId = m_ReflectorId;
memcpy(Buffer.data(), "NXDND", 5); memcpy(Buffer.data(), "NXDND", 5);
Buffer.data()[5U] = (NXDNId >> 8) & 0xFFU; Buffer.data()[5U] = (NXDNId >> 8) & 0xFFU;
Buffer.data()[6U] = (NXDNId >> 0) & 0xFFU; Buffer.data()[6U] = (NXDNId >> 0) & 0xFFU;
Buffer.data()[7U] = (RptrId >> 8) & 0xFFU; Buffer.data()[7U] = (RptrId >> 8) & 0xFFU;
Buffer.data()[8U] = (RptrId >> 0) & 0xFFU; Buffer.data()[8U] = (RptrId >> 0) & 0xFFU;
Buffer.data()[9U] = 0x01U; Buffer.data()[9U] = 0x01U;
uint8_t msg[3U]; uint8_t msg[3U];
m_lich = 0; m_lich = 0;
memset(m_sacch, 0, 5U); memset(m_sacch, 0, 5U);
@ -590,7 +590,7 @@ bool CNXDNProtocol::EncodeNXDNPacket(const CDvHeaderPacket &Header, uint32_t seq
for(int i = 0; i < 4; ++i){ for(int i = 0; i < 4; ++i){
decode(DvFrames[i].GetCodecData(ECodecType::dmr), ambe+(i*7)); decode(DvFrames[i].GetCodecData(ECodecType::dmr), ambe+(i*7));
} }
memcpy(&Buffer.data()[15], ambe, 7); memcpy(&Buffer.data()[15], ambe, 7);
for(int i = 0; i < 7; ++i){ for(int i = 0; i < 7; ++i){
Buffer.data()[21+i] |= (ambe[7+i] >> 1); Buffer.data()[21+i] |= (ambe[7+i] >> 1);
@ -604,7 +604,7 @@ bool CNXDNProtocol::EncodeNXDNPacket(const CDvHeaderPacket &Header, uint32_t seq
Buffer.data()[36+i] = (ambe[21+i] & 1) << 7; Buffer.data()[36+i] = (ambe[21+i] & 1) << 7;
} }
Buffer.data()[41] |= (ambe[27] >> 2); Buffer.data()[41] |= (ambe[27] >> 2);
return true; return true;
} }
@ -657,11 +657,11 @@ void CNXDNProtocol::decode(const unsigned char* in, unsigned char* out) const
void CNXDNProtocol::encode(const unsigned char* in, unsigned char* out) const void CNXDNProtocol::encode(const unsigned char* in, unsigned char* out) const
{ {
unsigned int aOrig = 0U; unsigned int aOrig = 0U;
unsigned int bOrig = 0U; unsigned int bOrig = 0U;
unsigned int cOrig = 0U; unsigned int cOrig = 0U;
unsigned int MASK = 0x000800U; unsigned int MASK = 0x000800U;
for (unsigned int i = 0U; i < 12U; i++, MASK >>= 1) { for (unsigned int i = 0U; i < 12U; i++, MASK >>= 1) {
unsigned int n1 = i + 0U; unsigned int n1 = i + 0U;
@ -838,4 +838,3 @@ void CNXDNProtocol::encode_crc6(uint8_t *d, uint8_t len)
WRITE_BIT(d, n, b); WRITE_BIT(d, n, b);
} }
} }

@ -19,6 +19,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"
@ -80,10 +81,10 @@ protected:
// uiStreamId helpers // uiStreamId helpers
uint32_t IpToStreamId(const CIp &) const; uint32_t IpToStreamId(const CIp &) const;
void encode(const unsigned char*, unsigned char*) const; void encode(const unsigned char*, unsigned char*) const;
void decode(const unsigned char*, unsigned char*) const; void decode(const unsigned char*, unsigned char*) const;
uint8_t get_lich_fct(uint8_t); uint8_t get_lich_fct(uint8_t);
void set_lich_rfct(uint8_t); void set_lich_rfct(uint8_t);
void set_lich_fct(uint8_t); void set_lich_fct(uint8_t);
@ -121,4 +122,7 @@ protected:
uint8_t m_lich; uint8_t m_lich;
uint8_t m_sacch[5]; uint8_t m_sacch[5];
uint8_t m_layer3[22]; uint8_t m_layer3[22];
uint16_t m_ReflectorId;
char m_AutolinkModule;
}; };

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "Notification.h" #include "Notification.h"
CNotification::CNotification() CNotification::CNotification()

@ -32,12 +32,6 @@
class CNotificationQueue class CNotificationQueue
{ {
public: public:
// constructor
CNotificationQueue() {}
// destructor
~CNotificationQueue() {}
// lock // lock
void Lock() { m_Mutex.lock(); } void Lock() { m_Mutex.lock(); }
void Unlock() { m_Mutex.unlock(); } void Unlock() { m_Mutex.unlock(); }

@ -17,7 +17,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "P25Client.h" #include "P25Client.h"

@ -18,8 +18,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once #pragma once
#include "Defines.h"
#include "Client.h" #include "Client.h"
class CP25Client : public CClient class CP25Client : public CClient
{ {
public: public:

@ -17,13 +17,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h> #include <string.h>
#include "P25Client.h" #include "P25Client.h"
#include "P25Protocol.h" #include "P25Protocol.h"
#include "Reflector.h" #include "Global.h"
#include "GateKeeper.h"
const uint8_t REC62[] = {0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; const uint8_t REC62[] = {0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const uint8_t REC63[] = {0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U}; const uint8_t REC63[] = {0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U};
@ -50,6 +49,11 @@ const uint8_t REC80[] = {0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
bool CP25Protocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6) bool CP25Protocol::Initialize(const char *type, const EProtocol ptype, const uint16_t port, const bool has_ipv4, const bool has_ipv6)
{ {
// config data
m_ReflectorId = g_Configure.GetUnsigned(g_Keys.p25.reflectorid);
m_DefaultId = g_Configure.GetUnsigned(g_Keys.mmdvm.defaultid);
m_AutolinkModule = g_Configure.GetAutolinkModule(g_Keys.p25.autolinkmod);
m_uiStreamId = 0; m_uiStreamId = 0;
// base class // base class
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
@ -118,9 +122,8 @@ void CP25Protocol::Task(void)
auto newclient = std::make_shared<CP25Client>(Callsign, Ip); auto newclient = std::make_shared<CP25Client>(Callsign, Ip);
// aautolink, if enabled // aautolink, if enabled
#if P25_AUTOLINK_ENABLE if (m_AutolinkModule)
newclient->SetReflectorModule(P25_AUTOLINK_MODULE); newclient->SetReflectorModule(m_AutolinkModule);
#endif
// and append // and append
clients->AddClient(newclient); clients->AddClient(newclient);
@ -129,7 +132,7 @@ void CP25Protocol::Task(void)
{ {
client->Alive(); client->Alive();
} }
// acknowledge the request -- P25Reflector simply echoes the packet // acknowledge the request -- P25Reflector simply echoes the packet
Send(Buffer, Ip); Send(Buffer, Ip);
// and done // and done
@ -226,11 +229,10 @@ void CP25Protocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
void CP25Protocol::HandleQueue(void) void CP25Protocol::HandleQueue(void)
{ {
m_Queue.Lock(); while (! m_Queue.IsEmpty())
while ( !m_Queue.empty() )
{ {
// get the packet // get the packet
auto packet = m_Queue.pop(); auto packet = m_Queue.Pop();
// get our sender's id // get our sender's id
const auto module = packet->GetPacketModule(); const auto module = packet->GetPacketModule();
@ -273,7 +275,6 @@ void CP25Protocol::HandleQueue(void)
} }
} }
} }
m_Queue.Unlock();
} }
@ -308,7 +309,7 @@ bool CP25Protocol::IsValidDvPacket(const CIp &Ip, const CBuffer &Buffer, std::un
{ {
int offset = 0; int offset = 0;
bool last = false; bool last = false;
switch (Buffer.data()[0U]) { switch (Buffer.data()[0U]) {
case 0x62U: case 0x62U:
offset = 10U; offset = 10U;
@ -357,7 +358,7 @@ bool CP25Protocol::IsValidDvPacket(const CIp &Ip, const CBuffer &Buffer, std::un
default: default:
break; break;
} }
frame = std::unique_ptr<CDvFramePacket>(new CDvFramePacket(&(Buffer.data()[offset]), m_uiStreamId, last)); frame = std::unique_ptr<CDvFramePacket>(new CDvFramePacket(&(Buffer.data()[offset]), m_uiStreamId, last));
return true; return true;
} }
@ -387,19 +388,18 @@ bool CP25Protocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer, s
void CP25Protocol::EncodeP25Packet(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32_t iSeq, CBuffer &Buffer, bool islast) const void CP25Protocol::EncodeP25Packet(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32_t iSeq, CBuffer &Buffer, bool islast) const
{ {
uint32_t uiSrcId = Header.GetMyCallsign().GetDmrid(); uint32_t uiSrcId = Header.GetMyCallsign().GetDmrid();
uint32_t uiRptrId = P25_REFID;
if(uiSrcId == 0){ if(uiSrcId == 0){
uiSrcId = DMRMMDVM_DEFAULTID; uiSrcId = m_DefaultId;
} }
if(islast) if(islast)
{ {
Buffer.resize(17); Buffer.resize(17);
::memcpy(Buffer.data(), REC80, 17U); ::memcpy(Buffer.data(), REC80, 17U);
return; return;
} }
switch (iSeq % 18) { switch (iSeq % 18) {
case 0x00U: case 0x00U:
Buffer.resize(22); Buffer.resize(22);
@ -420,9 +420,9 @@ void CP25Protocol::EncodeP25Packet(const CDvHeaderPacket &Header, const CDvFrame
Buffer.resize(17); Buffer.resize(17);
::memcpy(Buffer.data(), REC65, 17U); ::memcpy(Buffer.data(), REC65, 17U);
::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U); ::memcpy(Buffer.data() + 5U, DvFrame.GetCodecData(ECodecType::p25), 11U);
Buffer.data()[1U] = (uiRptrId >> 16) & 0xFFU; Buffer.data()[1U] = (m_ReflectorId >> 16) & 0xFFU;
Buffer.data()[2U] = (uiRptrId >> 8) & 0xFFU; Buffer.data()[2U] = (m_ReflectorId >> 8) & 0xFFU;
Buffer.data()[3U] = (uiRptrId >> 0) & 0xFFU; Buffer.data()[3U] = (m_ReflectorId >> 0) & 0xFFU;
break; break;
case 0x04U: case 0x04U:
Buffer.resize(17); Buffer.resize(17);
@ -528,4 +528,3 @@ void CP25Protocol::HandleKeepalives(void)
} }
g_Reflector.ReleaseClients(); g_Reflector.ReleaseClients();
} }

@ -19,6 +19,7 @@
#pragma once #pragma once
#include "Defines.h"
#include "Timer.h" #include "Timer.h"
#include "Protocol.h" #include "Protocol.h"
#include "DVHeaderPacket.h" #include "DVHeaderPacket.h"
@ -53,7 +54,7 @@ protected:
// queue helper // queue helper
void HandleQueue(void); void HandleQueue(void);
void HandleKeepalives(void); void HandleKeepalives(void);
// stream helpers // stream helpers
void OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &, const CIp &); void OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &, const CIp &);
@ -72,4 +73,9 @@ protected:
// for queue header caches // for queue header caches
std::unordered_map<char, CP25StreamCacheItem> m_StreamsCache; std::unordered_map<char, CP25StreamCacheItem> m_StreamsCache;
uint32_t m_uiStreamId; uint32_t m_uiStreamId;
// config data
uint32_t m_ReflectorId;
uint32_t m_DefaultId;
char m_AutolinkModule;
}; };

@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h" #include <iostream>
#include "Packet.h" #include "Packet.h"
// default constructor // default constructor

@ -40,13 +40,8 @@ public:
CPacket(uint16_t sid, uint8_t dstarpid, uint8_t dmrpid, uint8_t dmrsubpid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysfsubpidmax, ECodecType, bool lastpacket); CPacket(uint16_t sid, uint8_t dstarpid, uint8_t dmrpid, uint8_t dmrsubpid, uint8_t ysfpid, uint8_t ysfsubpid, uint8_t ysfsubpidmax, ECodecType, bool lastpacket);
CPacket(const CM17Packet &); CPacket(const CM17Packet &);
// destructor
virtual ~CPacket() {}
// virtual duplication
virtual std::unique_ptr<CPacket> Duplicate(void) const = 0;
// identity // identity
virtual std::unique_ptr<CPacket> Copy(void) = 0;
virtual bool IsDvHeader(void) const = 0; virtual bool IsDvHeader(void) const = 0;
virtual bool IsDvFrame(void) const = 0; virtual bool IsDvFrame(void) const = 0;
bool IsLastPacket(void) const { return m_bLastPacket; } bool IsLastPacket(void) const { return m_bLastPacket; }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.