From f0f0fa326642deaec5ce8331935fc87c88c0de8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Pe=CC=81rez?= Date: Fri, 10 May 2024 23:04:24 -0400 Subject: [PATCH 1/6] .gitignore --- .gitignore | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) mode change 100755 => 100644 .gitignore diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index 192fd9b..83e7b68 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,29 @@ +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -88,7 +114,6 @@ ENV/ # Rope project settings .ropeproject -.DS_Store hblink.cfg *.config *.bak @@ -98,3 +123,18 @@ local_subscriber_ids.* peer_ids.* local_peer_ids.* talkgroup_ids.* + + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix From dbbaf85be42cbe03c0ba385779bbd660bb8e36df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Pe=CC=81rez?= Date: Sat, 11 May 2024 00:24:02 -0400 Subject: [PATCH 2/6] Changes in config files and default values to use with ADN Systems --- Audio/CW/adn.ambe | Bin 0 -> 2781 bytes Audio/CW/freedmr.ambe | Bin 2007 -> 0 bytes Audio/cy_GB/adn.ambe | Bin 0 -> 2304 bytes Audio/cy_GB/freedmr.ambe | Bin 603 -> 0 bytes Audio/de_DE/adn.ambe | Bin 0 -> 2304 bytes Audio/el_GR/adn.ambe | Bin 0 -> 2304 bytes Audio/el_GR/freedmr.ambe | Bin 900 -> 0 bytes Audio/en_GB/adn.ambe | Bin 0 -> 2304 bytes Audio/en_GB/freedmr.ambe | Bin 720 -> 0 bytes Audio/en_GB_2/adn.ambe | Bin 0 -> 2304 bytes Audio/en_GB_2/freedmr.ambe | Bin 720 -> 0 bytes Audio/es_ES/adn.ambe | Bin 0 -> 2304 bytes Audio/es_ES/freedmr.ambe | Bin 855 -> 0 bytes Audio/fr_FR/adn.ambe | Bin 0 -> 2304 bytes Audio/fr_FR/freedmr.ambe | Bin 504 -> 0 bytes Audio/pt_PT/adn.ambe | Bin 0 -> 2304 bytes Audio/pt_PT/freedmr.ambe | Bin 567 -> 0 bytes Audio/th_TH/adn.ambe | Bin 0 -> 2304 bytes Audio/th_TH/freedmr.ambe | Bin 594 -> 0 bytes README.md | 8 +- bridge.py | 2 +- bridge_master.py | 2 +- config.py | 20 +- FreeDMR-MINIMAL.cfg => config/ADN-MINIMAL.cfg | 4 +- .../ADN-SAMPLE-commented.cfg | 20 +- FreeDMR-SAMPLE.cfg => config/ADN-SAMPLE.cfg | 16 +- loro.cfg => config/loro.cfg | 0 playback_file.cfg => config/playback_file.cfg | 0 docker-configs/docker-compose_install.sh | 2 +- docker-configs/freedmr.cfg | 2 +- hblink-SAMPLE.cfg | 241 ------------------ playback_file.py | 2 +- 32 files changed, 40 insertions(+), 279 deletions(-) create mode 100644 Audio/CW/adn.ambe delete mode 100644 Audio/CW/freedmr.ambe create mode 100644 Audio/cy_GB/adn.ambe delete mode 100644 Audio/cy_GB/freedmr.ambe create mode 100644 Audio/de_DE/adn.ambe create mode 100644 Audio/el_GR/adn.ambe delete mode 100644 Audio/el_GR/freedmr.ambe create mode 100644 Audio/en_GB/adn.ambe delete mode 100644 Audio/en_GB/freedmr.ambe create mode 100644 Audio/en_GB_2/adn.ambe delete mode 100644 Audio/en_GB_2/freedmr.ambe create mode 100644 Audio/es_ES/adn.ambe delete mode 100644 Audio/es_ES/freedmr.ambe create mode 100644 Audio/fr_FR/adn.ambe delete mode 100644 Audio/fr_FR/freedmr.ambe create mode 100644 Audio/pt_PT/adn.ambe delete mode 100644 Audio/pt_PT/freedmr.ambe create mode 100644 Audio/th_TH/adn.ambe delete mode 100644 Audio/th_TH/freedmr.ambe rename FreeDMR-MINIMAL.cfg => config/ADN-MINIMAL.cfg (94%) rename FreeDMR-SAMPLE-commented.cfg => config/ADN-SAMPLE-commented.cfg (94%) rename FreeDMR-SAMPLE.cfg => config/ADN-SAMPLE.cfg (80%) rename loro.cfg => config/loro.cfg (100%) rename playback_file.cfg => config/playback_file.cfg (100%) delete mode 100755 hblink-SAMPLE.cfg diff --git a/Audio/CW/adn.ambe b/Audio/CW/adn.ambe new file mode 100644 index 0000000000000000000000000000000000000000..b7f6fd19f7dc5c3abefb33912215e38376632bf8 GIT binary patch literal 2781 zcmYk8c~n!^7RD2TMCen=2((&3rfTha1{un;E(w_s2S`w$QD!8H%3uQ50Y$hZ(HBG` zLLOQN5<*mjh>?dwF^FPZfLN+XMGzd4R6waREk)#=djs_ScUE@JI(L8P`+oZrtuck- z>@VXX$tF{#jn4^Jug}muN<^^3KevB*4To{<7oU)|jFzSgdBNqC{rJfe2X>sJX5n15 zwL@C$lkH2FsxcH{+SvCi=S$gXfmr3{`~NES4rEQ-OBnx3+VOPm$huG&!q_+P*9TNqyqRO()rN4PQ<_I#`NbnqJS8+qVgG|>Z?}0_a_O{m6jvQ_n zIZ|_>yAdoH+vGiC|85j886k(~=<*r^H?lH#4{~eFY+=O4#2a#r!8?MECm)fV2Wos9 zO*kI5?*0na`#_lv?R}=fLVmyC^p0L8(lS`QKxKid_P-2p(*^fK1v}~&VQbcdQtIsh z&tt(dC~x?&#=9v<`{kDSE+h>4_?p+(c06WO|J?Zcln)HdvvwK7@^O$;_-L)Q~{x}LGQcE_n4D> zt%pNNPA@e+?z3t%qi5Vp7kOtEWtBHn7e>XoxfjUmJYX01T^)|MpDr$?Ge|uO=>Cs6 zShn-qgRVtCJo>=&ZYk|niW?al*cpsLoy0)*VQ?by?OuufJVF(6v)u>eKTXwaGmzqZr6lfu_clTv%gUIpC-ydGc-mjZGbPu78VjNcaM)TzdAdxc4|!=7 z4pbEsWYsSsSX8YK@H-w{0i6XpqrX(8-mw_+bc)5RP<|HT)nJ)GVHkVi`z}&D8R~i1 zUuZqC1UG2Pg#u_>uMS?(*N-Yk`RlW%JTKE-RK4`0DVnr;G$}y&{_Fg<3E$B77Y(0o zgZo-OFsH29R)vqne7?85$uWl!=y}8}i0PJMx42ZU5hdf)uEb@*-yGS4k>Z5ji&y-y z$5k4)fwpPxjlQo`Zan9n4sM=R$Zz?KY~ zoNi0$xXj17Ii{NiW+pGDz;X0sBRkW2^XHVJ?FJSPUj)VfxfqU1a2_lQn?GNgX2Y~P z@%`NSVje4uFqDmE8MwDFHl}aWtag@6btu>34XwgF<*1S;Z_mUf)|EYbZcvKCa6zSS z6^Q2-0_D+NkJ+bmCAo+XJAoQwVAnvTc3kLt!JkO3OU5_7IC+_l8E`5>db)t@vgYgLMb!zeV2nyFszyvUI}%N$JX*iA9)p$J$ujjRz@mT~nTSRGSeK zZ|TB<6i|XBW9?Fh=AgmxFb71#H_6m2NmmLBH=CBFvYfRxCjJ0IY%7qIsj~$7od}4g zTSgf*g>EQ+^9yH8;oN@cZ+w9g@)>|p?I@!Lg+3RYcEwgEQ%A3!EJ;`-!Tf6XyOc~0 ztSP?j|3X8hx|J1a>hL)(zV;`wKRmOA)7`8;=TdsvMp~wGSL=t~2;*`Q`O>Fy)KP$J z%D||pEMGs@Kp+lvP8=;W0_8si1LeI@<+tK57zUnsWKHq~xVOjv zlz%t7?pc`c;cp0-$|ZzW;9edf2>xO@D+c%XE;P%`tQe4z`B`Vg58bs;aE86$x6=v5Yi5s;67=om5`5ap38m{xvfj6yRd zgxC&EL4$ogx|-iam<$ga$-dVc+NU!R8J|7J9PToRC)zR+&*^spnp{f>j8Oto zl9-=>sW(L%l67UWLpiBn1$Gx>O%_U5FT}EwW#O&0Bt58m(n0-|FY(mc{fH#Hvd?;PKivSF@8vP`p!T1;fHxLoyGH= zbw^P~z4`xLrtE`zMLhADJrd;2w%`Cxh0d>GK!A_*1`4N^PUv9&q%^FI&lT+Y=-Ksj zIV&S|)NUwm`Lb~F9eI^IEb@v_%&Ts%FmMEM*{vxf7maKYHR`ZquGp{(FAV2Q=!%VA tY&ykH);bFN5Q($R4<;K+)6<=I+_~{(6i(kj{PEQnB^QpSk{_nu`X3U-O*;Sp literal 0 HcmV?d00001 diff --git a/Audio/CW/freedmr.ambe b/Audio/CW/freedmr.ambe deleted file mode 100644 index e56b8d6dcd5f6123aa6e3dfd3dabf1fdf6be6086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2007 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 aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/cy_GB/freedmr.ambe b/Audio/cy_GB/freedmr.ambe deleted file mode 100644 index 41d027171df69cf082f5534b683a4a3dea504a48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 zcmV-h0;K()ljk%1fYm1V>*J;u6|4{6LBBw@phZH>jc*e5m=7d!2ZBG{=Av;@Q8>fv z*(ss}XEnHkCr4n7cj{d?80o%y!C~!uKo0iH_QyAnsg=@=udt*S@-A2V@2;|@SNAIf z_?r3WjawP8~-vg7Xb(dI-=N zKnuT}s-t zeE}o4jgBg!N*ZAlfKOaeqiqdlFyiA~I)51`B9Kju$}|TE4IqKnMzG_QNH_>f??JXk z1vw8(y!aVfu@^WxfHo7HM!J+e=UR+4lEDK_e&PC6>&jpv;dcj4;3&0Nwp%C zzG@Y7CL6>gm9mdT@8=w@d?G9MQD%y4lTulg6CA9Y9=0ZP<8}=&qb!#sbK?>nA)!9B z5b3ZzzBj@jbT$%M>%SM6RcAc*) zZrfz^jAfkN8E&6pH2Y-GeByqdRG7YNO=8Sy&K(xBt;~-~9nUeF%p-|6MIQeU`HlnK pHfQH`eI$g*=}4Z}AZ#e)pcs@)O5Y0mu;e54MJ_DmQdrBY#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/el_GR/adn.ambe b/Audio/el_GR/adn.ambe new file mode 100644 index 0000000000000000000000000000000000000000..fea8c880c9b9a765e22b8bbc1e683070885996b2 GIT binary patch literal 2304 zcmV+b3IFz$OXg4m5U>Y#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/el_GR/freedmr.ambe b/Audio/el_GR/freedmr.ambe deleted file mode 100644 index 828312b26c1f9924b68f15eaca13630e06d31ca0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 900 zcmV-~1AF}b=YtkyWgUe7q~y{}M%R~@mH*{|CsIHkle_G7=L7*SMGijSqJUgxY1uAK zwAg|pU`&adq_yjSQ3h_zzq66y7 zDnUWg1(d`bb{IfX=X8LVdPE*!b}ISYK#N8kV@Fuc$NK!7>XCVqk6!SqR37B0%Ch6Y6FIITK;RFJPD8hGZ)hsgE=>*V-sWX4Aco`_9PHTnVf*7cg6-%M3{)sg?GjWQbdTD z(3DriCvygv$kByH!(OYnFf-)2--MZ)8`i1HM diff --git a/Audio/en_GB/adn.ambe b/Audio/en_GB/adn.ambe new file mode 100644 index 0000000000000000000000000000000000000000..fea8c880c9b9a765e22b8bbc1e683070885996b2 GIT binary patch literal 2304 zcmV+b3IFz$OXg4m5U>Y#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/en_GB/freedmr.ambe b/Audio/en_GB/freedmr.ambe deleted file mode 100644 index d6d3f22844a4079b4f71610f0e75e4312fc976d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 720 zcmV;>0x$iQOXg4m5U>Y#w~vBfGet=QCJv0j5F&qReaH^I;dVoOOb@F)t>QFeDSb)n z^*W$65(q+9?v&Y~Cj|)o_=U^~y*uo-+tiatY{wL-Y`7hrx9#F`v|MJ;Ny3VN7WSHx zzAv)**gtW~)D|jn?m7}N0lo3ePY%B`S&Jo>8!fsCW z7O|Iqd|E=(kdid{(|v1U#D}Ff%J*y`Cpe>DB}Xqdj9@>qS9t+k+mEI!2Gpl;1VAr6*)bD#b;3u$sBOHAeE0h6N4<;48iCf2JV`wP!k7q2h zC1h;M1>;3OY6QAFJqW{1YAJ8w(U*w9MNTOT^kxbL)esCkl@K=+1IIWV)rfy2SolWq z$)>j(0ioz5gIk0F=~WPu5EB(a8D4ZwseA(E$Ai@p~`eA1ejKxuird zlMYM`C^@tfPxu1<30{MTMQ_qa(cFb{@Kh{}bWDp7(vc*fLNNY#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/en_GB_2/freedmr.ambe b/Audio/en_GB_2/freedmr.ambe deleted file mode 100644 index d6d3f22844a4079b4f71610f0e75e4312fc976d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 720 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 aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/es_ES/freedmr.ambe b/Audio/es_ES/freedmr.ambe deleted file mode 100644 index be242e9a37df92a1c05c5f4f9bf35db9884bb472..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 855 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 aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/fr_FR/freedmr.ambe b/Audio/fr_FR/freedmr.ambe deleted file mode 100644 index 41bff87cdf041da4905c60c4edbcc86555f563be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 504 zcmV& zy^w}?^=okJ{jIQuH_-?ut;^5j;5V~!klqoPw}Lm(&=b^WxU`~GmgGm5K}-TVsqo~H z$NNo0DxtE@Q7CH^*&Ng80eS`A7u7|R%}5A|Z8nm)5R7gW_p?=_V1j(T}&Gca^x-gNLe+<)P u#WQjs$?K-o!;frccBrQn+H&Y^cTgGZJNql5B4Y%Lhd28Qf-pw}xW^+_OYrpo diff --git a/Audio/pt_PT/adn.ambe b/Audio/pt_PT/adn.ambe new file mode 100644 index 0000000000000000000000000000000000000000..fea8c880c9b9a765e22b8bbc1e683070885996b2 GIT binary patch literal 2304 zcmV+b3IFz$OXg4m5U>Y#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/pt_PT/freedmr.ambe b/Audio/pt_PT/freedmr.ambe deleted file mode 100644 index cf1601e6756cbed0bf9cab5277041c0beea93c62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 567 zcmV-70?7U30|?Z~AO%Ewm1j)WQNa24R74Y#q{hdpm%i;Jcp~~OqenQq|8ya zR{@CTM)Or!)1F-6VMO&?{t=&V;&x%64DNE=9DV)+<=9}2-wth!mCXmQ>^x7cN8^bitBNB| zy9f30-EaLbY>E(t&12c^EN#s<#F%I6&JJ$PWq`R6y0$Osx^dO@P86+f<5D5F9{yGO zapF-`u?292frjsRW};1sLHlFEaW>^c0kfY><3T2rZJ7tGP2ooYpbhUq+EU|Hb>c%* z!M{x619QVU-zTPe%^gL+&d>VuErnqQ!6a*Qf?UF3M~I~f-I}rE5p{;0h>MGpDA`in z$Pt*>(S}nL_z3TJfk=$B;R27P!?`o2b8(r47Fno*;S)6W-0PjIk>Pa{qurg_sUn0V zR{oPGdW#{ZB2fJC0Bg7~hGjzd!8Q$x0E96`?tm35?H{IAQQ7^?iT^K$CwGjSso$?m z;Q>~I9g8=#lE45Gs}y_&$MdBCR*=wN@5|zUxe?@jF+|zIPv29e3}z9^Q{px?!4(j; z`#|G#Qh_yg#rRUdLMNpp0J5xZ;UZMkP0azlPQX_nmmIG*qW`BgcJavZ8lvxqF%*T| zqifq^zz1gXMHJD|pBRrcrCBJ{z?}>DV3rtq^XPEmWhC(sGnA9=h8AV_$&n3$-=;JK z@tO5{*w=}X%D?id2c3W%x%}M5iPm+PMq!@74zSU4pFmdVS_|tqaE?QF$X_4MJ7Awd zRoFf~>RB_0BpsQ0X=5^T+hRSLVEX~SToOD6#r%Kiwj38YZ?6)M`_)ZG9Z8dytso~j zbxsz;3yEE@0D6vXmMU1sl$37mPzB!fQsxWwSVbNstAc zh3aw|JAyJNop;)`ua1<-4I4k#>8jERHv30Z7z%UxDszFt=xdXco7X9YSjQND!Vtz$JKpgCpW@zrK`+-a3mLBh?`0bYHDagPF$POUm6%7fPKIH zW->e+09-3PGz0TqU2ar))$5M;IVdkxUv>!d<|te%)m||~F)JBN3H5&wRRcahk}Bd! zN1zpv4xtF5OjUyggZOa8zh}SODb3|!fm13$9X~^q0BxxyoheM$hBHs;L%j`r_w`}G zV<6kk#SDO5;uR#Flv}E!P2g8znZ-@o>O&ZtQH?RHy&~ z5>2l$(bVh=+hF5iCxk^2!`pJ;M*{UQWv9c8IoeT_7$}3%j|u!&)k%7#DQ7Fo&E2JS z#|m{ii|x7bV8>}<@(hP+o0mB4%7Uz@S$R9f767Unfn4{THfksRSrRdwxoq=>2B%WOP`C#C85wHYw zmgrO9S5?CRaM6Iyr#2(tiNPzG;fD91&AO>ad|Tt9&eplTt3Bf~$V zD4{PUqEwYh>4PR>yx0QROX;9MMcueykN*v%MPj+!n(eH7!DV5jY>Fq_L*rIqvlVr> z+Hv6&LXcAyj^qlkgy zS8(v?i@B+@<5N(hpN{_fV&P+U)j?I4`$OS#C4(d|^XO6GR|eq|27$^!<1`Y4H3PBR zaKca!lr$2hh@-;|C)2S3|Yc(xr3-)|GF|qTYuzCWgz$ zYJkm!6a~SIkq)BYh6NPD$)hQ%0EAOB{l*7t+d#r#cJO5-lIcLfU^T-KbH&+F;$~8@ z76-AIGo%Do&cRSSnuFsrHKpCIn5(kDQ8cve+y1U>;Q(de9JxW3!_~lzWMxko4QSKF aY5a@dyU_*Q1h_&8Jq-LZ)3b3+7|)`XgJtvp literal 0 HcmV?d00001 diff --git a/Audio/th_TH/freedmr.ambe b/Audio/th_TH/freedmr.ambe deleted file mode 100644 index 53b634c9aff00eb32d78fded0f4af47733adab11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 594 zcmV-Y0Y#q{hdpm%i;Jcq0ItL8&sQCO=# z#2ci^R&m-drgcWk$O|MjTuraBr9|cnK)C5`)eZH!Xx)DCc;Gsb_`v;hTR5)@0Ng#R z5Zx+{OJRfd8jR;HsBa^qA70TIZ=E1;@w;>6xqiJtR;Uo9T83Y~Ky;cSq-)oGyZ~3E z>sQvVKm9~g!iC_F09l?mvtg0Xot&->ZW%h z1Y4)+tGd%NU?qHK9_H(UaZ0sBZ}_Y0(Q9siQstBw*QPTt3^NcWr1tNG)tAciuV*~(7e5j)*NUQ+;c=MJA zzJ1Lm@eWjUSoKii7W+NwWU#kOAJP<2qE}%mJtO{aQ&u`WjFL*$c z*4)C?2Plw0{5+?-_a`cWK$mr8PNpXC_=jhra7)BCKlrQf@o7({ zWb*L~_x~Ub_YzvvPp`@~Ig=H~(M3gSiWfJ9X8?&ce~$q**6t96$i@d{CJf9tjV8B5 z14b&BjLDsqMj=Kywv33Jl|~>&Ij4-NorZQG1Utsq%gfPaWal475Jg4eRA<9H7ZgQ9 z@gV{hC5oIag_A%-ym8v(B?iVN3(--YSjC9;d}*RV3(#gdlWz))bG5~x?WB4xpLZRV ghy#MR4vix}K#z~}$D% Date: Sat, 11 May 2024 00:24:54 -0400 Subject: [PATCH 3/6] Added hotspot proxy from CS8ABG dashboard to use with Self Service. --- hotspot_proxy_self_service.py | 684 ++++++++++++++++++++++++++++++++++ hotspot_proxy_v2.py | 10 +- proxy_db.py | 147 ++++++++ 3 files changed, 836 insertions(+), 5 deletions(-) create mode 100644 hotspot_proxy_self_service.py create mode 100644 proxy_db.py diff --git a/hotspot_proxy_self_service.py b/hotspot_proxy_self_service.py new file mode 100644 index 0000000..86d971d --- /dev/null +++ b/hotspot_proxy_self_service.py @@ -0,0 +1,684 @@ +#!/usr/bin/env python3 +############################################################################### +# Copyright (C) 2020 Simon Adlem, G7RZU +# +# 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, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +############################################################################### +from hashlib import pbkdf2_hmac +import random +import ipaddress +import os +from datetime import datetime +from time import time + +from twisted.internet.protocol import DatagramProtocol +from twisted.internet.defer import inlineCallbacks +from twisted.internet import reactor, task +from setproctitle import setproctitle +from dmr_utils3.utils import int_id +import Pyro5.api +from proxy_db import ProxyDB + +# Does anybody read this stuff? There's a PEP somewhere that says I should do this. +__author__ = "Simon Adlem - G7RZU" +__verion__ = "23.10.14" +__copyright__ = "Copyright (c) Simon Adlem, G7RZU 2020,2021,2022,2023" +__credits__ = "Jon Lee, G4TSN; Norman Williams, M6NBP; Christian, OA4DOA" +__license__ = "GNU GPLv3" +__maintainer__ = "Simon Adlem G7RZU" +__email__ = "simon@gb7fr.org.uk" + + +def IsIPv4Address(ip): + try: + ipaddress.IPv4Address(ip) + return True + except ValueError as errorCode: + pass + return False + + +def IsIPv6Address(ip): + try: + ipaddress.IPv6Address(ip) + return True + except ValueError as errorCode: + pass + + +class privHelper: + def __init__(self): + self._netfilterURI = ( + "PYRO:netfilterControl@./u:/run/priv_control/priv_control.unixsocket" + ) + self._conntrackURI = ( + "PYRO:conntrackControl@./u:/run/priv_control/priv_control.unixsocket" + ) + + def addBL(self, dport, ip): + try: + with Pyro5.api.Proxy(self._netfilterURI) as nf: + nf.blocklistAdd(dport, ip) + except Exception as e: + print("(PrivError) {}".format(e)) + + def delBL(self, dport, ip): + try: + with Pyro5.api.Proxy(self._netfilterURI) as nf: + nf.blocklistDel(dport, ip) + except Exception as e: + print("(PrivError) {}".format(e)) + + def blocklistFlush(self): + try: + with Pyro5.api.Proxy(self._netfilterURI) as nf: + nf.blocklistFlush() + except Exception as e: + print("(PrivError) {}".format(e)) + + def flushCT(self): + try: + with Pyro5.api.Proxy(self._conntrackURI) as ct: + ct.flushUDPTarget(62031) + except Exception as e: + print("(PrivError) {}".format(e)) + + +class Proxy(DatagramProtocol): + + def __init__( + self, + Master, + ListenPort, + connTrack, + peerTrack, + blackList, + IPBlackList, + Timeout, + Debug, + ClientInfo, + DestportStart, + DestPortEnd, + privHelper, + rptlTrack, + db_proxy, + selfservice, + ): + self.master = Master + self.ListenPort = ListenPort + self.connTrack = connTrack + self.peerTrack = peerTrack + self.timeout = Timeout + self.debug = Debug + self.clientinfo = ClientInfo + self.blackList = blackList + self.IPBlackList = IPBlackList + self.destPortStart = DestportStart + self.destPortEnd = DestPortEnd + self.numPorts = DestPortEnd - DestportStart + self.privHelper = privHelper + self.rptlTrack = rptlTrack + self.db_proxy = db_proxy + self.selfserv = selfservice + + def reaper(self, _peer_id): + if self.debug: + print("dead", _peer_id) + if self.clientinfo and _peer_id != b"\xff\xff\xff\xff": + print( + f"{datetime.now().replace(microsecond=0)} Client: ID:{str(int_id(_peer_id)).rjust(9)} IP:{self.peerTrack[_peer_id]['shost'].rjust(15)} Port:{self.peerTrack[_peer_id]['sport']} Removed." + ) + self.transport.write( + b"RPTCL" + _peer_id, (self.master, self.peerTrack[_peer_id]["dport"]) + ) + # Tell client we have closed the session - 3 times, in case they are on a lossy network + self.transport.write( + b"MSTCL", + (self.peerTrack[_peer_id]["shost"], self.peerTrack[_peer_id]["sport"]), + ) + self.transport.write( + b"MSTCL", + (self.peerTrack[_peer_id]["shost"], self.peerTrack[_peer_id]["sport"]), + ) + self.transport.write( + b"MSTCL", + (self.peerTrack[_peer_id]["shost"], self.peerTrack[_peer_id]["sport"]), + ) + self.connTrack[self.peerTrack[_peer_id]["dport"]] = False + if self.selfserv: + self.db_proxy.updt_tbl("log_out", _peer_id) + del self.peerTrack[_peer_id] + + def datagramReceived(self, data, addr): + + # HomeBrew Protocol Commands + DMRD = b"DMRD" + DMRA = b"DMRA" + MSTCL = b"MSTCL" + MSTNAK = b"MSTNAK" + MSTPONG = b"MSTPONG" + MSTN = b"MSTN" + MSTP = b"MSTP" + MSTC = b"MSTC" + RPTL = b"RPTL" + RPTPING = b"RPTPING" + RPTCL = b"RPTCL" + RPTL = b"RPTL" + RPTACK = b"RPTACK" + RPTK = b"RPTK" + RPTC = b"RPTC" + RPTP = b"RPTP" + RPTA = b"RPTA" + RPTO = b"RPTO" + + # Proxy control commands + PRBL = b"PRBL" + + # Proxy info commands + PRIN = b"PRIN" + + _peer_id = False + + host, port = addr + + nowtime = time() + + Debug = self.debug + + if host in self.IPBlackList: + return + + # If the packet comes from the master + if host == self.master: + _command = data[:4] + + if _command == PRBL: + _peer_id = data[4:8] + _bltime = data[8:].decode("UTF-8") + _bltime = float(_bltime) + try: + self.IPBlackList[self.peerTrack[_peer_id]["shost"]] = _bltime + except KeyError: + return + if self.clientinfo: + print( + "Add to blacklist: host {}. Expire time {}".format( + self.peerTrack[_peer_id]["shost"], _bltime + ) + ) + if self.privHelper: + print( + "Ask priv_helper to add to iptables: host {}, port {}.".format( + self.peerTrack[_peer_id]["shost"], self.ListenPort + ) + ) + reactor.callInThread( + self.privHelper.addBL, + self.ListenPort, + self.peerTrack[_peer_id]["shost"], + ) + return + + if _command == DMRD: + _peer_id = data[11:15] + elif _command == RPTA: + if data[6:10] in self.peerTrack: + _peer_id = data[6:10] + else: + _peer_id = self.connTrack[port] + elif _command == MSTN: + _peer_id = data[6:10] + elif _command == MSTP: + _peer_id = data[7:11] + elif _command == MSTC: + _peer_id = data[5:9] + + if self.debug: + print(data) + if _peer_id in self.peerTrack: + self.transport.write( + data, + ( + self.peerTrack[_peer_id]["shost"], + self.peerTrack[_peer_id]["sport"], + ), + ) + # Remove the client after send a MSTN or MSTC packet + if _command in (MSTN, MSTC): + # Give time to the client for a reply to prevent port reassignment + self.peerTrack[_peer_id]["timer"].reset(15) + + return + + else: + _command = data[:4] + + if _command == DMRD: # DMRData -- encapsulated DMR data frame + _peer_id = data[11:15] + elif _command == DMRA: # DMRAlias -- Talker Alias information + _peer_id = data[4:8] + elif _command == RPTL: # RPTLogin -- a repeater wants to login + _peer_id = data[4:8] + + # if we have seen more than 50 RPTL packets from this IP since the RPTL tracking table was reset (every 60 secs) + # blacklist IP for 10 minutes + if host not in self.rptlTrack: + self.rptlTrack[host] = 1 + else: + self.rptlTrack[host] += 1 + + if self.rptlTrack[host] > 50: + print("(RPTL) exceeded max: {}".format(self.rptlTrack[host])) + _bltime = nowtime + 600 + self.IPBlackList[host] = _bltime + self.rptlTrack.pop(host) + + if self.clientinfo: + print( + "(RPTL) Add to blacklist: host {}. Expire time {}".format( + host, _bltime + ) + ) + if self.privHelper: + print( + "(RPTL) Ask priv_helper to add to iptables: host {}, port {}.".format( + host, self.ListenPort + ) + ) + reactor.callInThread( + self.privHelper.addBL, self.ListenPort, host + ) + return + + elif _command == RPTK: # Repeater has answered our login challenge + _peer_id = data[4:8] + elif ( + _command == RPTC + ): # Repeater is sending it's configuraiton OR disconnecting + if data[:5] == RPTCL: # Disconnect command + _peer_id = data[5:9] + else: + _peer_id = data[4:8] # Configure Command + if self.selfserv and _peer_id in self.peerTrack: + mode = data[97:98].decode() + callsign = data[8:16].rstrip().decode() + self.db_proxy.ins_conf( + int_id(_peer_id), _peer_id, callsign, addr[0], mode + ) + # Self Service options will be send 10 sec. after login + self.peerTrack[_peer_id]["opt_timer"] = reactor.callLater( + 10, self.login_opt, _peer_id + ) + + elif _command == RPTO: # options + _peer_id = data[4:8] + if self.selfserv and _peer_id in self.peerTrack: + # Store Self Service password in database + if data[8:].upper().startswith(b"PASS="): + _psswd = data[13:] + if len(_psswd) >= 6: + dk = pbkdf2_hmac("sha256", _psswd, b"FreeDMR", 2000).hex() + self.db_proxy.updt_tbl("psswd", _peer_id, psswd=dk) + self.transport.write(b"".join([RPTACK, _peer_id]), addr) + print(f"Password stored for: {int_id(_peer_id)}") + return + self.db_proxy.updt_tbl("opt_rcvd", _peer_id) + # Options send by peer overrides Self Service options + if self.peerTrack[_peer_id]["opt_timer"].active(): + self.peerTrack[_peer_id]["opt_timer"].cancel() + print(f"Options received from: {int_id(_peer_id)}") + + elif _command == RPTP: # RPTPing -- peer is pinging us + _peer_id = data[7:11] + else: + return + + if _peer_id in self.peerTrack: + _dport = self.peerTrack[_peer_id]["dport"] + self.peerTrack[_peer_id]["sport"] = port + self.peerTrack[_peer_id]["shost"] = host + self.transport.write(data, (self.master, _dport)) + self.peerTrack[_peer_id]["timer"].reset(self.timeout) + if self.debug: + print(data) + return + + else: + if int_id(_peer_id) in self.blackList: + return + # Make a list with the available ports + _ports_avail = [ + port for port in self.connTrack if not self.connTrack[port] + ] + if _ports_avail: + _dport = random.choice(_ports_avail) + else: + return + self.connTrack[_dport] = _peer_id + self.peerTrack[_peer_id] = {} + self.peerTrack[_peer_id]["dport"] = _dport + self.peerTrack[_peer_id]["sport"] = port + self.peerTrack[_peer_id]["shost"] = host + self.peerTrack[_peer_id]["timer"] = reactor.callLater( + self.timeout, self.reaper, _peer_id + ) + self.transport.write(data, (self.master, _dport)) + pripacket = b"".join( + [b"PRIN", host.encode("UTF-8"), b":", str(port).encode("UTF-8")] + ) + # Send IP and Port info to server + self.transport.write(pripacket, (self.master, _dport)) + + if self.clientinfo and _peer_id != b"\xff\xff\xff\xff": + print( + f"{datetime.now().replace(microsecond=0)} New client: ID:{str(int_id(_peer_id)).rjust(9)} IP:{host.rjust(15)} Port:{port}, assigned to port:{_dport}." + ) + if self.debug: + print(data) + return + + @inlineCallbacks + def login_opt(self, _peer_id): + try: + res = yield db_proxy.slct_opt(_peer_id) + options = res[0][0] + if options: + bytes_pkt = b"".join((b"RPTO", _peer_id, options.encode())) + self.transport.write( + bytes_pkt, (self.master, self.peerTrack[_peer_id]["dport"]) + ) + print(f"Options sent at login for: {int_id(_peer_id)}, opt: {options}") + + except Exception as err: + print(f"login_opt error: {err}") + + @inlineCallbacks + def send_opts(self): + try: + results = yield db_proxy.slct_db() + for item in results: + _peer_id, options = item + if _peer_id not in self.peerTrack or not options: + continue + self.db_proxy.updt_tbl("rst_mod", _peer_id) + bytes_pkt = b"".join((b"RPTO", _peer_id, options.encode())) + self.transport.write( + bytes_pkt, (self.master, self.peerTrack[_peer_id]["dport"]) + ) + print(f"Options update sent for: {int_id(_peer_id)}") + + except Exception as err: + print(f"send_opts error: {err}") + + def lst_seen(self): + # Update last seen + dmrid_list = [(ite,) for ite in self.peerTrack] + if dmrid_list: + self.db_proxy.updt_lstseen(dmrid_list) + + +if __name__ == "__main__": + + import signal + import configparser + import argparse + import sys + import json + import stat + import functools + + print = functools.partial(print, flush=True) + + # Set process title early + setproctitle(__file__) + + # Change the current directory to the location of the application + os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) + + # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message + parser = argparse.ArgumentParser() + parser.add_argument( + "-c", + "--config", + action="store", + dest="CONFIG_FILE", + help="/full/path/to/config.file (usually freedmr.cfg)", + ) + cli_args = parser.parse_args() + + # Ensure we have a path for the config file, if one wasn't specified, then use the execution directory + if not cli_args.CONFIG_FILE: + cli_args.CONFIG_FILE = ( + os.path.dirname(os.path.abspath(__file__)) + "/config/adn.cfg" + ) + + _config_file = cli_args.CONFIG_FILE + + config = configparser.ConfigParser() + + if not config.read(_config_file): + print( + "Configuration file '" + + _config_file + + "' is not a valid configuration file!" + ) + + try: + + Master = config.get("PROXY", "Master") + ListenPort = config.getint("PROXY", "ListenPort") + ListenIP = config.get("PROXY", "ListenIP") + DestportStart = config.getint("PROXY", "DestportStart") + DestPortEnd = config.getint("PROXY", "DestPortEnd") + Timeout = config.getint("PROXY", "Timeout") + Stats = config.getboolean("PROXY", "Stats") + Debug = config.getboolean("PROXY", "Debug") + ClientInfo = config.getboolean("PROXY", "ClientInfo") + BlackList = json.loads(config.get("PROXY", "BlackList")) + IPBlackList = json.loads(config.get("PROXY", "IPBlackList")) + # Self Service + use_selfservice = config.getboolean("SELF SERVICE", "use_selfservice") + db_server = config.get("SELF SERVICE", "server") + db_username = config.get("SELF SERVICE", "username") + db_password = config.get("SELF SERVICE", "password") + db_name = config.get("SELF SERVICE", "db_name") + db_port = config.getint("SELF SERVICE", "port") + + except configparser.Error as err: + print("Error processing configuration file -- {}".format(err)) + + print("Using default config") + # *** CONFIG HERE *** + + Master = "127.0.0.1" + ListenPort = 62031 + #'' = all IPv4, '::' = all IPv4 and IPv6 (Dual Stack) + ListenIP = "" + DestportStart = 56400 + DestPortEnd = 56500 + Timeout = 30 + Stats = False + Debug = False + ClientInfo = False + BlackList = [1234567] + # e.g. {10.0.0.1: 0, 10.0.0.2: 0} + IPBlackList = {} + + # Self Service database configuration + use_selfservice = True + db_server = "localhost" + db_username = "root" + db_password = "" + db_name = "test" + db_port = 3306 + + # ******************* + + CONNTRACK = {} + PEERTRACK = {} + RPTLTRACK = {} + PRIV_HELPER = None + + # Set up the signal handler + def sig_handler(_signal, _frame): + print( + "(GLOBAL) SHUTDOWN: PROXY IS TERMINATING WITH SIGNAL {}".format( + str(_signal) + ) + ) + reactor.stop() + + # Install signal handlers + signal.signal(signal.SIGTERM, sig_handler) + signal.signal(signal.SIGINT, sig_handler) + + # readState() + + # If IPv6 is enabled by enivornment variable... + if ( + ListenIP == "" + and "FDPROXY_IPV6" in os.environ + and bool(os.environ["FDPROXY_IPV6"]) + ): + ListenIP = "::" + + # Override static config from Environment + if "FDPROXY_STATS" in os.environ: + Stats = bool(os.environ["FDPROXY_STATS"]) + # if 'FDPROXY_DEBUG' in os.environ: + # Debug = bool(os.environ['FDPROXY_DEBUG']) + if "FDPROXY_CLIENTINFO" in os.environ: + ClientInfo = bool(os.environ["FDPROXY_CLIENTINFO"]) + if "FDPROXY_LISTENPORT" in os.environ: + ListenPort = int(os.environ["FDPROXY_LISTENPORT"]) + + unixSocket = "/run/priv_control/priv_control.unixsocket" + + if os.path.exists(unixSocket) and stat.S_ISSOCK(os.stat(unixSocket).st_mode): + print("(PRIV) Found UNIX socket. Enabling priv helper") + PRIV_HELPER = privHelper() + print("(PRIV) flush conntrack") + PRIV_HELPER.flushCT() + print("(PRIV) flush blocklist") + PRIV_HELPER.blocklistFlush() + + for port in range(DestportStart, DestPortEnd + 1, 1): + CONNTRACK[port] = False + + # If we are listening IPv6 and Master is an IPv4 IPv4Address + # IPv6ify the address. + if ListenIP == "::" and IsIPv4Address(Master): + Master = "::ffff:" + Master + + if use_selfservice: + # Create an instance of db_proxy and them pass it to the proxy + db_proxy = ProxyDB(db_server, db_username, db_password, db_name, db_port) + db_proxy.test_db(reactor) + else: + db_proxy = None + + srv_proxy = Proxy( + Master, + ListenPort, + CONNTRACK, + PEERTRACK, + BlackList, + IPBlackList, + Timeout, + Debug, + ClientInfo, + DestportStart, + DestPortEnd, + PRIV_HELPER, + RPTLTRACK, + db_proxy, + use_selfservice, + ) + + reactor.listenUDP(ListenPort, srv_proxy, interface=ListenIP) + + def loopingErrHandle(failure): + print( + "(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}".format( + failure + ) + ) + reactor.stop() + + if use_selfservice: + # Options loop + opts_loop = task.LoopingCall(srv_proxy.send_opts) + opts_loop.start(10).addErrback(loopingErrHandle) + + # Clean table every hour + cl_tbl = task.LoopingCall(db_proxy.clean_tbl) + cl_tbl.start(3600).addErrback(loopingErrHandle) + + # Update last seen loop + ls_loop = task.LoopingCall(srv_proxy.lst_seen) + ls_loop.start(120).addErrback(loopingErrHandle) + + def stats(): + count = 0 + nowtime = time() + for port in CONNTRACK: + if CONNTRACK[port]: + count = count + 1 + + totalPorts = DestPortEnd - DestportStart + freePorts = totalPorts - count + + print( + "{} ports out of {} in use ({} free)".format(count, totalPorts, freePorts) + ) + + def blackListTrimmer(): + _timenow = time() + _dellist = [] + for entry in IPBlackList: + deletetime = IPBlackList[entry] + if deletetime and deletetime < _timenow: + _dellist.append(entry) + + for delete in _dellist: + IPBlackList.pop(delete) + if ClientInfo: + print("Remove dynamic blacklist entry for {}".format(delete)) + if PRIV_HELPER: + print( + "Ask priv helper to remove blacklist entry for {} from iptables".format( + delete + ) + ) + reactor.callInThread(PRIV_HELPER.delBL, ListenPort, delete) + + def rptlTrimmer(): + RPTLTRACK.clear() + print("Purge RPTL table") + + if Stats == True: + stats_task = task.LoopingCall(stats) + statsa = stats_task.start(30) + statsa.addErrback(loopingErrHandle) + + blacklist_task = task.LoopingCall(blackListTrimmer) + blacklista = blacklist_task.start(15) + blacklista.addErrback(loopingErrHandle) + + rptlTrimmer_task = task.LoopingCall(rptlTrimmer) + rptlTrimmera = rptlTrimmer_task.start(60) + rptlTrimmera.addErrback(loopingErrHandle) + + reactor.run() diff --git a/hotspot_proxy_v2.py b/hotspot_proxy_v2.py index 03ce5c0..0878cfc 100644 --- a/hotspot_proxy_v2.py +++ b/hotspot_proxy_v2.py @@ -196,10 +196,10 @@ class Proxy(DatagramProtocol): if _command in (MSTN,MSTC): # Give time to the client for a reply to prevent port reassignment self.peerTrack[_peer_id]['timer'].reset(15) - + return - + else: _command = data[:4] @@ -307,7 +307,7 @@ if __name__ == '__main__': # Ensure we have a path for the config file, if one wasn't specified, then use the execution directory if not cli_args.CONFIG_FILE: - cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/freedmr.cfg' + cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/config/adn.cfg' _config_file = cli_args.CONFIG_FILE @@ -340,8 +340,8 @@ if __name__ == '__main__': ListenPort = 62031 #'' = all IPv4, '::' = all IPv4 and IPv6 (Dual Stack) ListenIP = '' - DestportStart = 54000 - DestPortEnd = 54100 + DestportStart = 56400 + DestPortEnd = 56500 Timeout = 30 Stats = False Debug = False diff --git a/proxy_db.py b/proxy_db.py new file mode 100644 index 0000000..6d062cd --- /dev/null +++ b/proxy_db.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# +############################################################################### +# Copyright (C) 2021-2022 Christian Quiroz, OA4DOA +# +# 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, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +############################################################################### +import sys + +from twisted.enterprise import adbapi +from twisted.internet.defer import inlineCallbacks + + +__author__ = 'Christian Quiroz, OA4DOA' +__version__ = '1.0.0' +__copyright__ = 'Copyright (c) 2021-2022 Christian Quiroz, OA4DOA' +__license__ = 'GNU GPLv3' +__maintainer__ = 'Christian Quiroz, OA4DOA' +__email__ = 'adm@dmr-peru.pe' + + +class ProxyDB: + def __init__(self, host, user, psswd, db_name, port): + self.db_name = db_name + self.dbpool = adbapi.ConnectionPool("MySQLdb", host, user, psswd, db_name, + port=port, charset="utf8mb4") + + @inlineCallbacks + def make_clients_tbl(self): + try: + yield self.dbpool.runOperation( + ''' CREATE TABLE IF NOT EXISTS Clients( + int_id INT UNIQUE PRIMARY KEY NOT NULL, + dmr_id TINYBLOB NOT NULL, + callsign VARCHAR(10) NOT NULL, + host VARCHAR(15), + options VARCHAR(100), + opt_rcvd TINYINT(1) DEFAULT False NOT NULL, + mode TINYINT(1) DEFAULT 4 NOT NULL, + logged_in TINYINT(1) DEFAULT False NOT NULL, + modified TINYINT(1) DEFAULT False NOT NULL, + psswd BLOB(256), + last_seen INT NOT NULL) CHARSET=utf8mb4''') + + except Exception as err: + print(f"make_clientss_tbl error: {err}") + + @inlineCallbacks + def test_db(self, _reactor): + try: + res = yield self.dbpool.runQuery("SELECT 1") + if res: + self.updt_tbl("start") + print("Database connection test: OK") + + except Exception as err: + if _reactor.running: + print(f"Database connection error: {err}, stopping the reactor.") + _reactor.stop() + else: + sys.exit(f"Database connection error: {err}, exiting.") + + @inlineCallbacks + def ins_conf(self, int_id, dmr_id, callsign, host, mode): + try: + yield self.dbpool.runOperation( + '''INSERT IGNORE INTO Clients ( + int_id, dmr_id, callsign, host, mode, logged_in, last_seen, psswd) + VALUES (%s, %s, %s, %s, %s, True, UNIX_TIMESTAMP(), NULL) ON DUPLICATE KEY UPDATE + callsign = %s, host = %s, mode = %s, logged_in = True, opt_rcvd = False, + last_seen = UNIX_TIMESTAMP(), psswd = NULL''', + (int_id, dmr_id, callsign, host, mode, callsign, host, mode)) + + except Exception as err: + print(f"ins_conf error: {err}") + + @inlineCallbacks + def clean_tbl(self): + try: + yield self.dbpool.runOperation( + "DELETE FROM Clients WHERE last_seen < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY))") + + except Exception as err: + print(f"clean_tbl error: {err}") + + def slct_db(self): + return self.dbpool.runQuery( + "SELECT dmr_id, options FROM Clients WHERE modified = True and logged_in = True") + + def slct_opt(self, _peer_id): + return self.dbpool.runQuery("SELECT options FROM Clients WHERE dmr_id = %s", (_peer_id,)) + + @inlineCallbacks + def updt_tbl(self, actn, dmr_id=None, psswd=None): + try: + if actn == "start": + yield self.dbpool.runOperation("UPDATE Clients SET logged_in=False, opt_rcvd=False") + elif actn == "opt_rcvd": + yield self.dbpool.runOperation( + "UPDATE Clients SET opt_rcvd = True, options = NULL WHERE dmr_id = %s", + (dmr_id,)) + elif actn == "last_seen": + yield self.dbpool.runOperation( + "UPDATE Clients SET last_seen = UNIX_TIMESTAMP() WHERE dmr_id = %s and logged_in = True", + (dmr_id,)) + elif actn == "log_out": + yield self.dbpool.runOperation( + "UPDATE Clients SET logged_in = False, modified = False WHERE dmr_id = %s", + (dmr_id,)) + elif actn == "rst_mod": + yield self.dbpool.runOperation( + "UPDATE Clients SET modified = False WHERE dmr_id = %s", (dmr_id,)) + elif actn == "psswd": + yield self.dbpool.runOperation( + "UPDATE Clients SET psswd = %s WHERE dmr_id = %s", (psswd, dmr_id)) + + except Exception as err: + print(f"updt_tbl error: {err}") + + @inlineCallbacks + def updt_lstseen(self, dmrid_list): + try: + def db_actn(txn): + txn.executemany( + "UPDATE Clients SET last_seen = UNIX_TIMESTAMP() WHERE dmr_id = %s", dmrid_list) + yield self.dbpool.runInteraction(db_actn) + + except Exception as err: + print(f"updt_lstseen error: {err}") + + +if __name__ == "__main__": + db_test = ProxyDB('localhost', 'root', '', 'test', 3306) + print(db_test) + \ No newline at end of file From fbcb27eec97d73056b27abde70221d367e83803a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Pe=CC=81rez?= Date: Sun, 12 May 2024 21:21:06 -0400 Subject: [PATCH 4/6] Changes for Parrot/Loro --- config/ADN-MINIMAL.cfg | 32 +++++ config/ADN-SAMPLE.cfg | 39 +++++- config/{loro.cfg => parrot.cfg} | 27 ++-- config/playback_file.cfg | 233 -------------------------------- data/.empty | 0 5 files changed, 82 insertions(+), 249 deletions(-) rename config/{loro.cfg => parrot.cfg} (92%) delete mode 100644 config/playback_file.cfg create mode 100644 data/.empty diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index 957b5dd..645ef0b 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -41,3 +41,35 @@ ALLOW_UNREG_ID: False PROXY_CONTROL: False OVERRIDE_IDENT_TG: +[ECHO] +MODE: PEER +ENABLED: True +LOOSE: False +EXPORT_AMBE: False +IP: 127.0.0.1 +PORT: 54916 +MASTER_IP: 127.0.0.1 +MASTER_PORT: 54915 +PASSPHRASE: passw0rd +CALLSIGN: ECHO +RADIO_ID: 9990 +RX_FREQ: 449000000 +TX_FREQ: 444000000 +TX_POWER: 25 +COLORCODE: 1 +SLOTS: 1 +LATITUDE: 00.0000 +LONGITUDE: 000.0000 +HEIGHT: 0 +LOCATION: 9990 Parrot +DESCRIPTION: ECHO +URL: adn.systems +SOFTWARE_ID: 20170620 +PACKAGE_ID: MMDVM_ADN-Systems +GROUP_HANGTIME: 5 +OPTIONS: +USE_ACL: True +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +ANNOUNCEMENT_LANGUAGE: en_GB diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index 7d877aa..e8add2a 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -22,10 +22,10 @@ REPORT_PORT: 4321 REPORT_CLIENTS: 127.0.0.1 [LOGGER] -LOG_FILE: freedmr.log +LOG_FILE: adn.log LOG_HANDLERS: console-timed LOG_LEVEL: INFO -LOG_NAME: FreeDMR +LOG_NAME: ADN [ALIASES] TRY_DOWNLOAD: True @@ -68,7 +68,7 @@ SUB_ACL: DENY:1 TGID_ACL: PERMIT:ALL RELAX_CHECKS: True ENHANCED_OBP: True -PROTO_VER: 2 +PROTO_VER: 5 [SYSTEM] MODE: MASTER @@ -96,3 +96,36 @@ GENERATOR: 100 ALLOW_UNREG_ID: False PROXY_CONTROL: False OVERRIDE_IDENT_TG: + +[ECHO] +MODE: PEER +ENABLED: True +LOOSE: False +EXPORT_AMBE: False +IP: 127.0.0.1 +PORT: 54916 +MASTER_IP: 127.0.0.1 +MASTER_PORT: 54915 +PASSPHRASE: passw0rd +CALLSIGN: ECHO +RADIO_ID: 9990 +RX_FREQ: 449000000 +TX_FREQ: 444000000 +TX_POWER: 25 +COLORCODE: 1 +SLOTS: 1 +LATITUDE: 00.0000 +LONGITUDE: 000.0000 +HEIGHT: 0 +LOCATION: 9990 Parrot +DESCRIPTION: ECHO +URL: adn.systems +SOFTWARE_ID: 20170620 +PACKAGE_ID: MMDVM_ADN-Systems +GROUP_HANGTIME: 5 +OPTIONS: +USE_ACL: True +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +ANNOUNCEMENT_LANGUAGE: en_GB diff --git a/config/loro.cfg b/config/parrot.cfg similarity index 92% rename from config/loro.cfg rename to config/parrot.cfg index 63eed1f..0628cdf 100644 --- a/config/loro.cfg +++ b/config/parrot.cfg @@ -103,19 +103,21 @@ LOG_NAME: HBlink # STALE_DAYS is the number of days since the last download before we # download again. Don't be an ass and change this to less than a few days. [ALIASES] -TRY_DOWNLOAD: False -PATH: ./ +TRY_DOWNLOAD: True +PATH: ./data PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://www.radioid.net/static/rptrs.json -SUBSCRIBER_URL: https://www.radioid.net/static/users.json -TGID_URL: http://downloads.freedmr.uk/downloads/talkgroup_ids.json -LOCAL_SUBSCRIBER_FILE: local_subscriber_ids.json -STALE_DAYS: 7 +PEER_URL: https://adn.systems/files/peer_ids.json +SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json +TGID_URL: https://adn.systems/files/talkgroup_ids.json +LOCAL_SUBSCRIBER_FILE: local_subcriber_ids.json +STALE_DAYS: 1 SUB_MAP_FILE: -SERVER_ID_URL: http://downloads.freedmr.uk/downloads/FreeDMR_Hosts.csv +SERVER_ID_URL: https://adn.systems/files/server_ids.tsv SERVER_ID_FILE: server_ids.tsv +CHECKSUM_URL: https://adn.systems/files/file_checksums.json +CHECKSUM_FILE: file_checksums.json # OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS @@ -142,7 +144,7 @@ IP: PORT: 62044 NETWORK_ID: 1 PASSPHRASE: mypass -TARGET_IP: +TARGET_IP: TARGET_PORT: 62044 USE_ACL: True SUB_ACL: DENY:1 @@ -169,7 +171,7 @@ REPEAT: True MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 -PORT: 54915 +PORT: 54916 PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True @@ -181,11 +183,10 @@ DEFAULT_UA_TIMER: 10 SINGLE_MODE: True VOICE_IDENT: False TS1_STATIC: -TS2_STATIC: +TS2_STATIC: DEFAULT_REFLECTOR: 0 GENERATOR: 1 -ANNOUNCEMENT_LANGUAGE:es_ES +ANNOUNCEMENT_LANGUAGE: en_GB ALLOW_UNREG_ID: True PROXY_CONTROL: False OVERRIDE_IDENT_TG: - diff --git a/config/playback_file.cfg b/config/playback_file.cfg deleted file mode 100644 index 89f269d..0000000 --- a/config/playback_file.cfg +++ /dev/null @@ -1,233 +0,0 @@ -# PROGRAM-WIDE PARAMETERS GO HERE -# PATH - working path for files, leave it alone unless you NEED to change it -# PING_TIME - the interval that peers will ping the master, and re-try registraion -# - how often the Master maintenance loop runs -# MAX_MISSED - how many pings are missed before we give up and re-register -# - number of times the master maintenance loop runs before de-registering a peer -# -# ACLs: -# -# Access Control Lists are a very powerful tool for administering your system. -# But they consume packet processing time. Disable them if you are not using them. -# But be aware that, as of now, the configuration stanzas still need the ACL -# sections configured even if you're not using them. -# -# REGISTRATION ACLS ARE ALWAYS USED, ONLY SUBSCRIBER AND TGID MAY BE DISABLED!!! -# -# The 'action' May be PERMIT|DENY -# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999) -# Format: -# ACL = 'action:id|start-end|,id|start-end,....' -# --for example-- -# SUB_ACL: DENY:1,1000-2000,4500-60000,17 -# -# ACL Types: -# REG_ACL: peer radio IDs for registration (only used on HBP master systems) -# SUB_ACL: subscriber IDs for end-users -# TGID_TS1_ACL: destination talkgroup IDs on Timeslot 1 -# TGID_TS2_ACL: destination talkgroup IDs on Timeslot 2 -# -# ACLs may be repeated for individual systems if needed for granularity -# Global ACLs will be processed BEFORE the system level ACLs -# Packets will be matched against all ACLs, GLOBAL first. If a packet 'passes' -# All elements, processing continues. Packets are discarded at the first -# negative match, or 'reject' from an ACL element. -# -# If you do not wish to use ACLs, set them to 'PERMIT:ALL' -# TGID_TS1_ACL in the global stanza is used for OPENBRIDGE systems, since all -# traffic is passed as TS 1 between OpenBridges -[GLOBAL] -PATH: ./ -PING_TIME: 10 -MAX_MISSED: 3 -USE_ACL: True -REG_ACL: PERMIT:ALL -SUB_ACL: DENY:1 -TGID_TS1_ACL: PERMIT:ALL -TGID_TS2_ACL: PERMIT:ALL -GEN_STAT_BRIDGES: False -ALLOW_NULL_PASSPHRASE: False -ANNOUNCEMENT_LANGUAGES: es_ES -SERVER_ID: 9990 -DATA_GATEWAY: False -VALIDATE_SERVER_IDS: False - - - -# NOT YET WORKING: NETWORK REPORTING CONFIGURATION -# Enabling "REPORT" will configure a socket-based reporting -# system that will send the configuration and other items -# to a another process (local or remote) that may process -# the information for some useful purpose, like a web dashboard. -# -# REPORT - True to enable, False to disable -# REPORT_INTERVAL - Seconds between reports -# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK -# REPORT_CLIENTS - comma separated list of IPs you will allow clients -# to connect on. Entering a * will allow all. -# -# ****FOR NOW MUST BE TRUE - USE THE LOOPBACK IF YOU DON'T USE THIS!!!**** -[REPORTS] -REPORT: False -REPORT_INTERVAL: 60 -REPORT_PORT: 4821 -REPORT_CLIENTS: 127.0.0.1 - - -# SYSTEM LOGGER CONFIGURAITON -# This allows the logger to be configured without chaning the individual -# python logger stuff. LOG_FILE should be a complete path/filename for *your* -# system -- use /dev/null for non-file handlers. -# LOG_HANDLERS may be any of the following, please, no spaces in the -# list if you use several: -# null -# console -# console-timed -# file -# file-timed -# syslog -# LOG_LEVEL may be any of the standard syslog logging levels, though -# as of now, DEBUG, INFO, WARNING and CRITICAL are the only ones -# used. -# -[LOGGER] -LOG_FILE: /dev/null -LOG_HANDLERS: console-timed -LOG_LEVEL: DEBUG -LOG_NAME: HBlink - -# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES -# Ok, not the TGID, there's no master list I know of to download -# This is intended as a facility for other applcations built on top of -# HBlink to use, and will NOT be used in HBlink directly. -# STALE_DAYS is the number of days since the last download before we -# download again. Don't be an ass and change this to less than a few days. -[ALIASES] -TRY_DOWNLOAD: False -PATH: ./ -PEER_FILE: peer_ids.json -SUBSCRIBER_FILE: subscriber_ids.json -TGID_FILE: talkgroup_ids.json -PEER_URL: https://www.radioid.net/static/rptrs.json -SUBSCRIBER_URL: https://www.radioid.net/static/users.json -TGID_URL: http://downloads.freedmr.uk/downloads/talkgroup_ids.json -LOCAL_SUBSCRIBER_FILE: local_subscriber_ids.json -STALE_DAYS: 7 -SUB_MAP_FILE: -SERVER_ID_FILE: -SERVER_ID_URL: - -#Read further repeater configs from MySQL -[MYSQL] -USE_MYSQL: False -USER: hblink -PASS: mypassword -DB: hblink -SERVER: 127.0.0.1 -PORT: 3306 -TABLE: repeaters - -# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS -# OpenBridge is a protocol originall created by DMR+ for connection between an -# IPSC2 server and Brandmeister. It has been implemented here at the suggestion -# of the Brandmeister team as a way to legitimately connect HBlink to the -# Brandemiester network. -# It is recommended to name the system the ID of the Brandmeister server that -# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the -# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password -# that must be agreed upon between you and the operator of the server you are -# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that -# will be sent to the other server to identify this connection. -# other parameters follow the other system types. -# -# ACLs: -# OpenBridge does not 'register', so registration ACL is meaningless. -# OpenBridge passes all traffic on TS1, so there is only 1 TGID ACL. -# Otherwise ACLs work as described in the global stanza -[OBP-TEST] -MODE: OPENBRIDGE -ENABLED: False -IP: -PORT: 62044 -NETWORK_ID: 1 -PASSPHRASE: mypass -TARGET_IP: -TARGET_PORT: 62044 -USE_ACL: True -SUB_ACL: DENY:1 -TGID_ACL: PERMIT:ALL -RELAX_CHECKS: False - -# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS -# HomeBrew Protocol Master instances go here. -# IP may be left blank if there's one interface on your system. -# Port should be the port you want this master to listen on. It must be unique -# and unused by anything else. -# Repeat - if True, the master repeats traffic to peers, False, it does nothing. -# -# MAX_PEERS -- maximun number of peers that may be connect to this master -# at any given time. This is very handy if you're allowing hotspots to -# connect, or using a limited computer like a Raspberry Pi. -# -# ACLs: -# See comments in the GLOBAL stanza -[PARROT] -MODE: MASTER -ENABLED: False -REPEAT: True -MAX_PEERS: 1 -EXPORT_AMBE: False -IP: bmproject.freedmr.uk -PORT: 54915 -PASSPHRASE: passw0rd -GROUP_HANGTIME: 5 -USE_ACL: True -REG_ACL: DENY:1 -SUB_ACL: DENY:1 -TGID_TS1_ACL: PERMIT:ALL -TGID_TS2_ACL: PERMIT:ALL -DEFAULT_UA_TIMER: 10 -SINGLE_MODE: True -VOICE_IDENT: False -TS1_STATIC: -TS2_STATIC: -DEFAULT_REFLECTOR: 0 -GENERATOR: 1 -ANNOUNCEMENT_LANGUAGE:es_ES -ALLOW_UNREG_ID: True -PROXY_CONTROL: False - - -[PLAY] -MODE: PEER -ENABLED: True -LOOSE: False -EXPORT_AMBE: False -IP: -PORT: 54002 -MASTER_IP: localhost -MASTER_PORT: 54001 -PASSPHRASE: homebrew -CALLSIGN: M0XFD -RADIO_ID: 2340210 -RX_FREQ: 449000000 -TX_FREQ: 444000000 -TX_POWER: 25 -COLORCODE: 1 -SLOTS: 1 -LATITUDE: 38.0000 -LONGITUDE: -095.0000 -HEIGHT: 75 -LOCATION: Anywhere, USA -DESCRIPTION: play_file.py -URL: www.w1abc.org -SOFTWARE_ID: 20170620 -PACKAGE_ID: MMDVM_FreeDMR -GROUP_HANGTIME: 5 -OPTIONS: -USE_ACL: True -SUB_ACL: DENY:1 -TGID_TS1_ACL: PERMIT:ALL -TGID_TS2_ACL: PERMIT:ALL -ANNOUNCEMENT_LANGUAGE: en_GB - diff --git a/data/.empty b/data/.empty new file mode 100644 index 0000000..e69de29 From 2471acdac50f523ee08db91b4636e9568c3caf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Pe=CC=81rez?= Date: Sun, 12 May 2024 22:00:19 -0400 Subject: [PATCH 5/6] Configs --- config.py | 2 +- config/ADN-MINIMAL.cfg | 4 ++-- config/ADN-SAMPLE-commented.cfg | 4 ++-- config/ADN-SAMPLE.cfg | 4 ++-- config/parrot.cfg | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config.py b/config.py index 49cd204..83bbf1c 100755 --- a/config.py +++ b/config.py @@ -176,7 +176,7 @@ def build_config(_config_file): elif section == 'ALIASES': CONFIG['ALIASES'].update({ 'TRY_DOWNLOAD': config.getboolean(section, 'TRY_DOWNLOAD', fallback=True), - 'PATH': config.get(section, 'PATH', fallback='./json/'), + 'PATH': config.get(section, 'PATH', fallback='./data/'), 'PEER_FILE': config.get(section, 'PEER_FILE', fallback='peer_ids.json'), 'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE', fallback='subscriber_ids.json'), 'TGID_FILE': config.get(section, 'TGID_FILE', fallback='talkgroup_ids.json'), diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index 645ef0b..3c8043a 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -33,8 +33,8 @@ DEFAULT_UA_TIMER: 60 TS1_STATIC: TS2_STATIC: DEFAULT_REFLECTOR: 0 -SINGLE_MODE: True -VOICE_IDENT: True +SINGLE_MODE: False +VOICE_IDENT: False ANNOUNCEMENT_LANGUAGE: en_GB GENERATOR: 100 ALLOW_UNREG_ID: False diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index 3989e91..1c254e8 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -201,8 +201,8 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL DEFAULT_UA_TIMER: 10 -SINGLE_MODE: True -VOICE_IDENT: True +SINGLE_MODE: False +VOICE_IDENT: False #the next three lines no longer have any effect TS1_STATIC: TS2_STATIC: diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index e8add2a..913896f 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -86,8 +86,8 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL DEFAULT_UA_TIMER: 60 -SINGLE_MODE: True -VOICE_IDENT: True +SINGLE_MODE: False +VOICE_IDENT: False TS1_STATIC: TS2_STATIC: DEFAULT_REFLECTOR: 0 diff --git a/config/parrot.cfg b/config/parrot.cfg index 0628cdf..8506ed7 100644 --- a/config/parrot.cfg +++ b/config/parrot.cfg @@ -47,7 +47,7 @@ TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL GEN_STAT_BRIDGES: False ALLOW_NULL_PASSPHRASE: False -ANNOUNCEMENT_LANGUAGES: es_ES +ANNOUNCEMENT_LANGUAGES: en_GB SERVER_ID: 9990 DATA_GATEWAY: False VALIDATE_SERVER_IDS: False @@ -94,7 +94,7 @@ REPORT_CLIENTS: 127.0.0.1 LOG_FILE: /dev/null LOG_HANDLERS: null LOG_LEVEL: DEBUG -LOG_NAME: HBlink +LOG_NAME: ADN # DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES # Ok, not the TGID, there's no master list I know of to download @@ -103,7 +103,7 @@ LOG_NAME: HBlink # STALE_DAYS is the number of days since the last download before we # download again. Don't be an ass and change this to less than a few days. [ALIASES] -TRY_DOWNLOAD: True +TRY_DOWNLOAD: False PATH: ./data PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json From eed7b8df5f68f1860d40667889115846fc1ee672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Pe=CC=81rez?= Date: Sun, 12 May 2024 22:06:07 -0400 Subject: [PATCH 6/6] docker-compose.yml --- docker-configs/docker-compose.yml | 55 ++++++++++--------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/docker-configs/docker-compose.yml b/docker-configs/docker-compose.yml index b2fe633..9f07818 100644 --- a/docker-configs/docker-compose.yml +++ b/docker-configs/docker-compose.yml @@ -1,5 +1,4 @@ -############################################################################### -# Copyright (C) 2020 Simon Adlem, G7RZU +############################################################################### # # 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 @@ -14,24 +13,25 @@ # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# ############################################################################### version: '2.4' services: - freedmr: - container_name: freedmr + + adn-server: + container_name: adn-server cpu_shares: 1024 mem_reservation: 600m volumes: - - '/etc/freedmr/freedmr.cfg:/opt/freedmr/freedmr.cfg' - #Write JSON files outside of container - - '/etc/freedmr/json/:/opt/freedmr/json/' + - '/etc/ADN-Systems/ADN.cfg:/opt/ADN-DMR-Peer-Server/config/ADN.cfg' ports: - '62031:62031/udp' #Change the below to inlude ports used for your OBP(s) - '62041:62041/udp' - image: 'gitlab.hacknix.net:5050/hacknix/freedmr:latest' + - '8000:8000' + image: 'registry.gitlab.com/hp3icc/fdmr/adn-server-single:latest' restart: "unless-stopped" networks: app_net: @@ -48,45 +48,25 @@ services: - FDPROXY_DEBUG=0 #Override proxy external port #- FDPROXY_LISTENPORT=62031 - read_only: "true" - freedmrmonitor2: - container_name: freedmrmonitor2 - cpu_shares: 512 - depends_on: - - freedmr - image: 'gitlab.hacknix.net:5050/freedmr/freedmrmonitor2/freedmrmonitor2:monitor-latest' - restart: "unless-stopped" - networks: - app_net: - ipv4_address: 172.16.238.20 - read_only: "true" - logging: - driver: json-file - - freedmrmonpache: - container_name: freedmrmonapache + adn-dashboard: + container_name: adn-dashboard cpu_shares: 512 - depends_on: - - freedmrmonitor2 - #where to store TLS certificates - #and acme.sh files volumes: - - '/etc/freedmr/certs/:/opt/apachecerts/' - - '/etc/freedmr/acme.sh:/root/.acme.sh/' + - '/etc/freedmr/fdmr-mon.cfg:/opt/FDMR-Monitor/fdmr-mon.cfg' ports: - '80:80/tcp' - '443:443/tcp' - image: 'gitlab.hacknix.net:5050/freedmr/freedmrmonitor2/freedmrmonitor2:apache-latest' + - '9000:9000' + image: 'registry.gitlab.com/hp3icc/fdmr/adn-mon2-single:latest' restart: "unless-stopped" - environment: - #Set to 1 to enable TLS support + #Set USE_SSL = to True to enable TLS support #you'll need to actually generate the certtificates too #using these commands when the container is running: - #docker exec -it freedmrmonapache gencert.sh - #docker-compose restart freedmrmonapache + #docker exec -it adn-dashboard gencert.sh + #docker-compose restart adn-dashboard #This only needs to be done once - unless the files in the volumes above are deleted. @@ -94,10 +74,9 @@ services: #Note -the gencert.sh script only works when the webserver is available on the default port 80 #If it's on non-standard ports, you'll need to request the certificates manually. - - 'USE_TLS=1' networks: app_net: - ipv4_address: 172.16.238.30 + ipv4_address: 172.16.238.20 logging: driver: json-file