From 85da9edc164b92933d92aeca16be430a8274e992 Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Fri, 30 Aug 2024 11:46:56 +0100 Subject: [PATCH 1/4] Try hardening on for size --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index b802fff..acc4edb 100755 --- a/debian/rules +++ b/debian/rules @@ -1,6 +1,6 @@ #!/usr/bin/make -f -#export DEB_BUILD_MAINT_OPTIONS=hardening=+all +export DEB_BUILD_MAINT_OPTIONS=hardening=+all CFLAGS = $(shell dpkg-buildflags --get CFLAGS) CFLAGS += -fcommon From b18a579b0c56cbf065714972a48c19d1662180e8 Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Wed, 25 Sep 2024 01:37:12 +0100 Subject: [PATCH 2/4] Updates to .42 --- debian/changelog | 5 +++-- debian/control | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index f482e14..1efabd0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,10 @@ -linbpq (6.0.24.42-1) UNRELEASED; urgency=medium +linbpq (6.0.24.42-1) unstable; urgency=medium * Install config to /etc * New upstream update + * Add in new repo for continued updates - -- Dave Hibberd Fri, 30 Aug 2024 10:15:11 +0100 + -- Dave Hibberd Wed, 25 Sep 2024 01:36:59 +0100 linbpq (6.0.24.40-1) unstable; urgency=medium diff --git a/debian/control b/debian/control index e879a32..6c598e2 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Rules-Requires-Root: no Package: linbpq Architecture: linux-any -Depends: ${shlibs:Depends}, ${misc:Depends}, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, hibbian-archive-keyring Description: Packet node and ax25 stack LINBPQ is a Linux version of the BPQ32 Node, BBS and Chat Server components. It is actively developed by John G8BPQ and contains a complete, independent From 0d48067b828c78732fdf51981f254016933d4091 Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Thu, 26 Sep 2024 23:13:41 +0100 Subject: [PATCH 3/4] Sneak in the 32bit fixes --- debian/patches/ftbfs-gcc14.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/patches/ftbfs-gcc14.patch b/debian/patches/ftbfs-gcc14.patch index 38a656c..1f9b9d8 100644 --- a/debian/patches/ftbfs-gcc14.patch +++ b/debian/patches/ftbfs-gcc14.patch @@ -5,7 +5,7 @@ UINT rlen; - UINT outlen; -+ long outlen; ++ size_t outlen; memcpy(&rlen, &Decoded[5], 4); From eb0d36d3f67be13de74023adb6d30362d47805fe Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Fri, 11 Oct 2024 15:37:11 +0100 Subject: [PATCH 4/4] New upstream version 6.0.24.45 --- APRSCode.c | 4 +- APRSStdPages.c | 168 ++++++ ARDOP.c | 44 +- BBSUtilities.c | 33 +- BPQChat.aps | Bin 49660 -> 0 bytes BPQChat.vcproj.NOTTSDESKTOP.John.user | 65 --- BPQMail.aps | Bin 91908 -> 0 bytes BPQMail.c | 2 + BPQMail.sln | 20 - BPQMail.vcproj.NOTTSDESKTOP.John.user | 65 --- BPQMail.vcproj.SKIGACER.johnw.user | 65 --- BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user | 65 --- BPQWinAPP.vcproj.NOTTSDESKTOP.John.user | 65 --- Bpq32.c | 7 +- CBPQ32.suo | Bin 5632 -> 0 bytes CBPQ32.vcproj.LAPTOP-Q6S4RP5Q.johnw.user | 65 --- CBPQ32.vcproj.NOTTSDESKTOP.John.user | 65 --- CBPQ32.vcproj.SKIGACER.johnw.user | 65 --- Cmd.c | 140 ++++- CommonCode.c | 76 ++- Events.c | 13 + FreeDATA.c | 5 +- HTTPcode.c | 77 ++- Housekeeping.c | 16 +- KISSHF.c | 115 ++++- L2Code.c | 485 ++++++++++++------ MBLRoutines.c | 2 +- MailNode.vcproj.NOTTSDESKTOP.John.user | 65 --- MailNode.vcproj.SKIGACER.johnw.user | 65 --- MailRouting.c | 17 + Moncode.c | 11 + RigControl.c | 2 +- SCSPactor.c | 10 +- SCSTracker.c | 6 + UZ7HODrv.c | 5 + VARA.c | 224 +++++++- Versions.h | 6 +- WinRPRHelper.vcproj.NOTTSDESKTOP.John.user | 65 --- ...rControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user | 65 --- WinmorControl.vcproj.NOTTSDESKTOP.John.user | 65 --- asmstrucs.h | 24 +- cMain.c | 9 +- config.c | 11 +- config.ini | 40 -- configstructs.h | 2 + kiss.c | 356 ++++++++++++- linether.c | 35 +- nodeapi.c | 5 +- tncinfo.h | 14 +- upnp.c.bak | 187 ------- 50 files changed, 1629 insertions(+), 1387 deletions(-) delete mode 100644 BPQChat.aps delete mode 100644 BPQChat.vcproj.NOTTSDESKTOP.John.user delete mode 100644 BPQMail.aps delete mode 100644 BPQMail.sln delete mode 100644 BPQMail.vcproj.NOTTSDESKTOP.John.user delete mode 100644 BPQMail.vcproj.SKIGACER.johnw.user delete mode 100644 BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user delete mode 100644 BPQWinAPP.vcproj.NOTTSDESKTOP.John.user delete mode 100644 CBPQ32.suo delete mode 100644 CBPQ32.vcproj.LAPTOP-Q6S4RP5Q.johnw.user delete mode 100644 CBPQ32.vcproj.NOTTSDESKTOP.John.user delete mode 100644 CBPQ32.vcproj.SKIGACER.johnw.user delete mode 100644 MailNode.vcproj.NOTTSDESKTOP.John.user delete mode 100644 MailNode.vcproj.SKIGACER.johnw.user delete mode 100644 WinRPRHelper.vcproj.NOTTSDESKTOP.John.user delete mode 100644 WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user delete mode 100644 WinmorControl.vcproj.NOTTSDESKTOP.John.user delete mode 100644 config.ini delete mode 100644 upnp.c.bak diff --git a/APRSCode.c b/APRSCode.c index 34c9062..ff1e322 100644 --- a/APRSCode.c +++ b/APRSCode.c @@ -348,7 +348,7 @@ APRSHEARDRECORD MHTABLE[MAXHEARD] = {0}; APRSHEARDRECORD * MHDATA = &MHTABLE[0]; -static SOCKET sock = (SOCKET) NULL; +static SOCKET sock = (SOCKET)0; //Duplicate suppression Code @@ -9154,7 +9154,7 @@ void GetSavedAPRSMessages() FILE *file; struct APRSMESSAGE * Message; struct APRSMESSAGE * ptr; - char Line[256]; + char Line[512]; char * Stamp = 0; char * From = 0; char * To = 0; diff --git a/APRSStdPages.c b/APRSStdPages.c index 300df3c..456e8ba 100644 --- a/APRSStdPages.c +++ b/APRSStdPages.c @@ -3225,6 +3225,170 @@ char * get_plane(int * Len) return ptr; } +char * get_portstats() +{ + char Msg[] = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "

Last hour's stats for Port

\n" + "

\n" + "\n" + "Your browser does not support the HTML canvas tag.\n" + "
\n" + "\n" + "Your browser does not support the HTML canvas tag.\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ""; + + return _strdup(Msg);; +} char * get_aprs() { @@ -3638,6 +3802,10 @@ char * GetStandardPage(char * FN, int * Len) if (_stricmp(FN, "leaflet.rotatedMarker.js") == 0) return get_rotatedMarker(); + if (_stricmp(FN, "PortStats.html") == 0) + return get_portstats(); + + if (_stricmp(FN, "info_call.html") == 0) return get_info_call(); diff --git a/ARDOP.c b/ARDOP.c index 61fab6b..d79b632 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -958,7 +958,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) TNC->Streams[0].Connecting || TNC->Streams[0].Connected) { - // discard if TNC not connected or sesison active + // discard if TNC not connected or session active ReleaseBuffer(buffptr); continue; @@ -1865,13 +1865,11 @@ VOID ARDOPReleaseTNC(struct TNCINFO * TNC) ARDOPChangeMYC(TNC, TNC->NodeCall); - ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); - strcpy(TNC->WEB_TNCSTATE, "Free"); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - // Start Scanner - + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + // Start Scanner if (TNC->DefaultRadioCmd) @@ -1889,12 +1887,19 @@ VOID ARDOPReleaseTNC(struct TNCINFO * TNC) VOID ARDOPSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } VOID ARDOPReleasePort(struct TNCINFO * TNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } extern char WebProcTemplate[]; @@ -2953,6 +2958,15 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) if (TNC->PTTMode) Rig_PTT(TNC, TRUE); + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } return; } if (_memicmp(Buffer, "PTT F", 5) == 0) @@ -2961,6 +2975,12 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) if (TNC->PTTMode) Rig_PTT(TNC, FALSE); + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + return; } @@ -2969,6 +2989,8 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) TNC->BusyFlags |= CDBusy; TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->BusyonTime = GetTickCount(); + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); strcpy(TNC->WEB_CHANSTATE, "Busy"); @@ -2985,6 +3007,12 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) else strcpy(TNC->WEB_CHANSTATE, "Clear"); + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); TNC->WinmorRestartCodecTimer = time(NULL); return; @@ -3852,6 +3880,7 @@ VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, in char * ptr2; char c; int Len = Length; + char Call[10] = ""; Debugprintf(Data); @@ -3924,7 +3953,12 @@ VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, in buffptr->LENGTH = 16 + MSGHDDRLEN + APLen; time(&buffptr->Timestamp); + memcpy(Call,ptr1, 9); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + BPQTRACE((MESSAGE *)buffptr, TRUE); + } else { diff --git a/BBSUtilities.c b/BBSUtilities.c index a3b9bd8..1db4bed 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -124,7 +124,7 @@ void decodeblock( unsigned char in[4], unsigned char out[3]); int encode_quoted_printable(char *s, char * out, int Len); int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); -void SendMessageReadEvent(char * user, struct MsgInfo * Msg); +void SendMessageReadEvent(char * call, struct MsgInfo * Msg); void SendNewMessageEvent(char * call, struct MsgInfo * Msg); config_t cfg; @@ -10059,6 +10059,7 @@ BOOL GetConfig(char * ConfigName) char Size[80]; config_setting_t *setting; const char * ptr; + char * ptr1; char FBBString[8192]= ""; FBBFilter f; config_init(&cfg); @@ -10265,7 +10266,7 @@ BOOL GetConfig(char * ConfigName) GetStringValue(group, "FBBFilters", FBBString); - ptr = FBBString; + ptr1 = FBBString; // delete old list @@ -10279,31 +10280,31 @@ BOOL GetConfig(char * ConfigName) free(Filters); Filters = NULL; - while (ptr && ptr[0]) + while (ptr1 && ptr1[0]) { FBBFilter * PFilter; - f.Action = ptr[0]; - f.Type = ptr[2]; - ptr = &ptr[4]; + f.Action = ptr1[0]; + f.Type = ptr1[2]; + ptr1 = &ptr1[4]; - memcpy(f.From, ptr, 10); + memcpy(f.From, ptr1, 10); strlop(f.From, '|'); - ptr = strlop(ptr, '|'); + ptr1 = strlop(ptr1, '|'); - memcpy(f.TO, ptr, 10); + memcpy(f.TO, ptr1, 10); strlop(f.TO, '|'); - ptr = strlop(ptr, '|'); + ptr1 = strlop(ptr1, '|'); - memcpy(f.AT, ptr, 10); + memcpy(f.AT, ptr1, 10); strlop(f.AT, '|'); - ptr = strlop(ptr, '|'); + ptr1 = strlop(ptr1, '|'); - memcpy(f.BID, ptr, 10); + memcpy(f.BID, ptr1, 10); strlop(f.BID, '|'); - ptr = strlop(ptr, '|'); + ptr1 = strlop(ptr1, '|'); - f.MaxLen = atoi(ptr); + f.MaxLen = atoi(ptr1); // add to list @@ -10325,7 +10326,7 @@ BOOL GetConfig(char * ConfigName) p->Next = PFilter; } - ptr = strlop(ptr, '|'); + ptr1 = strlop(ptr1, '|'); } diff --git a/BPQChat.aps b/BPQChat.aps deleted file mode 100644 index 579f2ade2c20de49bcba2902074863ff023b21c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49660 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 diff --git a/BPQChat.vcproj.NOTTSDESKTOP.John.user b/BPQChat.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index fa82c00..0000000 --- a/BPQChat.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/BPQMail.aps b/BPQMail.aps deleted file mode 100644 index 8e698f3d85c0ee070383c9430a923a9e0fc10b7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91908 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 AmH+?% diff --git a/BPQMail.c b/BPQMail.c index 402d87f..fc6a172 100644 --- a/BPQMail.c +++ b/BPQMail.c @@ -1138,6 +1138,8 @@ // Allow selection of 2 or 4 character country codes for forward processing (39) // Fix Send P to multiple BBS's when routing on HR (40) // Rewrite PG server code on Lunux (41) +// Fix SendPToMultiple not stopping at Implied AT match (45) +// Log Our HA when checking for flood bulls (45) #include "bpqmail.h" #include "winstdint.h" diff --git a/BPQMail.sln b/BPQMail.sln deleted file mode 100644 index 8861280..0000000 --- a/BPQMail.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BPQMail", "BPQMail.vcproj", "{3766AA10-C777-4ED8-A83D-F1452DE9B665}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3766AA10-C777-4ED8-A83D-F1452DE9B665}.Debug|Win32.ActiveCfg = Debug|Win32 - {3766AA10-C777-4ED8-A83D-F1452DE9B665}.Debug|Win32.Build.0 = Debug|Win32 - {3766AA10-C777-4ED8-A83D-F1452DE9B665}.Release|Win32.ActiveCfg = Release|Win32 - {3766AA10-C777-4ED8-A83D-F1452DE9B665}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/BPQMail.vcproj.NOTTSDESKTOP.John.user b/BPQMail.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index fa82c00..0000000 --- a/BPQMail.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/BPQMail.vcproj.SKIGACER.johnw.user b/BPQMail.vcproj.SKIGACER.johnw.user deleted file mode 100644 index b5b0536..0000000 --- a/BPQMail.vcproj.SKIGACER.johnw.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user deleted file mode 100644 index 0cd9a72..0000000 --- a/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/BPQWinAPP.vcproj.NOTTSDESKTOP.John.user b/BPQWinAPP.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index fa82c00..0000000 --- a/BPQWinAPP.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/Bpq32.c b/Bpq32.c index d98b6ea..8f59fc9 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1212,7 +1212,6 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Add NodeAPI call sendLinks and remove get from other calls (32) // Improve validation of Web Beacon Config (33) // Support SNMP via host ip stack as well as IPGateway (34) - // Switch APRS Map to OSM tile servers (36) // Fix potential buffer overflow in Telnet login (36) // Allow longer serial device names (37) @@ -1222,6 +1221,12 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Change default of SECURETELNET to 1 (41) // Add optional ATTACH time limit for ARDOP (42) // Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) diff --git a/CBPQ32.suo b/CBPQ32.suo deleted file mode 100644 index b8ffa3c02960d9c230e0173679cbef6d767078e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5632 zcmeH~Ymkp+7{(uj*suyk#TFKe#kR<15sMtwZjO~*tgN+8>)eWrrY6cU%+O>rR7?&v zl+;Wo)2Gxpt&nNTis?hg4-L}?w(GLLHE(uzCZC!b_s%`f_5Z#1{XWn89PansqemK^ z-MY6~Nx05>g&N^XX`N6j!mCN!uUc#f)g}DT<9NWs+|)SYQ_J#HPMbKpP6g5p^EgSI}dIiaQJk! z8oK^jy|SQ8^;i(5hC%9mvQ+<2SVeZ_xJjs9&HcL;xve~^B31diY8hd^o_c!7SO50B zFhaj`&kbX#QdzfTMk@hJ{ zjj*B8J^|@4pP)Et+we%-2-s+tPv%(k1X}loM-Rn z&jMLmge`)th_Ln2k1K8?%oSuMalgQPpKN_%_~edL>-)RH{7T$Ln5)AkY1?L)W4B2A zIkv%Em5PYl3lj>-Dq&udUTU3ZTPE#2vLV7Y!uChl8R>Y%orR5*-bWuR^-#W_$G6H4 zC_4tTJ`O96uo7wadj;mI`Ls)`Y>8Um&%R39)o?Y;YjcA8ZHKvv?vS<}hI#&upo?i; zVNapY&^qT^XN>E*Chuy$grVe4RR#MZ+$!JPXFtO(Xe>^0aMFrn}!Y&$GfYzORPSTC_pU?*WI zVy9p~z~aQt!@B8}=(+NkyZiMh>k0GtQeh9n62f}vx!z`@ zXVN;ifViCz786@mv+Jw%`21qpMch!BbJL}588DA;w6wo+#=v~6)=B#w8(^+4nbNj_ zD#Gr=_J|9zY!J*LN2Oh3kHK6c1`{`5#oxL1B5Bu|EfMy*bb{FS2-_{~TDK>{K9zR; zI0%yrUr48^Jp^;k0%_Y}n1^u$b{^(h{fD%l@ghtzT+-*G>qc!2vix~tr2RbgU>VXE zxL-W1lUN&RTXKYTh4p~-*7X$h5ZYw1bZOf-nDuzr1Xz+-4lFkkHyM@(^SVxfJp=Q+ zEP=fW>nXMwwiPB6w!wD8d<;I4cFp@7=05fiR}AxfohI%Zn78 zmb?{VXQaJf>gzE3Ieeyz#B8xJ*Sd!2R - - - - - - - - - - diff --git a/CBPQ32.vcproj.NOTTSDESKTOP.John.user b/CBPQ32.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index 270b67b..0000000 --- a/CBPQ32.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/CBPQ32.vcproj.SKIGACER.johnw.user b/CBPQ32.vcproj.SKIGACER.johnw.user deleted file mode 100644 index f8a6101..0000000 --- a/CBPQ32.vcproj.SKIGACER.johnw.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/Cmd.c b/Cmd.c index 06f9295..60c4003 100644 --- a/Cmd.c +++ b/Cmd.c @@ -10,7 +10,7 @@ the Free Software Foundation, either version 3 of the License, or LinBPQ/BPQ32 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 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. S"paclenee the GNU General Public License for more details. You should have received a copy of the GNU General Public License @@ -66,6 +66,10 @@ VOID SaveMH(); BOOL RestartTNC(struct TNCINFO * TNC); void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); VOID WriteMiniDump(); +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive); +int seeifInterlockneeded(struct PORTCONTROL * PORT); + +extern VOID KISSTX(); char COMMANDBUFFER[81] = ""; // Command Hander input buffer char OrigCmdBuffer[81] = ""; // Command Hander input buffer before toupper @@ -171,7 +175,8 @@ VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); - +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); @@ -1196,11 +1201,12 @@ VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); +// Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); + Bufferptr = Cmdprintf(Session, Bufferptr, "Active(TX/Busy) %%"); for (i = 0; i < cols; i++) { - Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d", PORT->AVSENDING, PORT->AVACTIVE); + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d ", PORT->AVSENDING, PORT->AVACTIVE); PORT = PORT->PORTPOINTER; } Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); @@ -2295,8 +2301,8 @@ VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * C int TextCallLen; char PortString[10]; char cmdCopy[256]; - struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;; - + struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT; + char toCall[12], fromCall[12]; #ifdef EXCLUDEBITS @@ -2737,6 +2743,16 @@ noFlip: SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } + + ret = CheckKissInterlock(PORT, TRUE); + + if (ret) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Interlocked port %d is in use\r", ret); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44) { @@ -2822,6 +2838,12 @@ noFlip3: RESET2(LINK); // RESET ALL FLAGS + toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0; + fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0; + + hookL2SessionAttempt(CONNECTPORT, fromCall, toCall, LINK); + + if (CMD->String[0] == 'N' && SUPPORT2point2) LINK->L2STATE = 1; // New (2.2) send XID else @@ -2836,6 +2858,8 @@ noFlip3: if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM { + seeifInterlockneeded(PORT); + if (LINK->L2STATE == 1) L2SENDXID(LINK); else @@ -4166,7 +4190,7 @@ VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX - if (EXTPORT->ATTACHEDSESSIONS[sess]) + if (EXTPORT->ATTACHEDSESSIONS[sess] || PORT->PortSuspended) { // In use @@ -4434,6 +4458,7 @@ CMDX COMMANDS[] = "NAT ",3,SHOWNAT,0, "IPROUTE ",3,SHOWIPROUTE,0, "UZ7HO ",5,UZ7HOCMD,0, + "QTSM ",4,QTSMCMD,0, "..FLMSG ",7,FLMSG,0 }; @@ -4897,19 +4922,69 @@ VOID DoTheCommand(TRANSPORTENTRY * Session) VOID StatsTimer() { struct PORTCONTROL * PORT = PORTTABLE; - int sum; + uint64_t sum, sum2; + + // Interval is 60 secs while(PORT) { - sum = PORT->SENDING / 11; - PORT->AVSENDING = sum; + int index = PORT->StatsPointer++; - sum = (PORT->SENDING + PORT->ACTIVE) /11; - PORT->AVACTIVE = sum; + if (index == 1439) + PORT->StatsPointer = 0; // Cyclic through 24 hours (1440 Mins) + + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + if (TNC->Hardware == H_ARDOP || TNC->Hardware == H_VARA) + { + sum = TNC->PTTActivemS / 600; // ms but want % + PORT->AVSENDING = (UCHAR)sum; + TNC->PTTActivemS = 0; + + sum2 = TNC->BusyActivemS / 600; // ms but want % + PORT->AVACTIVE = (UCHAR)(sum + sum2); + TNC->BusyActivemS = 0; + } + } + else + { + // if KISS port using QtSM Average is already updated + + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + + if (PORT->PORTNUMBER == 17) + { + int x = 17; + } + + if (PORT->PORTTXROUTINE == KISSTX && (KISS->QtSMStats || KISS->FIRSTPORT->PORT.QtSMPort)) // KISS Port QtSM Stats + { + } + else + { + sum = PORT->SENDING / 11; + PORT->AVSENDING = (UCHAR)sum; + + sum = (PORT->SENDING + PORT->ACTIVE) /11; + PORT->AVACTIVE = (UCHAR)sum; + } + } + + if (PORT->TX == NULL && PORT->AVACTIVE) + { + PORT->TX = zalloc(1440); // Keep 1 day history + PORT->BUSY = zalloc(1440); + } + if (PORT->TX) + { + PORT->TX[index] = PORT->AVSENDING; + PORT->BUSY[index] = PORT->AVACTIVE; + } PORT->SENDING = 0; PORT->ACTIVE = 0; - + PORT = PORT->PORTPOINTER; } } @@ -5819,7 +5894,7 @@ VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * AGW = TNC->AGWInfo; if (TNC == 0 || AGW == 0) - { + { Bufferptr = Cmdprintf(Session, Bufferptr, "Error - %d is not UZ7HO port\r", port); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; @@ -5865,6 +5940,43 @@ VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * return; } +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + int port; + struct PORTCONTROL * PORT; + struct KISSINFO * KISS; + + CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version + + port = atoi(CmdTail); + + PORT = GetPortTableEntryFromPortNum(port); + + if (PORT == NULL || PORT->PORTTXROUTINE != KISSTX) // Must be a kiss like port + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d is not a KISS port\r", port); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *)PORT; + + if (KISS->QtSMModem == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d has no QtSM information\r", port); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Modem %s Centre frequency %d\r", + (KISS->QtSMModem) ? KISS->QtSMModem : "Not Available", KISS->QtSMFreq); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + diff --git a/CommonCode.c b/CommonCode.c index 48a253d..1b6352f 100644 --- a/CommonCode.c +++ b/CommonCode.c @@ -73,6 +73,7 @@ void SendDataToPktMap(char *Msg); extern BOOL LogAllConnects; extern BOOL M0LTEMap; +char * stristr (char *ch1, char *ch2); extern VOID * ENDBUFFERPOOL; @@ -547,6 +548,7 @@ VOID * _GetBuff(char * File, int Line) Msg->Process = (short)GetCurrentProcessId(); Msg->Linkptr = NULL; + Msg->Padding[0] = 0; // Used for modem status info } else Debugprintf("Warning - Getbuff returned NULL"); @@ -1669,7 +1671,7 @@ DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count) Stamp = MSG->Timestamp; - memcpy(msg, MSG, Msglen); + memcpy(msg, MSG, BUFFLEN - sizeof(void *)); // To c *len = Msglen; @@ -2454,8 +2456,8 @@ HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet struct termios term; struct speed_struct *s; - if ((UINT)pPort < 256) - sprintf(Port, "%s/com%d", BPQDirectory, (int)pPort); + if ((uintptr_t)pPort < 256) + sprintf(Port, "%s/com%d", BPQDirectory, (int)(uintptr_t)pPort); else strcpy(Port, pPort); @@ -3688,7 +3690,7 @@ VOID OpenReportingSockets() { // Enable Node Map Reports - ReportTimer = 60; + ReportTimer = 1200; // 2 mins - Give Rigcontrol time to start ReportSocket = socket(AF_INET,SOCK_DGRAM,0); @@ -5160,6 +5162,7 @@ void SendDataToPktMap(char *Msg) char * Use; char * Type; char * Modulation; + char * Usage; char locked[] = " ! "; int Percent = 0; @@ -5264,6 +5267,10 @@ void SendDataToPktMap(char *Msg) Type = "RF"; Bitrate = 0; Modulation = "FSK"; + Usage = "Access"; + + if (PORT->PortFreq) + Freq = PORT->PortFreq; if (PORT->PORTTYPE == 0) { @@ -5321,7 +5328,7 @@ void SendDataToPktMap(char *Msg) continue; } - if (TNC->RIG) + if (Freq == 0 && TNC->RIG) Freq = TNC->RIG->RigFreq * 1000000; switch (TNC->Hardware) // Hardware Type @@ -5387,6 +5394,15 @@ void SendDataToPktMap(char *Msg) break; + case H_KISSHF: + + // Try to get mode from ID then drop through + + if (stristr(PORT->PORTDESCRIPTION, "BPSK")) + { + Modulation = "BPSK"; + } + case H_WINMOR: case H_V4: @@ -5395,7 +5411,7 @@ void SendDataToPktMap(char *Msg) case H_UIARQ: case H_ARDOP: case H_VARA: - case H_KISSHF: + case H_FREEDATA: // TCP @@ -5431,12 +5447,58 @@ void SendDataToPktMap(char *Msg) while (*(ptr2) == ' ' && ptr2 != ID) *(ptr2--) = 0; + if (PORT->M0LTEMapInfo) + { + // Override with user configured values - RF,7.045,BPSK,300,300,Access + + char param[256]; + char *p1, *p2, *p3, *p4, *p5; + + strcpy(param, PORT->M0LTEMapInfo); + + p1 = strlop(param, ','); + p2 = strlop(p1, ','); + p3 = strlop(p2, ','); + p4 = strlop(p3, ','); + p5 = strlop(p4, ','); + + // int n = sscanf(PORT->M0LTEMapInfo, "%s,%s,%s,%s,%s,%s", &p1, &p2, &p3, &p4, &p5, &p6); + + if (p5) + { + if (param[0]) Type = param; + + if (p1[0]) + { + // if set to DIAL+=n and frequency set from config or rigcontrol modify it + + uint64_t offset = 0; + + if (_memicmp(p1, "DIAL+", 5) == 0) + offset = atoi(&p1[5]); + else if (_memicmp(p1, "DIAL-", 5) == 0) + offset = -atoi(&p1[5]); + else + Freq = atof(p1) * 1000000; + + if (Freq != 0) + Freq += offset; + + } + + if (p2[0]) Modulation = p2; + if (p3[0]) Baud = atoi(p3); + if (p4[0]) Bitrate = atoi(p4); + if (p5[0]) Usage = p5; + } + } + ptr += sprintf(ptr, "{\"id\": \"%d\",\"linkType\": \"%s\"," "\"freq\": \"%lld\",\"mode\": \"%s\",\"modulation\": \"%s\"," "\"baud\": \"%d\",\"bitrate\": \"%d\",\"usage\": \"%s\",\"comment\": \"%s\"},\r\n", PortNo, Type, Freq, Mode, Modulation, - Baud, Bitrate, "Access", ID); + Baud, Bitrate, Usage, ID); // G7TAJ // // make MH list to be added later diff --git a/Events.c b/Events.c index c7714ba..34ed64a 100644 --- a/Events.c +++ b/Events.c @@ -107,3 +107,16 @@ DllExport void APIENTRY RunEventProgram(char * Program, char * Param) return; } + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) +{ +} + +void hookL2SessionDeleted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK) +{ +} + +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) +{ +} + diff --git a/FreeDATA.c b/FreeDATA.c index 7b92f21..b2ae499 100644 --- a/FreeDATA.c +++ b/FreeDATA.c @@ -686,8 +686,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (buffptr == 0) return (0); // No buffers, so ignore - buffptr->Len = 36; - memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to TNC\r"); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); @@ -3215,7 +3214,7 @@ void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) } } - else if (memcmp(ptr, "close", 12) == 0) + else if (memcmp(ptr, "close", 5) == 0) { if (TNC->FreeDataInfo->arqstate != 4) { diff --git a/HTTPcode.c b/HTTPcode.c index e1ddfbe..1e4502c 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -1623,23 +1623,23 @@ int InnerProcessHTTPMessage(struct ConnectionInfo * conn) char * WebSock = 0; char PortsHddr[] = "

Ports

" - ""; + ""; - char PortLine[] = ""; +// char PortLine[] = ""; char PortLineWithBeacon[] = "" - "\r\n"; + "\r\n"; char SessionPortLine[] = "" - "\r\n"; + "\r\n"; char PortLineWithDriver[] = "" - "\r\n"; + "\r\n"; char PortLineWithBeaconAndDriver[] = "" "" - "\r\n"; + "\r\n"; char RigControlLine[] = "" "\r\n"; @@ -2707,6 +2707,53 @@ doHeader: return 0; } + else if (_memicmp(NodeURL, "/portstats.txt", 15) == 0) + { + char * Compressed; + char * ptr; + int port; + struct PORTCONTROL * PORT; + + ptr = &NodeURL[15]; + + port = atoi(ptr); + + PORT = GetPortTableEntryFromPortNum(port); + + ReplyLen = 0; + + if (PORT && PORT->TX) + { + // We send the last 24 hours worth of data. Buffer is cyclic so oldest byte is at StatsPointer + + int first = PORT->StatsPointer; + int firstlen = 1440 - first; + + memcpy(&_REPLYBUFFER[0], &PORT->TX[first], firstlen); + memcpy(&_REPLYBUFFER[firstlen], PORT->TX, first); + + memcpy(&_REPLYBUFFER[1440], &PORT->BUSY[first], firstlen); + memcpy(&_REPLYBUFFER[1440 + firstlen], PORT->BUSY, first); + + ReplyLen = 2880; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/Icon", 5) == 0 && _memicmp(&NodeURL[10], ".png", 4) == 0) { // APRS internal Icon @@ -2880,7 +2927,7 @@ doHeader: " {" " // The browser doesn't support WebSocket\r\n" " const div = document.getElementById('div');\r\n" - " div.innerHTML = 'WebSocket not supported by your Browser - RigControl Page not availble'\r\n" + " div.innerHTML = 'WebSocket not supported by your Browser - RigControl Page not availible'\r\n" " }" "}" "function PTT(p)" @@ -3235,6 +3282,7 @@ doHeader: int count; char DLL[20]; + char StatsURL[64]; ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PortsHddr); @@ -3243,6 +3291,13 @@ doHeader: Port = GetPortTableEntryFromSlot(count); ExtPort = (struct _EXTPORTDATA *)Port; + // see if has a stats page + + if (Port->AVACTIVE) + sprintf(StatsURL, " Stats Graph", Port->PORTNUMBER); + else + StatsURL[0] = 0; + if (Port->PORTTYPE == 0x10) { strcpy(DLL, ExtPort->PORT_DLL_NAME); @@ -3265,20 +3320,20 @@ doHeader: { if (Port->UICAPABLE) ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeaconAndDriver, Port->PORTNUMBER, DLL, - Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200); + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); else ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithDriver, Port->PORTNUMBER, DLL, - Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200); + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); continue; } if (Port->PORTTYPE == 16 && Port->PROTOCOL == 10 && Port->UICAPABLE == 0) // EXTERNAL, Pactor/WINMO ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], SessionPortLine, Port->PORTNUMBER, DLL, - Port->PORTDESCRIPTION, Port->PORTNUMBER); + Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); else ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeacon, Port->PORTNUMBER, Port->PORTNUMBER, - DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER); + DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); } if (RigActive) diff --git a/Housekeeping.c b/Housekeeping.c index 8188793..48ff1e7 100644 --- a/Housekeeping.c +++ b/Housekeeping.c @@ -343,12 +343,12 @@ VOID ExpireMessages() Killed = 0; - PRLimit = now - PR*86400; - PURLimit = now - PUR*86400; - PFLimit = now - PF*86400; - PNFLimit = now - PNF*86400; - BFLimit = now - BF*86400; - BNFLimit = now - BNF*86400; + PRLimit = now - (time_t)PR*86400; + PURLimit = now -(time_t)PUR*86400; + PFLimit = now - (time_t)PF*86400; + PNFLimit = now - (time_t)PNF*86400; + BFLimit = now - (time_t)BF*86400; + BNFLimit = now -(time_t) BNF*86400; if (NTSU == 0) { @@ -391,7 +391,7 @@ VOID ExpireMessages() if (Msg->datecreated < PURLimit) { if (SendNonDeliveryMsgs) - SendNonDeliveryMessage(Msg, TRUE, PUR); + SendNonDeliveryMessage(Msg, TRUE, (int)PUR); KillMsg(Msg); } @@ -401,7 +401,7 @@ VOID ExpireMessages() if (Msg->datecreated < PNFLimit) { if (SendNonDeliveryMsgs) - SendNonDeliveryMessage(Msg, FALSE, PNF); + SendNonDeliveryMessage(Msg, FALSE, (int)PNF); KillMsg(Msg); } diff --git a/KISSHF.c b/KISSHF.c index a8aeb41..93d0e43 100644 --- a/KISSHF.c +++ b/KISSHF.c @@ -50,8 +50,8 @@ int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); VOID SendInitScript(struct TNCINFO * TNC); int KISSHFGetLine(char * buf); int ProcessEscape(UCHAR * TXMsg); -VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC); -static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC, int Channel); +static int KissEncode(struct TNCINFO * TNC, UCHAR * inbuff, UCHAR * outbuff, int len, int Channel); int ConnecttoKISS(int port); TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr); BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls); @@ -265,6 +265,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) struct TNCINFO * TNC = TNCInfo[port]; struct STREAMINFO * STREAM = &TNC->Streams[0]; struct ScanEntry * Scan; + int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4; if (TNC == NULL) return 0; // Port not defined @@ -417,7 +418,10 @@ ok: if (buff->PID != 240) // ax.25 address { - txlen = KissEncode(&buff->PID, txbuff, txlen); + txlen = KissEncode(TNC, &buff->PID, txbuff, txlen, Channel); + + // We need to che check for ackmode + txlen = send(TNC->TCPSock, txbuff, txlen, 0); return 1; } @@ -504,7 +508,8 @@ ok: } } - if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + if ((toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + || (toupper(buff->L2DATA[0]) == 'N' && buff->L2DATA[1] == ' ' && txlen > 2)) // Connect { // Connect Command. Pass to L2 code to start session @@ -616,9 +621,9 @@ noFlip3: RESET2(LINK); // RESET ALL FLAGS -// if (CMD->String[0] == 'N' && SUPPORT2point2) -// LINK->L2STATE = 1; // New (2.2) send XID -// else + if (toupper(buff->L2DATA[0]) == 'N' && SUPPORT2point2) + LINK->L2STATE = 1; // New (2.2) send XID + else LINK->L2STATE = 2; // Send SABM LINK->CIRCUITPOINTER = NewSess; @@ -722,10 +727,16 @@ VOID KISSHFReleaseTNC(struct TNCINFO * TNC) VOID KISSHFSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = 1; + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } VOID KISSHFReleasePort(struct TNCINFO * TNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = 0; + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) @@ -735,9 +746,9 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) "function ScrollOutput()\r\n" "{var textarea = document.getElementById('textarea');" "textarea.scrollTop = textarea.scrollHeight;}" - "VARA Status" + "KISSHF Status" "

KISSHF Status" + "'width=440,height=50');\" action=ARDOPAbort?%d>KISSHF Status" "

", TNC->Port); @@ -746,14 +757,14 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); // Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); Len += sprintf(&Buff[Len], "
PortDriverIDBeaconsDriver Window
PortDriverIDBeaconsDriver WindowStats Graph
%d %s%s
%d %s%s
%d %s%s Beacons
 Beacons %s
%d%s%s
%s
%d%s%s Driver Window
Driver Window%s
%d%s%s BeaconsDriver Window
Driver Window%s
%d%s%s Rig Control
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); - Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); Len = DoScanLine(TNC, Buff, Len); return Len; @@ -976,6 +987,7 @@ VOID KISSThread(void * portptr) char * ptr1; char * ptr2; UINT * buffptr; + int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4; if (TNC->HostName == NULL) return; @@ -1184,7 +1196,7 @@ VOID KISSThread(void * portptr) if (FD_ISSET(TNC->TCPSock, &readfs)) { GetSemaphore(&Semaphore, 52); - KISSHFProcessReceivedPacket(TNC); + KISSHFProcessReceivedPacket(TNC, Channel); FreeSemaphore(&Semaphore); } @@ -1243,16 +1255,43 @@ static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) } +#define ACKMODE 4 // CAN USE ACK REQURED FRAMES +#define MSGHDDRLEN (USHORT)(sizeof(VOID *) + sizeof(UCHAR) + sizeof(USHORT)) +#define ONEMINUTE 60*3 -static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +static int KissEncode(struct TNCINFO * TNC, UCHAR * inbuff, UCHAR * outbuff, int len, int Channel) { int i,txptr=0; UCHAR c; - outbuff[0]=FEND; - outbuff[1]=0; + outbuff[0] = FEND; + outbuff[1] = Channel; txptr=2; + // See if we need ackmode + + if (TNC->PortRecord->PORTCONTROL.KISSFLAGS & ACKMODE) + { + UCHAR * ptr = inbuff - MSGHDDRLEN; + PMESSAGE Buffer = (PMESSAGE)ptr; + + if (Buffer->Linkptr) // Frame Needs ACK + { + UINT ACKWORD = (UINT)(Buffer->Linkptr - LINKS); + outbuff[1] |= 0x0c; // ACK OPCODE + outbuff[2] = ACKWORD & 0xff; + outbuff[3] = (ACKWORD >> 8) &0xff; + + Buffer->Linkptr->L2TIMER = ONEMINUTE; // Extend timeout + + txptr = 4; + + // have to reset flag so trace doesnt clear it + + Buffer->Linkptr = 0; + } + } + for (i=0;i 1) { - PMESSAGE Buff = GetBuff(); + PMESSAGE Buff; + + if ((TNC->ARDOPBuffer[1] & 0xf0) != Channel) + goto ignoreit; + + Buff = GetBuff(); MsgLen = KissDecode(TNC->ARDOPBuffer, Buffer, MsgLen); + // See if ACKMODE ACK + + if ((Buffer[1] & 0xf) == 12) + { + //Ackmode + + struct _LINKTABLE * LINK; + int ACKWORD = Buffer[2] | Buffer[3] << 8; + + if (ACKWORD < MAXLINKS) + { + LINK = LINKS + ACKWORD; + + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + } + + ReleaseBuffer(Buff); + goto ignoreit; + } + + if ((Buffer[1] & 0xf) != 0) + { + ReleaseBuffer(Buff); + goto ignoreit; // Not data + } + // we dont need the FENDS or control byte MsgLen -= 3; @@ -1354,6 +1425,8 @@ VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC) } } +ignoreit: + if (TNC->InputLen == 0) return; @@ -1463,7 +1536,7 @@ void DetachKISSHF(struct PORTCONTROL * PORT) struct STREAMINFO * STREAM = &TNC->Streams[0]; if (STREAM->Attached) - STREAM->ReportDISC = TRUE; // Tell Node + STREAM->ReportDISC = 10; // Tell Node but give time for error message to display STREAM->Connecting = FALSE; STREAM->Connected = FALSE; diff --git a/L2Code.c b/L2Code.c index 234e7d4..da2d7d7 100644 --- a/L2Code.c +++ b/L2Code.c @@ -106,6 +106,13 @@ void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer); void DetachKISSHF(struct PORTCONTROL * PORT); void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK); void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode); +int seeifInterlockneeded(struct PORTCONTROL * PORT); +int seeifUnlockneeded(struct _LINKTABLE * LINK); +int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive); +void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +void hookL2SessionDeleted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); + extern int REALTIMETICKS; @@ -773,163 +780,171 @@ VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buff VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) { - // I think it is fairly safe to accept XID as soon as we - // can process SREJ, but only accept Mod 8 and 256 Byte frames + // I think it is fairly safe to accept XID as soon as we + // can process SREJ, but only accept Mod 8 and 256 Byte frames - // I think the only way to run 2.2 Mod 8 is to preceed a - // SABM with XID, but others don't seem to agree! + // I think the only way to run 2.2 Mod 8 is to preceed a + // SABM with XID, but others don't seem to agree! - // Run through XID fields, changing any we don't like, - // then return an XID response + // Run through XID fields, changing any we don't like, + // then return an XID response - // Decode and process XID + // Decode and process XID - UCHAR * ptr = &ADJBUFFER->PID; - UCHAR * ptr1, * ptr2; - UCHAR TEMPDIGI[57]; - int n; + UCHAR * ptr = &ADJBUFFER->PID; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; - if (*ptr++ == 0x82 && *ptr++ == 0x80) - { - int Type; - int Len; - unsigned int value; - int xidlen = *(ptr++) << 8; - xidlen += *ptr++; - - // XID is set of Type, Len, Value n-tuples + // Check Interlock - should we also check exclude etc?. No, checked in L2FORUS - while (xidlen > 0) - { - Type = *ptr++; - Len = *ptr++; - - value = 0; - xidlen -= (Len + 2); - - while (Len--) - { - value <<=8; - value += *ptr++; - } - switch(Type) - { - case 2: //Bin fields - - break; - - case 3: - - if ((value & OPMustHave) != OPMustHave) - goto BadXID; - - if ((value & OPMod8) == 0) - goto BadXID; - - if ((value & OPSREJMult) == 0) - goto BadXID; - - - // Reply Mod 8 SREJMULTI - - value = OPMustHave | OPSREJMult | OPMod8; - ptr -=3; - *ptr++ = value >> 16; - *ptr++ = value >> 8; - *ptr++ = value; - - - break; - - case 6: //RX Size - - break; - - case 8: //RX Window - - break; - } - } - - // Send back as XID response - - LINK->L2STATE = 1; // XID received - LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID - LINK->L2TIME = PORT->PORTT1; - - LINK->LINKPORT = PORT; - - // save calls so we can match up SABM when it comes - - memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); - LINK->LINKCALL[6] &= 0x1e; // Mask SSID - - memcpy(LINK->OURCALL, Buffer->DEST, 7); - - LINK->OURCALL[6] &= 0x1e; // Mask SSID - - memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT - - if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address - { - // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK - - memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT - - ptr1 = &Buffer->ORIGIN[6]; // End of add - ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi - - while((*ptr1 & 1) == 0) // End of address bit - { - ptr1++; - memcpy(ptr2, ptr1, 7); - ptr2[6] &= 0x1e; // Mask Repeated and Last bits - ptr2 -= 7; - ptr1 += 6; - } - - // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY - - n = PORT->PORTMAXDIGIS; - - ptr1 = ptr2 + 7; // First in TEMPDIGIS - ptr2 = &LINK->DIGIS[0]; - - while (*ptr1) - { - if (n == 0) - { - // Too many for us - - CLEAROUTLINK(LINK); - ReleaseBuffer(Buffer); - return; - } - - memcpy(ptr2, ptr1, 7); - ptr1 += 7; - ptr2 += 7; - n--; - } - } - - ADJBUFFER->CTL = CTL | PFBIT; - -// Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT - - L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS - - // We need to save APPLMASK and ALIASPTR so following SABM connects to application - - LINK->APPLMASK = APPLMASK; - LINK->ALIASPTR = ALIASPTR; - - PUT_ON_PORT_Q(PORT, Buffer); - return; - } -BadXID: - L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); return; + } + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + + break; + + case 3: + + if ((value & OPMustHave) != OPMustHave) + goto BadXID; + + if ((value & OPMod8) == 0) + goto BadXID; + + if ((value & OPSREJMult) == 0) + goto BadXID; + + + // Reply Mod 8 SREJMULTI + + value = OPMustHave | OPSREJMult | OPMod8; + ptr -=3; + *ptr++ = value >> 16; + *ptr++ = value >> 8; + *ptr++ = value; + + + break; + + case 6: //RX Size + + break; + + case 8: //RX Window + + break; + } + } + + // Send back as XID response + + LINK->L2STATE = 1; // XID received + LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID + LINK->L2TIME = PORT->PORTT1; + + LINK->LINKPORT = PORT; + + // save calls so we can match up SABM when it comes + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + ReleaseBuffer(Buffer); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + } + } + + ADJBUFFER->CTL = CTL | PFBIT; + + // Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + // We need to save APPLMASK and ALIASPTR so following SABM connects to application + + LINK->APPLMASK = APPLMASK; + LINK->ALIASPTR = ALIASPTR; + + PUT_ON_PORT_Q(PORT, Buffer); + return; + } +BadXID: + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; } @@ -1100,13 +1115,22 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe TRANSPORTENTRY * Session; int CONERROR; - + + char toCall[12], fromCall[12]; + + if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE { L2SENDDM(PORT, Buffer, ADJBUFFER); return; } + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); if (LINK->L2STATE != 5) // Setup OK? @@ -1115,6 +1139,14 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe return; } + // See if need to Interlock non-sharable modes, eg ARDOP and VARA + + seeifInterlockneeded(PORT); + + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + + // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION if (APPLMASK == 0) @@ -1136,6 +1168,8 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; WriteConnectLog(fromCall, toCall, "AX.25"); } + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); L2SENDUA(PORT, Buffer, ADJBUFFER); @@ -3226,6 +3260,15 @@ VOID SENDFRMR(struct _LINKTABLE * LINK) VOID CLEAROUTLINK(struct _LINKTABLE * LINK) { + char toCall[12], fromCall[12]; + + toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0; + fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0; + + hookL2SessionDeleted(LINK->LINKPORT->PORTNUMBER, fromCall, toCall, LINK); + + seeifUnlockneeded(LINK); + CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS memset(LINK, 0, sizeof(struct _LINKTABLE)); @@ -3593,7 +3636,7 @@ VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY - InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION + InSession = Session->L4CROSSLINK; // TO INCOMMING SESSION CLEARSESSIONENTRY(Session); @@ -3960,3 +4003,141 @@ BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg) } return FALSE; } + + +int COUNTLINKS(int Port); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + + +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive) +{ + // This checks for interlocked kiss and other ports. Returns 1 if attach/connect not allowed + + // If Exclusive is not set allow connects on specified port up to l2limit, + + // If Exclusive is set also don't allow any connects on specified port. + + // Generally use Exclusive if locking a port that doesn't allow shared access, eg ARDOP, VARAus + + // Maybe only Exclusive is needed, and just check session mode ports. Sharing of KISS ports is controlled by USERS + + int Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + PORT = PORTTABLE; + + if (Exclusive) + { + while(PORT) + { + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + { + // See if port in use + + int n; + + for (n = 0; n <= 26; n++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[n]) + { + return TNC->Port; ; // Refuse Connect + } + } + } + } + PORT = PORT->PORTPOINTER; + } + } + return 0; // ok to connect +} + +int seeifInterlockneeded(struct PORTCONTROL * PORT) +{ + // Can we just call SuspendOtherPorts - it won't do any harm if already suspended + // No, at that needs a TNC Record, so duplicate code here + + int i; + int Interlock = PORT->PORTINTERLOCK; + struct TNCINFO * TNC; + + if (Interlock == 0) + return 0; // No locking + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == FALSE) + TNC->SuspendPortProc(TNC, TNC); + } + + return 0; +} + +int seeifUnlockneeded(struct _LINKTABLE * LINK) +{ + // We need to see if any other links are active on any interlocked KISS ports. If not, release the lock + + int i; + int links = 0; + + int Interlock; + struct TNCINFO * TNC; + struct PORTCONTROL * PORT = LINK->LINKPORT; + + if (PORT == NULL) + return 0; + + // Should only be called for KISS links, but just in case + + if (PORT->PORTTYPE > 12) // INTERNAL or EXTERNAL? + return 0; // Not KISS Port + + Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + + // Count all L2 links on interlocked KISS ports + + PORT = PORTTABLE; + + while(PORT) + { + if (PORT->PORTTYPE <= 12) // INTERNAL or EXTERNAL? + if (Interlock == PORT->PORTINTERLOCK) + links += COUNTLINKS(PORT->PORTNUMBER); + + PORT = PORT->PORTPOINTER; + } + + if (links > 1) // must be the one we are closing + return 0; // Keep lock + + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == TRUE) + TNC->ReleasePortProc(TNC, TNC); + } + + return 0; +} + + + + diff --git a/MBLRoutines.c b/MBLRoutines.c index e7ae622..87d3d8a 100644 --- a/MBLRoutines.c +++ b/MBLRoutines.c @@ -23,7 +23,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "bpqmail.h" -void SendMessageReadEvent(struct UserInfo * user, struct MsgInfo * Msg); +void SendMessageReadEvent(char * call, struct MsgInfo * Msg); VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len) diff --git a/MailNode.vcproj.NOTTSDESKTOP.John.user b/MailNode.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index 34131b7..0000000 --- a/MailNode.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/MailNode.vcproj.SKIGACER.johnw.user b/MailNode.vcproj.SKIGACER.johnw.user deleted file mode 100644 index b5b0536..0000000 --- a/MailNode.vcproj.SKIGACER.johnw.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/MailRouting.c b/MailRouting.c index ff6feca..5cc17e7 100644 --- a/MailRouting.c +++ b/MailRouting.c @@ -1317,6 +1317,10 @@ FULLHA: { int i = 0; + // Log My HA for debugging + + Logprintf(LOG_BBS, conn, '?', "Routing Trace. Check if reached correct area My HA is %s", HRoute); + // All elements of Helements must match Myelements while (MyElements[i] && HElements[i]) // Until one set runs out @@ -1409,6 +1413,9 @@ NOHA: // So if SendPtoMultiple is set I think I need to find the best depth then send to all with the same depth // If none are found on HA match drop through. + // But this means a message at the @BBS call will be forwarded without checking the Implied AT. So do that first + + if (SendPtoMultiple && Msg->type == 'P') { struct UserInfo * bestbbs = NULL; @@ -1425,6 +1432,16 @@ NOHA: { ForwardingInfo = bbs->ForwardingInfo; + // Check Implied AT + + if ((strcmp(ATBBS, bbs->Call) == 0)) // @BBS = BBS + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches implied AT %s", ATBBS, bbs->Call); + + CheckAndSend(Msg, conn, bbs); + return 1; + } + depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]); if (depth) diff --git a/Moncode.c b/Moncode.c index 6463b14..d807e21 100644 --- a/Moncode.c +++ b/Moncode.c @@ -210,7 +210,12 @@ KC6OAR*>ID: Port &= 0x7F; if ((((uint64_t)1 << (Port - 1)) & Mask) == 0) // Check MMASK + { + if (msg->Padding[0] == '[') + msg->Padding[0] = 0; + return 0; + } // We now pass Text format monitoring from non-ax25 drivers through this code @@ -535,6 +540,12 @@ KC6OAR*>ID: } } } + + if (msg->Padding[0] == '[') + Output += sprintf((char *)Output, " %s", msg->Padding); + + msg->Padding[0] = 0; + if (Info) { // We have an info frame diff --git a/RigControl.c b/RigControl.c index 4c32caa..6003e31 100644 --- a/RigControl.c +++ b/RigControl.c @@ -8941,7 +8941,7 @@ VOID FLRIGThread(struct RIGPORTINFO * PORT) setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); - if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest, sizeof(PORT->remoteDest)) == 0) { // // Connected successful diff --git a/SCSPactor.c b/SCSPactor.c index 798d5ce..6edf0ed 100644 --- a/SCSPactor.c +++ b/SCSPactor.c @@ -4297,16 +4297,22 @@ VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects - Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +// Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); } VOID PTCReleasePort(struct TNCINFO * TNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); if (TNC->UseAPPLCallsforPactor && TNC->RIG && TNC->RIG != &TNC->DummyRig @@ -4315,7 +4321,7 @@ VOID PTCReleasePort(struct TNCINFO * TNC) else sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); - Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +// Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); } diff --git a/SCSTracker.c b/SCSTracker.c index 016d893..9965925 100644 --- a/SCSTracker.c +++ b/SCSTracker.c @@ -109,6 +109,9 @@ VOID TRKSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); sprintf(STREAM->CmdSet, "\1\1\1IDSPTNC"); } @@ -117,6 +120,9 @@ VOID TRKReleasePort(struct TNCINFO * TNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); sprintf(STREAM->CmdSet, "\1\1\1I%s", TNC->NodeCall); } diff --git a/UZ7HODrv.c b/UZ7HODrv.c index f506495..b61aea1 100644 --- a/UZ7HODrv.c +++ b/UZ7HODrv.c @@ -350,12 +350,17 @@ VOID UZ7HOSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) return; TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); RegisterAPPLCalls(TNC, TRUE); } VOID UZ7HOReleasePort(struct TNCINFO * TNC) { TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + RegisterAPPLCalls(TNC, FALSE); } diff --git a/VARA.c b/VARA.c index f83adb1..7c6deef 100644 --- a/VARA.c +++ b/VARA.c @@ -258,6 +258,9 @@ static int ProcessLine(char * buf, int Port) *ptr = 0; } + else if (_memicmp(buf, "VARAAC", 6) == 0) + TNC->VaraACAllowed = atoi(&buf[7]); + else if (_memicmp(buf, "BW2300", 6) == 0) { TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning @@ -281,7 +284,8 @@ static int ProcessLine(char * buf, int Port) else if (_memicmp(buf, "FM9600", 5) == 0) TNC->DefaultMode = TNC->WL2KMode = 52; else if (standardParams(TNC, buf) == FALSE) - strcat(TNC->InitScript, buf); + strcat(TNC->InitScript, buf); + } return (TRUE); @@ -304,12 +308,31 @@ static SOCKADDR_IN rxaddr; static int addrlen=sizeof(sinx); +void doVarACSend(struct TNCINFO * TNC) +{ + int hdrlen; + int txlen = strlen(TNC->VARACMsg); + char txbuff[64]; + + txlen--; // remove cr + hdrlen = sprintf(txbuff, "%d ", txlen); + send(TNC->TCPDataSock, txbuff, hdrlen, 0); // send length + send(TNC->TCPDataSock, TNC->VARACMsg, txlen, 0); + + free (TNC->VARACMsg); + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + + TNC->VarACTimer = 0; + return ; +} static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { size_t datalen; PMSGWITHLEN buffptr; char txbuff[500]; - unsigned int bytes,txlen=0; + unsigned int bytes; + size_t txlen=0; size_t Param; HKEY hKey=0; struct TNCINFO * TNC = TNCInfo[port]; @@ -342,6 +365,14 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + if (TNC->VarACTimer) + { + TNC->VarACTimer--; + + if (TNC->VarACTimer == 0) + doVarACSend(TNC); + } + // G7TAJ's code to record activity for stats display if ( TNC->BusyFlags && CDBusy ) @@ -528,7 +559,45 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); txlen = (int)buffptr->Len; - memcpy(txbuff, buffptr->Data, txlen); + + if(TNC->VaraACMode || TNC->VaraModeSet == 0) + { + // Send in varac format - + + // 5 Hello, 15 de G8 BPQ + + buffptr->Data[txlen] = 0; // Null terminate + + STREAM->BytesTXed += txlen; + WritetoTrace(TNC, buffptr->Data, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buffptr->Data); + + TNC->VarACTimer = 10; // One second + return 0; + } + + + + else + memcpy(txbuff, buffptr->Data, txlen); + bytes = VARASendData(TNC, &txbuff[0], txlen); STREAM->BytesTXed += bytes; ReleaseBuffer(buffptr); @@ -565,8 +634,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (buffptr == 0) return (0); // No buffers, so ignore - buffptr->Len=36; - memcpy(buffptr->Data,"No Connection to VARA TNC\r", 36); + buffptr->Len = sprintf(buffptr->Data,"No Connection to VARA TNC\r"); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); @@ -595,11 +663,56 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (TNC->Streams[0].Connected) { + unsigned char txbuff[512]; + STREAM->PacketsSent++; - bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); - STREAM->BytesTXed += bytes; + if(TNC->VaraACMode == 0 && TNC->VaraModeSet == 1) + { + // Normal Send + + memcpy(txbuff, buff->L2DATA, txlen); + + bytes=send(TNC->TCPDataSock, txbuff, txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, buff->L2DATA, txlen); + return 0; + } + + // Send in varac format - len space data. No cr on end, but is implied + + // 5 Hello + + // I think we have to send a whole message (something terminated with a new line) + // may need to combine packets. Also I think we need to combine seqential sends + // (eg CTEXT and SID) + + + buff->L2DATA[txlen] = 0; // Null terminate + + STREAM->BytesTXed += txlen; WritetoTrace(TNC, buff->L2DATA, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buff->L2DATA); + + TNC->VarACTimer = 10; // One second + return 0; } else { @@ -608,7 +721,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) TNC->Streams[0].ReportDISC = TRUE; // Tell Node return 0; } - + // See if Local command (eg RADIO) if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) @@ -651,13 +764,13 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (buff->L2DATA[16] != 13) { PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; if (buffptr) { buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "VARA} OK\r"); - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); } return 0; } @@ -704,7 +817,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) TNC->Streams[0].Connecting = TRUE; // See if Busy - + if (InterlockedCheckBusy(TNC)) { // Channel Busy. Unless override set, wait @@ -712,7 +825,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (TNC->OverrideBusy == 0) { // Save Command, and wait up to 10 secs - + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); @@ -979,12 +1092,19 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) VOID VARASuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } VOID VARAReleasePort(struct TNCINFO * TNC) { + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; VARASendCommand(TNC, "LISTEN ON\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } @@ -1730,6 +1850,15 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active) + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } if (TNC->PTTMode) Rig_PTT(TNC, TRUE); @@ -1740,6 +1869,12 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) { // Debugprintf("PTT Off"); + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + if (TNC->PTTMode) Rig_PTT(TNC, FALSE); @@ -1760,6 +1895,8 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) TNC->BusyFlags |= CDBusy; TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->BusyonTime = GetTickCount(); + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); strcpy(TNC->WEB_CHANSTATE, "Busy"); @@ -1775,6 +1912,13 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) else strcpy(TNC->WEB_CHANSTATE, "Clear"); + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); TNC->WinmorRestartCodecTimer = time(NULL); return; @@ -1879,6 +2023,17 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) STREAM->ConnectTime = time(NULL); STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + if (TNC->VARACMsg) + free(TNC->VARACMsg); + + TNC->VaraACMode = 0; + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + if (TNC->VaraACAllowed == 0) + TNC->VaraModeSet = 1; // definitly not varaac + else + TNC->VaraModeSet = 0; // Don't know yet + strcpy(TNC->WEB_MODE, ""); if (strstr(Buffer, "2300")) @@ -1937,6 +2092,8 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + // Only allow VarAC mode for incomming sessions + ProcessIncommingConnectEx(TNC, Call, 0, (TNC->NetRomMode == 0), TRUE); SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; @@ -2125,7 +2282,6 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) if (TNC->NetRomTxLen) { - STREAM->PacketsSent++; bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); @@ -2137,6 +2293,9 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) } else { + TNC->VaraACMode = 0; + TNC->VaraModeSet = 1; // Don't allow connect to VaraAC + buffptr = GetBuff(); if (buffptr == 0) @@ -2592,6 +2751,45 @@ VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length) STREAM->BytesTXed, STREAM->BytesRXed,STREAM->BytesOutstanding); MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + // if VARAAC Mode, remove byte count from front and add cr + // could possibly be longer than buffer size + + if (TNC->VaraModeSet == 0) // Could be normal or VaraAC + { + unsigned char *ptr = memchr(Data, ' ', Length); // contains a space + + if (ptr) + { + int ACLen = atoi(Data); + int lenLen = (ptr - Data) + 1; + + if (ACLen == (Length - lenLen)) + TNC->VaraACMode = 1; // AC Mode + } + TNC->VaraModeSet = 1; // Know which mode + } + + if (TNC->VaraACMode) + { + char * lenp; + char * msg; + int len; + + lenp = Data; + msg = strlop(lenp, ' '); + + len = atoi(lenp); + if (len != strlen(msg)) + return; + + msg[len++] = 13; + msg[len] = 0; + + Length = len; + memmove(Data, msg, len + 1); + + } + // May need to fragment while (Length) diff --git a/Versions.h b/Versions.h index 332f2ea..a33d31e 100644 --- a/Versions.h +++ b/Versions.h @@ -10,14 +10,14 @@ #endif -#define KVers 6,0,24,42 -#define KVerstring "6.0.24.42\0" +#define KVers 6,0,24,45 +#define KVerstring "6.0.24.45\0" #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "August 2024" +#define Datestring "October 2024" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2024 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/WinRPRHelper.vcproj.NOTTSDESKTOP.John.user b/WinRPRHelper.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index fa82c00..0000000 --- a/WinRPRHelper.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user deleted file mode 100644 index 0cd9a72..0000000 --- a/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/WinmorControl.vcproj.NOTTSDESKTOP.John.user b/WinmorControl.vcproj.NOTTSDESKTOP.John.user deleted file mode 100644 index fa82c00..0000000 --- a/WinmorControl.vcproj.NOTTSDESKTOP.John.user +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/asmstrucs.h b/asmstrucs.h index 70696b5..f8b4d1e 100644 --- a/asmstrucs.h +++ b/asmstrucs.h @@ -600,6 +600,8 @@ typedef struct PORTCONTROL UCHAR AVSENDING; // LAST MINUTE UCHAR AVACTIVE; + char PktFlags[64]; // Decode stts rom QtSM + char PORTTYPE; // H/W TYPE // 0 = ASYNC, 2 = PC120, 4 = DRSI // 6 = TOSH, 8 = QUAD, 10 = RLC100 @@ -691,6 +693,13 @@ typedef struct PORTCONTROL time_t SmartIDInterval; // Smart ID Interval (Secs) int SendtoM0LTEMap; uint64_t PortFreq; // Configured freq + char * M0LTEMapInfo; + int QtSMPort; + BOOL QtSMConnected; + + int StatsPointer; + UCHAR * TX; // % Sending + UCHAR * BUSY; // % Active (Normally DCD active or TX) } PORTCONTROLX, *PPORTCONTROL; @@ -704,7 +713,7 @@ typedef struct FULLPORTDATA typedef struct KISSINFO { - struct PORTCONTROL PORT; + struct PORTCONTROL PORT; int LINKSTS; // CURRENT STATE UINT * CURALP; // CURRENT BUFFER @@ -735,6 +744,19 @@ typedef struct KISSINFO UCHAR * KISSCMD; // Commands to be sent when port opened int KISSCMDLEN; + int PTTMode; // PTT Mode Flags + int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; + + char * QtSMModem; + int QtSMFreq; + int QtSMStats; // Set if stats received as KISS Command + + // UCHAR WIN32INFO[16]; // FOR WINDOWS DRIVER } *PKISSINFO; diff --git a/cMain.c b/cMain.c index eb529f6..b2cc48d 100644 --- a/cMain.c +++ b/cMain.c @@ -46,6 +46,8 @@ int upnpInit(); void AISTimer(); void ADSBTimer(); VOID SendSmartID(struct PORTCONTROL * PORT); +int CanPortDigi(int Port); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); #include "configstructs.h" @@ -368,7 +370,8 @@ VOID EXTTX(PEXTPORTDATA PORTVEC, MESSAGE * Buffer) if (LINK->L2TIMER) LINK->L2TIMER = LINK->L2TIME; - Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + if (PORT->TNC == 0 || PORT->TNC->Hardware != H_KISSHF) + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER } PORTVEC->PORT_EXT_ADDR(2, PORT->PORTNUMBER, Buffer); @@ -1081,6 +1084,10 @@ BOOL Start() PORT->SendtoM0LTEMap = PortRec->SendtoM0LTEMap; PORT->PortFreq = PortRec->PortFreq; + PORT->M0LTEMapInfo = PortRec->M0LTEMapInfo; + + PORT->QtSMPort = PortRec->QtSMPort; + if (PortRec->BBSFLAG) // Appl 1 not permitted - BBSFLAG=NOBBS PORT->PERMITTEDAPPLS &= 0xfffffffe; // Clear bottom bit diff --git a/config.c b/config.c index 4c2c3fb..81586b6 100644 --- a/config.c +++ b/config.c @@ -365,7 +365,7 @@ static char *pkeywords[] = "BCALL", "DIGIMASK", "NOKEEPALIVES", "COMPORT", "DRIVER", "WL2KREPORT", "UIONLY", "UDPPORT", "IPADDR", "I2CBUS", "I2CDEVICE", "UDPTXPORT", "UDPRXPORT", "NONORMALIZE", "IGNOREUNLOCKEDROUTES", "INP3ONLY", "TCPPORT", "RIGPORT", "PERMITTEDAPPLS", "HIDE", -"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq"}; /* parameter keywords */ +"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq", "M0LTEMapInfo", "QTSMPort"}; /* parameter keywords */ static void * poffset[] = { @@ -379,7 +379,7 @@ static void * poffset[] = &xxp.BCALL, &xxp.DIGIMASK, &xxp.DefaultNoKeepAlives, &xxp.IOADDR, &xxp.DLLNAME, &xxp.WL2K, &xxp.UIONLY, &xxp.IOADDR, &xxp.IPADDR, &xxp.INTLEVEL, &xxp.IOADDR, &xxp.IOADDR, &xxp.ListenPort, &xxp.NoNormalize, &xxp.IGNOREUNLOCKED, &xxp.INP3ONLY, &xxp.TCPPORT, &xxp.RIGPORT, &xxp.PERMITTEDAPPLS, &xxp.Hide, -&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq}; /* offset for corresponding data in config file */ +&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq, &xxp.M0LTEMapInfo, &xxp.QtSMPort}; /* offset for corresponding data in config file */ static int proutine[] = { @@ -393,7 +393,7 @@ static int proutine[] = 0, 1, 2, 18, 15, 16, 2, 1, 17, 1, 1, 1, 1, 2, 2, 2, 1, 1, 19, 2, -1, 20, 1, 21}; /* routine to process parameter */ +1, 20, 1, 21, 22, 1}; /* routine to process parameter */ int PPARAMLIM = sizeof(proutine)/sizeof(int); @@ -2237,7 +2237,10 @@ int decode_port_rec(char * rec) cn = int64_value(poffset[i], value, rec); /* INTEGER VALUES */ break; - + case 22: + xxp.M0LTEMapInfo = _strdup(value); + cn = 1; + break; case 9: diff --git a/config.ini b/config.ini deleted file mode 100644 index cfefd65..0000000 --- a/config.ini +++ /dev/null @@ -1,40 +0,0 @@ -[NETWORK] -#network settings -tncport = 3004 - -[STATION] -#station settings -mycall = GM8BPQ-2 -mygrid = IO68VL - -[AUDIO] -#audio settings -rx = 8 -tx = 16 -txaudiolevel = 125 - -[RADIO] -#radio settings -radiocontrol = disabled -devicename = RIG_MODEL_NETRIGCTL -deviceport = COM99 -serialspeed = 19200 -pttprotocol = USB -pttport = COM99 -data_bits = 8 -stop_bits = 1 -handshake = None -rigctld_ip = 127.0.0.1 -rigctld_port = 0 - -[TNC] -#tnc settings -scatter = False -fft = False -narrowband = True -fmin = -50.0 -fmax = 50.0 -qrv = True -rxbuffersize = 16 -explorer = False - diff --git a/configstructs.h b/configstructs.h index 231cfd8..e68f7cd 100644 --- a/configstructs.h +++ b/configstructs.h @@ -79,6 +79,8 @@ struct PORTCONFIG unsigned char * KissParams; int SendtoM0LTEMap; uint64_t PortFreq; + char * M0LTEMapInfo; + int QtSMPort; }; struct ROUTECONFIG diff --git a/kiss.c b/kiss.c index 0be9043..678b091 100644 --- a/kiss.c +++ b/kiss.c @@ -44,6 +44,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses //#include //#include +#include + #ifdef MACBPQ #define NOI2C #endif @@ -84,6 +86,7 @@ int i2cPoll(struct PORTCONTROL * PORT, NPASYINFO npKISSINFO); #define FESC 0xDB #define TFEND 0xDC #define TFESC 0xDD +#define QTSMKISSCMD 7 #define STX 2 // NETROM CONTROL CODES #define ETX 3 @@ -112,7 +115,8 @@ int ConnecttoTCP(NPASYINFO ASY); int KISSGetTCPMessage(NPASYINFO ASY); VOID CloseKISSPort(struct PORTCONTROL * PortVector); int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); -void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr); +void processDRATSFrame(unsigned char * Message, int Len, void * sockptr); +VOID ConnecttoQtSM(struct PORTCONTROL * PORT); extern struct PORTCONTROL * PORTTABLE; extern int NUMBEROFPORTS; @@ -801,11 +805,14 @@ VOID KISSINIT(struct KISSINFO * KISS) PORTCONTROLX * PORT = (struct PORTCONTROL *)KISS; struct KISSINFO * FIRSTCHAN = NULL; - PORT->PORTINTERLOCK = 0; // CANT USE INTERLOCK ON KISS - if (PORT->CHANNELNUM == 0) PORT->CHANNELNUM = 'A'; + // As transition aid warn if Interlock is used on kiss port + + if (PORT->PORTINTERLOCK) + WritetoConsoleLocal("Interlock defined on KISS port - is this intended? "); + FIRSTCHAN = (struct KISSINFO *)CHECKIOADDR(PORT); // IF ANOTHER ENTRY FOR THIS ADDR // MAY BE CHANGED IN ANOTHER CHANNEL USED @@ -1510,7 +1517,7 @@ SeeifMore: // ACK FRAME. WE DONT SUPPORT ACK REQUIRED FRAMES AS A SLAVE - THEY ARE ONLY ACCEPTED BY TNCS struct _LINKTABLE * LINK; - UINT ACKWORD = Port->RXMSG[1] | Port->RXMSG[2] << 8; + int ACKWORD = Port->RXMSG[1] | Port->RXMSG[2] << 8; if (ACKWORD < MAXLINKS) { @@ -1524,6 +1531,66 @@ SeeifMore: if (Port->RXMSG[0] & 0x0f) // Not Data { + // See if QTSM Status Packet + + if ((Port->RXMSG[0] & 0x0f) == QTSMKISSCMD) + { + unsigned char * Msg = &Port->RXMSG[1]; + int Chan = Port->RXMSG[0] & 0xf0; + len--; + + Msg[len] = 0; + + while (KISS->OURCTRL != Chan) + { + KISS = KISS->SUBCHAIN; + + if (KISS == NULL) + goto SeeifMore; // SEE IF ANYTHING ELSE + } + + // ok, KISS now points to our port + + // Debugprintf("%d %x %s", PORT->PORTNUMBER, Port->RXMSG[0], Msg); + + if (memcmp(Msg, "STATS ", 6) == 0) + { + // Save busy + + int TX, DCD; + char * Msg1 = strlop(&Msg[6], ' '); + + TX = atoi(&Msg[6]); + if (Msg1) + { + DCD = atoi(Msg1); + + KISS->PORT.AVSENDING = TX; + KISS->PORT.AVACTIVE = DCD + TX; + + KISS->QtSMStats = 1; + } + } + else if (memcmp(Msg, "PKTINFO ", 8) == 0) + { + // Save State + + char * Msg1 = &Msg[8]; + + if (strlen(Msg) > 63) + Msg[63] = 0; + + strcpy(PORT->PktFlags, Msg1); + } + else + Debugprintf("Unknown Command %d %x %s", PORT->PORTNUMBER, Port->RXMSG[0], Msg); + + // Note status applies to NEXT data packet received + + goto SeeifMore; + } + + // If a reply to a manual KISS command(Session set and time not too long ago) // send reponse to terminal @@ -1618,6 +1685,8 @@ SeeifMore: PORT->RXERRORS++; Debugprintf("KISS Checksum Error"); + PORT->PktFlags[0] = 0; + goto SeeifMore; // SEE IF ANYTHING ELSE } len--; // Remove Checksum @@ -1630,7 +1699,12 @@ SeeifMore: KISS = KISS->SUBCHAIN; if (KISS == NULL) - goto SeeifMore; // SEE IF ANYTHING ELSE + { + // Unknown channel - clear any status info + + PORT->PktFlags[0] = 0; + goto SeeifMore; // SEE IF ANYTHING ELSE + } } // ok, KISS now points to our port @@ -1648,6 +1722,13 @@ SeeifMore: PutLengthinBuffer((PDATAMESSAGE)Buffer, len); // Needed for arm5 portability + if (PORT->PktFlags[0]) + strcpy(Buffer->Padding, PORT->PktFlags); + else + Buffer->Padding[0] = 0; + + PORT->PktFlags[0] = 0; + /* // Randomly drop packets @@ -1893,6 +1974,10 @@ VOID ConnecttoTCPThread(NPASYINFO ASY) if (KISS && KISS->KISSCMD && KISS->KISSCMDLEN) send(sock, KISS->KISSCMD, KISS->KISSCMDLEN, 0); + // Try to open Mgmt Port + + ConnecttoQtSM(&KISS->PORT); + continue; } else @@ -2030,3 +2115,264 @@ int KISSGetTCPMessage(NPASYINFO ASY) } return 0; } + +// Interface to QtSM Managmemt Interface + + +VOID QtSMThread(struct PORTCONTROL * PORT); + +VOID ConnecttoQtSM(struct PORTCONTROL * PORT) +{ + if (PORT && PORT->QtSMPort) + _beginthread(QtSMThread, 0, (void *)PORT); + + return ; +} + +VOID QtSMThread(struct PORTCONTROL * PORT) +{ + // This is the Managemt Interface in QtSM. It receives PTT ON/OFF msgs from QtSM and allows changing modem mode and freq. + // Also will collect link usage stats + + char Msg[255]; + int err, i, ret; + u_long param = 1; + BOOL bcopt = TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + SOCKET qtsmsock; + + struct sockaddr_in qtsmaddr; // For QtSM Management Session + + qtsmaddr.sin_family = AF_INET; + memcpy(&qtsmaddr.sin_addr.s_addr, &PORT->PORTIPADDR, 4); // Same as for KISS connection + qtsmaddr.sin_port = htons(PORT->QtSMPort); + + qtsmsock = 0; + qtsmsock = socket(AF_INET,SOCK_STREAM,0); + + if (qtsmsock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for QtSM Mgmt socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsoleLocal(Msg); + return; + } + + setsockopt(qtsmsock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(qtsmsock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(qtsmsock,(LPSOCKADDR) &qtsmaddr,sizeof(qtsmaddr)) == 0) + { + // + // Connected successful + // + + ioctl(qtsmsock, FIONBIO, ¶m); + PORT->QtSMConnected = TRUE; + } + else + { + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for QtSM Mgmt - error code = %d Port %d\r\n", + err, PORT->QtSMPort); + + WritetoConsoleLocal(Msg); + + + closesocket(qtsmsock); + + qtsmsock = 0; + PORT->QtSMConnected = FALSE; + return; + } + + PORT->QtSMConnected = TRUE; + + while (PORT->QtSMConnected) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(qtsmsock,&readfs); + FD_SET(qtsmsock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)qtsmsock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("QTSM Mgmt Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(qtsmsock, &readfs)) + { + char Buffer[512]; + + char * Line = Buffer; + char * Rest; + + int InputLen = recv(qtsmsock, Buffer, 500, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + goto Lost; + } + + Buffer[InputLen] = 0; + + while(Line && Line[0]) + { + Rest = strlop(Line, '\r'); + + + // Need to extract lines from data + + if (strcmp(Line, "Ok") == 0) + { + } + + else if (memcmp(Line, "PTT ", 4) == 0) + { + // PTT 43 OFF + + int Port = atoi(&Line[4]); + char * State = strlop(&Line[4], ' '); + + struct KISSINFO * KISS = (struct KISSINFO *)GetPortTableEntryFromPortNum(Port); + + if (strcmp(State, "ON") == 0) + { + KISS->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active) + + if (KISS->BusyonTime) + { + KISS->BusyActivemS += (GetTickCount() - KISS->BusyonTime); + KISS->BusyonTime = 0; + } + + // if (KISS->PTTMode) + // Rig_PTT(TNC, TRUE); + } + else if (strcmp(State, "OFF") == 0) + { + if (KISS->PTTonTime) + { + KISS->PTTActivemS += (GetTickCount() - KISS->PTTonTime); + KISS->PTTonTime = 0; + } + + // if (KISS->PTTMode) + // Rig_PTT(TNC, FALSE); + } + + } + else if (strcmp(Line, "Connected to QtSM") == 0) + { + char Msg[64]; + int Len; + + // We need tp send a QtSMPort message for each Channel sharing thia connection + // Note struct KISSINFO and struct PORTCONTROL are different mappings of the same data + + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + + while (KISS) + { + Len = sprintf(Msg, "QtSMPort %d %d\r", KISS->PORT.CHANNELNUM - '@', KISS->PORT.PORTNUMBER); + send(qtsmsock, Msg, Len, 0); + + Len = sprintf(Msg, "Modem %d\r", KISS->PORT.CHANNELNUM - '@'); + send(qtsmsock, Msg, Len, 0); + + KISS = KISS->SUBCHAIN; + } + } + else if (memcmp(Line, "Port ", 5) == 0) + { + // Port 1 Freq 1500 Modem BPSK AX.25 300bd + + int Port, chan, Freq; + char * Modem; + struct KISSINFO * KISS; + + Port = atoi(&Line[5]); + Line = strlop(&Line[9], ' '); + chan = atoi(Line); + Freq = atoi(&Line[6]); + Modem = strlop(&Line[13], ' '); + + KISS = (struct KISSINFO *)GetPortTableEntryFromPortNum(Port); + + if (KISS->QtSMModem) + free(KISS->QtSMModem); + + KISS->QtSMModem = _strdup(Modem); + KISS->QtSMFreq = Freq; + } + + else if (memcmp(Line, "XXSTATS ", 6) == 0) + { + // STATS PORT PTT BUSY + + int Port, PTT, Busy; + struct PORTCONTROL * MPORT; + + Port = atoi(&Line[6]); + Line = strlop(&Line[6], ' '); + PTT = atoi(Line); + Line = strlop(Line, ' '); + Busy = atoi(Line); + + MPORT = GetPortTableEntryFromPortNum(Port); + + if (MPORT) + { + MPORT->AVACTIVE = Busy + PTT; + MPORT->AVSENDING = PTT; + } + } + else + Debugprintf("Unexpected QtSM Message %s", Line); + + Line = Rest; + } + } + + if (FD_ISSET(qtsmsock, &errorfs)) + { +Lost: + sprintf(Msg, "QtSM Mgmt Connection lost for TCP Port %d Port %d\r\n", PORT->QtSMPort, PORT->PORTNUMBER); + WritetoConsoleLocal(Msg); + + PORT->QtSMConnected = FALSE; + + closesocket(qtsmsock); + qtsmsock = 0; + return; + } + continue; + } + else + { + } + } + + sprintf(Msg, "QtSM Mgmt Thread Terminated TCP Port %d Port %d\r\n", PORT->QtSMPort, PORT->PORTNUMBER); + WritetoConsoleLocal(Msg); +} + + + + + diff --git a/linether.c b/linether.c index bede180..806de46 100644 --- a/linether.c +++ b/linether.c @@ -67,7 +67,7 @@ static BOOL ReadConfigFile(int Port); static int ProcessLine(char * buf,int Port, BOOL CheckPort); int WritetoConsoleLocal(char * buff); -int ExtProc(int fn, int port,unsigned char * buff) +static size_t ExtProc(int fn, int port, PMESSAGE buff) { int len,txlen=0,res; char txbuff[500]; @@ -110,9 +110,8 @@ int ExtProc(int fn, int port,unsigned char * buff) len-=3; - memcpy(&buff[7],&rxbuff[19],len); - - len+=5; + memcpy(&buff->DEST, &rxbuff[19], len); + len += (1 + sizeof(void *)); } else { @@ -121,14 +120,11 @@ int ExtProc(int fn, int port,unsigned char * buff) if ((len < 16) || (len > 320)) return 0; // Probably RLI Mode Frame len-=3; - - memcpy(&buff[7],&rxbuff[16],len); - - len+=5; + memcpy(&buff->DEST, &rxbuff[16], len); + len += (1 + sizeof(void *)); } - buff[5]=(len & 0xff); - buff[6]=(len >> 8); + PutLengthinBuffer((PDATAMESSAGE)buff, len); return 1; @@ -140,9 +136,8 @@ int ExtProc(int fn, int port,unsigned char * buff) // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 { - txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes - - txlen-=2; + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); // 2 for CRC + txlen -= (sizeof(void *) - 2); txbuff[16]=0x41; txbuff[17]=(txlen & 0xff); txbuff[18]=(txlen >> 8); @@ -150,14 +145,14 @@ int ExtProc(int fn, int port,unsigned char * buff) if (txlen < 1 || txlen > 400) return 0; - memcpy(&txbuff[19],&buff[7],txlen); + memcpy(&txbuff[19], &buff->DEST[0], txlen); + } else { - txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes - - txlen-=2; + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); // 2 for CRC + txlen -= (sizeof(void *) - 2); txbuff[14]=(txlen & 0xff); txbuff[15]=(txlen >> 8); @@ -166,7 +161,7 @@ int ExtProc(int fn, int port,unsigned char * buff) return 0; - memcpy(&txbuff[16],&buff[7],txlen); + memcpy(&txbuff[16], &buff->DEST[0], txlen); } memcpy(&txbuff[0], &IF->EthDest[0],6); @@ -210,7 +205,7 @@ int ExtProc(int fn, int port,unsigned char * buff) } -UINT ETHERExtInit(struct PORTCONTROL * PortEntry) +void * ETHERExtInit(struct PORTCONTROL * PortEntry) { // Can have multiple ports, each mapping to a different Ethernet Adapter @@ -299,7 +294,7 @@ UINT ETHERExtInit(struct PORTCONTROL * PortEntry) // n=sprintf(buf,"Using %s Adapter = Interface %d\r", ifr.ifr_ifindex); // WritetoConsole(buf); - return ((int) ExtProc); + return (ExtProc); } diff --git a/nodeapi.c b/nodeapi.c index 395f9f9..c5070e7 100644 --- a/nodeapi.c +++ b/nodeapi.c @@ -158,13 +158,12 @@ int APIProcessHTTPMessage(char * response, char * Method, char * URL, char * req int request_token(char * response) { Token * token = generate_token(); - int expires_in = 3600; char scope[] = "create"; printf("Token generated: %s\n", token->token); - sprintf(response, "{\"access_token\":\"%s\", \"expires_in\":%d, \"scope\":\"create\"}\r\n", - token->token, expires_in); + sprintf(response, "{\"access_token\":\"%s\", \"expires_at\":%ld,\"scope\":\"create\"}\r\n", + token->token, token->expiration_time); return strlen(response); } diff --git a/tncinfo.h b/tncinfo.h index bd0639e..db29429 100644 --- a/tncinfo.h +++ b/tncinfo.h @@ -532,6 +532,8 @@ typedef struct TNCINFO BOOL TNCCONNECTING; // For FreeData BOOL TNCCONNECTED; + BOOL QtSMConnected; + char NodeCall[10]; // Call we listen for (PORTCALL or NODECALL char CurrentMYC[10]; // Save current call so we don't change it unnecessarily char * LISTENCALLS; // Calls TNC will respond to (currently only for VARA) @@ -543,6 +545,11 @@ typedef struct TNCINFO int PTTMode; // PTT Mode Flags int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; char PTTOn[60]; // Port override of RIGCONTROL config char PTTOff[60]; @@ -847,7 +854,12 @@ typedef struct TNCINFO int NRCloseTimer; struct _LINKTABLE * DummyLink; // Simulated link to simplify interface to ax,25 netrom code struct sixPackPortInfo * sixPack; - int VaraACMode; + int VaraACAllowed; // Set by config + int VaraACMode; // Set by first message received + int VaraModeSet; // Have decicded if VarAC mode or not + char * VARACMsg; // to build message from packets + int VarACTimer; // delayed send timer + size_t VARACSize; // malloc'ed size } *PTNCINFO; diff --git a/upnp.c.bak b/upnp.c.bak deleted file mode 100644 index cdc68a4..0000000 --- a/upnp.c.bak +++ /dev/null @@ -1,187 +0,0 @@ -// Includes code from MiniUPnPc, used subject to the following conditions: - -/* - -MiniUPnPc -Copyright (c) 2005-2020, Thomas BERNARD -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#define MINIUPNP_STATICLIB - -#include -#ifdef _WIN32 -#include "upnpcommands.h" -#include "miniupnpc.h" -#include "upnperrors.h" -#include -#else -#include -#include -#include -#include -#endif - -int AddMap(char * controlURL, char * eport, char * iport, char * proto); -int DeleteMap(char * controlURL, char * eport, char * iport, char * proto); - -void Consoleprintf(const char * format, ...); - -struct UPNP -{ - struct UPNP * Next; - char * Protocol; - char * LANport; - char * WANPort; -}; - -extern struct UPNP * UPNPConfig; - -char * controlURL = 0; -char * servicetype = 0; -char iaddr[] = "IP"; -char * inClient = NULL; -#ifdef LINBPQ -char desc[] = "LinBPQ "; -#else -char desc[] = "BPQ32 "; -#endif -char * remoteHost = NULL; -char * leaseDuration = NULL; - -struct UPNPDev * devlist = 0; -char lanaddr[64] = "unset"; /* my ip address on the LAN */ -struct UPNPUrls urls; -struct IGDdatas data; - -int i; -const char * rootdescurl = 0; -const char * multicastif = 0; -const char * minissdpdpath = 0; -int localport = UPNP_LOCAL_PORT_ANY; -int retcode = 0; -int error = 0; -int ipv6 = 0; -int ignore = 0; -unsigned char ttl = 2; - - -int upnpInit() -{ - struct UPNP * Config = UPNPConfig; - int i; -#ifdef WIN32 - WSADATA wsaData; - int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); - if(nResult != NO_ERROR) - { - fprintf(stderr, "WSAStartup() failed.\n"); - return -1; - } -#endif - - while (Config) - { - if (devlist == NULL) - { - devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - - if (devlist == NULL) - { - Consoleprintf("Failed to find a UPNP device"); - return 0; - } - - i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - } - - AddMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol); - Config = Config->Next; - } - - return 0; -} - -int upnpClose() -{ - struct UPNP * Config = UPNPConfig; - int i; - - while (Config) - { - if (devlist == NULL) - { - devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - - if (devlist == NULL) - { - Consoleprintf("Failed to find a UPNP device"); - return 0; - } - - i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); - } - - DeleteMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol); - Config = Config->Next; - } - - return 0; -} - -int AddMap(char * controlURL, char * eport, char * iport, char * proto) -{ - int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - eport, iport, lanaddr, desc, - proto, remoteHost, leaseDuration); - - if (r != UPNPCOMMAND_SUCCESS) - { - Consoleprintf("UPNP AddPortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r)); - return -2; - } - Consoleprintf("UPNP AddPortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r); - return 0; -} - -int DeleteMap(char * controlURL, char * eport, char * iport, char * proto) -{ - int r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport, proto, remoteHost); - - if(r != UPNPCOMMAND_SUCCESS) - { - Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r)); - return -2; - } - Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r); - - return 0; -} - - - -