From 1f40ff4a07793ecfa679a1384053451c4f3f536f Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Tue, 22 Jul 2025 20:56:59 +0100 Subject: [PATCH] New upstream version 6.0.24.77+repack --- AGWAPI.c | 60 +- AGWMoncode.c | 92 +- ARDOP.c | 38 +- BBSHTMLConfig.c | 30 +- BBSUtilities.c | 128 +- BPQINP3.c | 577 +++++-- BPQMail.aps | Bin 0 -> 92636 bytes BPQMail.c | 17 +- BPQMail.rc | 1090 +++++-------- BPQRemotePTT.cfg | 12 - Bpq32.c | 29 +- CMSAuth.c | 3 +- ChatHTMLConfig.c | 4 +- Cmd.c | 147 +- CommonCode.c | 28 +- Events.c | 6 + FBBRoutines.c | 193 ++- FLDigi64.c | 3860 ++++++++++++++++++++++++++++++++++++++++++++ FreeDATA.c | 5 + HALDriver64.c | 1907 ++++++++++++++++++++++ HFCommon.c | 5 +- HTTPcode.c | 100 +- IPCode.c | 9 +- KAMPactor.c | 211 ++- L2Code.c | 464 +++++- L3Code.c | 68 +- L4Code.c | 124 +- LinBPQ.c | 56 +- MHSave.txt | 0 MULTIPSK64.c | 1543 ++++++++++++++++++ Moncode.c | 19 + MultiConsole.c | 3 + RHP.c | 51 +- RTKnown.txt | 0 RigControl.c | 1 + SCSTrackeMulti64.c | 1714 ++++++++++++++++++++ TNCCode.c | 5 +- TNCEmulators.c | 10 +- TelnetV6.c | 129 +- UZ7HODrv.c | 5 + VARA.c | 1 - Versions.h | 7 +- WINMOR.c | 20 +- WPRoutines.c | 1 + WebMail.c | 227 ++- WinRPR.c | 3 - asmstrucs.h | 38 +- bpq32.h | 2 + bpqaxip.c | 51 +- bpqmail.h | 10 +- cMain.c | 36 + cheaders.h | 7 +- config.c | 21 +- configstructs.h | 6 + kiss.c | 2 +- linether.c | 24 +- lzhuf32.c | 2 +- makefile | 36 +- mqtt.c | 2 +- templatedefs.c | 3 +- upnp.c | 18 +- 61 files changed, 11979 insertions(+), 1281 deletions(-) create mode 100644 BPQMail.aps delete mode 100644 BPQRemotePTT.cfg create mode 100644 FLDigi64.c create mode 100644 HALDriver64.c delete mode 100644 MHSave.txt create mode 100644 MULTIPSK64.c delete mode 100644 RTKnown.txt create mode 100644 SCSTrackeMulti64.c diff --git a/AGWAPI.c b/AGWAPI.c index dcf3494..126a72a 100644 --- a/AGWAPI.c +++ b/AGWAPI.c @@ -36,7 +36,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses struct AGWHeader { - unsigned int Port; + uint8_t Port; + uint8_t filler1[3]; unsigned char DataKind; unsigned char filler2; unsigned char PID; @@ -128,7 +129,7 @@ int DataSocket_Write(struct AGWSocketConnectionInfo * sockptr, SOCKET sock); int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr); int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr); int SendDataToAppl(int Stream, byte * Buffer, int Length); -int InternalAGWDecodeFrame(char * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int doNodes); +int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int doNodes); int AGWDataSocket_Disconnect( struct AGWSocketConnectionInfo * sockptr); int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length); int ShowApps(); @@ -720,7 +721,7 @@ int AGWDoMonitorData() RawLen = monbuff->LENGTH; - if (RawLen < 7 || RawLen > 350) + if (RawLen < MSGHDDRLEN || RawLen > 350) { ReleaseBuffer(monbuff); FreeSemaphore(&Semaphore); @@ -734,12 +735,16 @@ int AGWDoMonitorData() ReleaseBuffer(monbuff); FreeSemaphore(&Semaphore); + + // Set monbuff to point to the copy + + monbuff = (MESSAGE *)Buffer; //' 4 byte chain //' 1 byte port - top bit = transmit //' 2 byte length (LO-HI) - Port = Buffer[4]; + Port = monbuff->PORT; if (Port > 127) { @@ -751,6 +756,12 @@ int AGWDoMonitorData() RXFlag = TRUE; } + if (Port == 0) + { + Debugprintf("AGWMON Port number is zero"); + return 0; + } + // Can now have different mon flags per connection, so need to run decode for each socket for (n = 1; n<= CurrentSockets; n++) @@ -759,7 +770,7 @@ int AGWDoMonitorData() if (sockptr->SocketActive && sockptr->MonFlag && (RXFlag || LoopMonFlag)) { - Length = InternalAGWDecodeFrame(Buffer, AGWBuffer, Stamp, &Frametype, sockptr->useLocalTime, sockptr->doNodes); + Length = InternalAGWDecodeFrame(monbuff, AGWBuffer, Stamp, &Frametype, sockptr->useLocalTime, sockptr->doNodes); if (Length > 0) { @@ -803,7 +814,7 @@ int AGWDoMonitorData() } } - RawLen = RawLen - 6; + RawLen = RawLen - (MSGHDDRLEN - 1); // One more for KISS control if (RXFlag || Loopflag) // Send transmitted frames if requested { @@ -811,8 +822,12 @@ int AGWDoMonitorData() // // Send raw data to any sockets that have requested Raw frames // - - Buffer[6]=0; + + // Format is ax.25 packet prceeded by a KISS command byte 00 for channel 1 0x10 for channel 2 etc + // As this is an application API I think all should go as Port 1 + + + Buffer[MSGHDDRLEN - 1] = 0; // Just in case big-endian AGWTXHeader.Port = Port - 1; // AGW Ports start from 0 AGWTXHeader.DataKind = 'K'; @@ -824,14 +839,12 @@ int AGWDoMonitorData() sockptr=&Sockets[n]; if (sockptr->SocketActive && sockptr->RawFlag) - SendRawPacket(sockptr, &Buffer[6], RawLen); + SendRawPacket(sockptr, &Buffer[MSGHDDRLEN - 1], RawLen); } } } - - return 0; - + return 0; } int DeleteConnection(struct BPQConnectionInfo * Con) @@ -1128,6 +1141,7 @@ int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock) int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) { int AGWVersion[2]={2003,999}; + byte AGWPortCaps[12] = { 0, 255, 30, 10, 63, 10, 4, 0, 1, 0, 0, 0 }; char AGWRegReply[1]; struct BPQConnectionInfo * Connection; int Stream; @@ -1195,7 +1209,7 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) // Need to convert port index (used by AGW) to port number - conport=GetPortNumber(VisiblePortToRealPort[key[0]-48]); + conport=GetPortNumber(VisiblePortToRealPort[key[0]-49] + 1); n = sprintf(ConnectMsg,"C %d %s",conport,ToCall); @@ -1293,9 +1307,7 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) // Version memset(&AGWTXHeader,0,36); - AGWTXHeader.DataKind = 'R'; - AGWTXHeader.DataLength = 8; // Length SendtoSocket(sockptr->socket, (char *)&AGWVersion[0]); @@ -1309,15 +1321,27 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) memset(&AGWTXHeader,0,36); - AGWTXHeader.DataKind = 'G'; - AGWTXHeader.DataLength =(int)strlen(AGWPorts)+1; // Length SendtoSocket(sockptr->socket, AGWPorts); return 0; + + case 'g': + + // Port capabilities. Currently hard-coded. + + AGWTXHeader.Port = sockptr->AGWRXHeader.Port; + AGWTXHeader.DataKind = 'g'; + AGWTXHeader.DataLength = 12; + + SendtoSocket(sockptr->socket, (char *)&AGWPortCaps[0]); + + return 0; + + case 'k': @@ -1416,6 +1440,8 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) AGWTXHeader.DataKind = 'X'; + memcpy(&AGWTXHeader.callfrom, RegCall, 10); + AGWTXHeader.DataLength = 1; // Length AGWRegReply[0] = 1; diff --git a/AGWMoncode.c b/AGWMoncode.c index 7427804..2c26191 100644 --- a/AGWMoncode.c +++ b/AGWMoncode.c @@ -49,9 +49,14 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #define DM 0x0F #define UA 0x63 #define FRMR 0x87 +#define XID 0xAF +#define TEST 0xE3 #define RR 1 #define RNR 5 #define REJ 9 +#define SREJ 0x0D +#define SABME 0x6F + #define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE @@ -261,6 +266,18 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra strcpy(SUP, "FRMR"); FRMRFLAG = 1; break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; } Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); @@ -270,7 +287,7 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra // Super int NR = (CTL >> 5) & 7; - char SUP[4] = "??"; + char SUP[5] = "??"; switch (CTL & 0x0F) { @@ -288,6 +305,13 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra strcpy(SUP, "REJ"); break; + + + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); @@ -300,6 +324,72 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra if (FRMRFLAG) Output += sprintf((char *)Output, "%02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + 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 + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + + case 16: + + Output += sprintf((char *)Output, " Can Compress"); + break; + + case 17: + + Output += sprintf((char *)Output, " Compress ok"); + break; + } + } + } + } + + + if (Info) { // We have an info frame diff --git a/ARDOP.c b/ARDOP.c index a46fe3e..ade0ecf 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -888,6 +888,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) { + Debugprintf("ARDOP closing session on SessionTimelimit"); ARDOPSendCommand(TNC, "DISCONNECT", TRUE); STREAM->ReportDISC = 1; STREAM->AttachTime = 0; @@ -900,6 +901,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) { + Debugprintf("ARDOP closing session on AttachTimelimit"); STREAM->ReportDISC = 1; STREAM->AttachTime = 0; } @@ -2059,21 +2061,12 @@ VOID * ARDOPExtInit(EXTPORTDATA * PortEntry) strcat(TempScript, "ARQTIMEOUT 90\r"); // strcat(TempScript, "ROBUST False\r"); - strcat(TempScript, TNC->InitScript); - - free(TNC->InitScript); - TNC->InitScript = TempScript; - - // Set MYCALL - -// strcat(TNC->InitScript,"FECRCV True\r"); -// strcat(TNC->InitScript,"AUTOBREAK True\r"); + // Make MYAUX and MYCALL overridable + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); - strcat(TNC->InitScript, Msg); -// strcat(TNC->InitScript,"PROCESSID\r"); -// strcat(TNC->InitScript,"CODEC TRUE\r"); -// strcat(TNC->InitScript,"LISTEN TRUE\r"); + strcat(TempScript, Msg); + for (i = 0; i < 32; i++) { @@ -2097,9 +2090,25 @@ VOID * ARDOPExtInit(EXTPORTDATA * PortEntry) if (strlen(Aux) > 8) { Aux[strlen(Aux) - 1] = '\r'; - strcat(TNC->InitScript, Aux); + strcat(TempScript, Aux); } + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); +// strcat(TNC->InitScript,"LISTEN TRUE\r"); + + + strcpy(TNC->CurrentMYC, TNC->NodeCall); if (TNC->WL2K == NULL) @@ -3141,6 +3150,7 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) // Incoming Connect TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + STREAM->AttachTime = time(NULL); // Stop other ports in same group diff --git a/BBSHTMLConfig.c b/BBSHTMLConfig.c index 6207298..027c786 100644 --- a/BBSHTMLConfig.c +++ b/BBSHTMLConfig.c @@ -117,6 +117,7 @@ struct UserInfo * FindBBS(char * Name); void ReleaseWebMailStruct(WebMailInfo * WebMail); VOID TidyWelcomeMsg(char ** pPrompt); int MailAPIProcessHTTPMessage(struct HTTPConnectionInfo * Session, char * response, char * Method, char * URL, char * request, BOOL LOCAL, char * Param, char * Token); +void UndoTransparency(char * input); char UNC[] = ""; char CHKD[] = "checked=checked "; @@ -186,7 +187,7 @@ char RefreshMainPage[] = "" char StatusPage [] = "
" -"
User     Callsign   Stream Queue
" +"
User     Callsign   Stream  Queue  Sent  Rxed
" "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID FLDIGISuspendPort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = FALSE; +} + +VOID FLDIGIReleasePort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = TRUE; +} + +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg) +{ + int txlen, rc; + char txbuff[256]; + char outbuff[256]; + + txlen = sprintf(txbuff, "%c%s", 6, Msg); + txlen = KissEncode(txbuff, outbuff, txlen); + rc = sendto(TNC->TCPDataSock, outbuff, txlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); +} + +VOID * FLDigiExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // The Socket to connect to is in IOBASE + // + + srand((unsigned int)time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + TNC->FLInfo->CONOK = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0 || PortEntry->PORTCONTROL.PORTPACLEN > 128) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + TNC->SuspendPortProc = FLDIGISuspendPort; + TNC->ReleasePortProc = FLDIGIReleasePort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_FLDIGI; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"FLDigi Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + +#ifndef LINBPQ + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) // Not running +#endif + TNC->WeStartedTNC = RestartTNC(TNC); // Always try if Linux + + if (TNC->FLInfo->KISSMODE) + { + // Open Datagram port + + SOCKET sock; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + struct hostent * HostEnt = NULL; + + TNC->FLInfo->CmdControl = 5; //Send params immediately + + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (HostEnt) + { + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + } + + TNC->TCPDataSock = sock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(TNC->TCPPort + 1); + + if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = WSAGetLastError(); + Consoleprintf("Bind Failed for UDP port %d - error code = %d", TNC->TCPPort, err); + } + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + } + else + ConnecttoFLDigi(port); + + time(&lasttime[port]); // Get initial time value + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode/CF", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0 Resent 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct ARQINFO * ARQ; + struct FLINFO * FL; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + ARQ = TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + FL = TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 5 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + + TNC->FLInfo->KISSMODE = TRUE; // Default to KISS + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort + 40); // Defaults XML 7362 ARQ 7322 + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "TIMEOUT", 7) == 0) + TNC->Timeout = atoi(&buf[8]) * 10; + else + if (_memicmp(buf, "RETRIES", 7) == 0) + TNC->Retries = atoi(&buf[8]); + else + if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + if (_memicmp(buf, "ARQMODE", 7) == 0) + TNC->FLInfo->KISSMODE = FALSE; + else + if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) // Send Beacon after each session + { + // Check that freq is also specified + + char * Freq = strchr(&buf[13], '/'); + + if (Freq) + { + *(Freq++) = 0; + strcpy(TNC->FLInfo->DefaultMode, &buf[13]); + TNC->FLInfo->DefaultFreq = atoi(Freq); + } + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoFLDigi(int port) +{ + _beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port); + + return 0; +} + +static VOID ConnecttoFLDigiThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt = NULL; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("FLDIGI Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi Control socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FLDigi Control socket - error code = %d\n", err); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for FLDigi Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + else + { + sprintf(Msg, "Connect Failed for FLDigi Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + } + + return; +} + +VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d Resent %d Queued %d", + STREAM->BytesRXed, STREAM->BytesTXed, STREAM->BytesAcked, STREAM->BytesResent, STREAM->BytesOutstanding); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); +} + +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen) +{ + if (TNC->FLInfo->KISSMODE) + { + char KissMsg[1000]; + char outbuff[1000]; + int newlen; + + if (TNC->FLInfo->RAW) + { + // KISS RAW + + // Add CRC and Send + + unsigned short CRC; + char crcstring[6]; + + KissMsg[0] = 7; // KISS Raw + KissMsg[1] = 1; // SOH + KissMsg[2] = '0'; // Version + KissMsg[3] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&KissMsg[4], Msg, MsgLen +1 ); // Get terminating NULL + + CRC = CalcCRC(KissMsg + 1, MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(KissMsg, crcstring); + MsgLen += 9; + } + else + { + // Normal KISS + + KissMsg[0] = 0; // KISS Control + KissMsg[1] = TNC->ARQInfo->FarStream; + memcpy(&KissMsg[2], Msg, MsgLen); + MsgLen += 2; + } + + newlen = KissEncode(KissMsg, outbuff, MsgLen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + + SendKISSCommand(TNC, "TXBUF:"); + + } + else + { + // ARQ Scoket + + // Add Header, CRC and Send + + unsigned short CRC; + char crcstring[6]; + char outbuff[1000]; + + outbuff[0] = 1; // SOH + outbuff[1] = '0'; // Version + outbuff[2] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&outbuff[3], Msg, MsgLen + 1); + + CRC = CalcCRC(outbuff , MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(outbuff, crcstring); + MsgLen += 8; + + send(TNC->TCPDataSock, outbuff, MsgLen, 0); + } +} + +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW); + +static int ProcessReceivedData(int port) +{ + int bytes, used, bytesleft; + int i; + char ErrMsg[255]; + unsigned char MessageBuff[1500]; + unsigned char * Message = MessageBuff; + unsigned char * MessageBase = MessageBuff; + + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + // If using KISS/UDP interface use recvfrom + + if (FL->KISSMODE) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + unsigned char * KissEnd; + + bytesleft = recvfrom(TNC->TCPDataSock, Message, 1500, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (bytesleft < 0) + { + int err = WSAGetLastError(); + // if (err != 11) + // printf("KISS Error %d %d\n", nLength, err); + bytes = 0; + } + + while (bytesleft > 0) + { + unsigned char * in; + unsigned char * out; + unsigned char c; + + if (bytesleft < 3) + return 0; + + if (Message[0] != FEND) + return 0; // Duff + + Message = MessageBase; + in = out = &Message[2]; + + // We may have more than one KISS message in a packet + + KissEnd = memchr(&Message[2], FEND, bytesleft ); + + if (KissEnd == 0) + return 0; // Duff + + *(KissEnd) = 0; + + used = (int)(KissEnd - Message + 1); + + bytesleft -= used; + bytes = used; + + MessageBase += used; + + if (Message[1] == 6) // KISS Command + { + UCHAR * ptr = strchr(&Message[2], FEND); + + if (ptr) *ptr = 0; // Null Terminate + + if (bytes > 250) + Message[250] = 0; + + FL->Responding = 5; + + if (TNC->TNCOK == 0) + { + TNC->TNCOK = TRUE; + TNC->CONNECTED = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + // Trap BUSY fiest - there are lots of them, and they are likely to be confused + // with tesponses to Interactive commands + + if (memcmp(&Message[2], "BUSY", 4) == 0) + { + BOOL Changed = FALSE; + + if (Message[7] == 'T' && FL->Busy == FALSE) + { + TNC->Busy = FL->Busy = TRUE; + Changed = TRUE; + } + else + { + if (Message[7] == 'F' && FL->Busy == TRUE) + { + TNC->Busy = FL->Busy = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok %s\r", &Message[2]); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + // Drop through in case need to extract info from command + } + + // Auto Command + +// Debugprintf("%d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + + if (memcmp(&Message[2], "FLSTAT", 4) == 0) + { + if (strstr(&Message[2], "FLSTAT:INIT")) + { + // FLDIGI Reloaded - set parmas + SendKISSCommand(TNC, "RSIDBCAST:ON TRXSBCAST:ON TXBEBCAST:ON KISSRAW:ON"); + } + continue; + } + + if (memcmp(&Message[2], "TRXS", 4) == 0) + { + char * ptr1, * context; + BOOL Changed = FALSE; + + ptr1 = strtok_s(&Message[7], ",", &context); + + if (strstr(ptr1, "TX")) + { + if (TNC->FLInfo->TX == FALSE) + { + TNC->FLInfo->TX = TRUE; + Changed = TRUE; + } + } + else + { + if (TNC->FLInfo->TX) + { + TNC->FLInfo->TX = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (memcmp(&Message[2], "TXBUF:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + STREAM->BytesOutstanding = atoi(ptr1); + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "TXBE:", 5) == 0) + { + STREAM->BytesOutstanding = 0; + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "RSIDN:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + + TNC->FLInfo->CenterFreq = atoi(ptr1); + ptr1 = strtok_s(NULL, ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "MODEM:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "WFF:", 4) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[6], ",", &context); + TNC->FLInfo->CenterFreq = atoi(ptr1); + } + + sprintf(TNC->WEB_MODE, "%s/%d", TNC->FLInfo->CurrentMode, TNC->FLInfo->CenterFreq); + SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + continue; + } + + if (Message[1] == 7) // Not Normal Data + { + // "RAW" Mode. Just process as if received from TCP Socket Interface + + ProcessFLDigiPacket(TNC, &Message[2] , bytes - 3); // Data may be for another port + continue; + } + + bytes -= 3; // Two FEND and Control + + // Undo KISS + + while (bytes) + { + bytes--; + + c = *(in++); + + if (c == FESC) + { + c = *(in++); + bytes--; + + if (c == TFESC) + c = FESC; + else if (c == TFEND) + c = FEND; + } + *(out++) = c; + } + ProcessFLDigiData(TNC, &Message[3], (int)(out - &Message[3]), Message[2], FALSE); // KISS not RAW + } + return 0; + } + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPDataSock, Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPDataSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessFLDigiPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + + +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + char c; + struct FLINFO * FL = TNC->FLInfo; + + + if (TNC->FLInfo->MCASTMODE) + { + if (TNC->Streams[0].Attached == 0) + return; + + while(Len) + { + c = *(MPTR++); + + if (TNC->InPacket) + { + TNC->DataBuffer[TNC->DataLen++] = c; + + // Sanity Check + + if (TNC->DataLen == 6) + { + char * ptr = &TNC->DataBuffer[1]; + + if (memcmp(ptr, "DATA ", 5) == 0 || + memcmp(ptr, "PROG ", 5) == 0 || + memcmp(ptr, "FILE ", 5) == 0 || + memcmp(ptr, "SIZE ", 5) == 0 || + memcmp(ptr, "DESC ", 5) == 0 || + memcmp(ptr, "CNTL ", 5) == 0 || + memcmp(ptr, "ID ", 3) == 0) + + { + } + else + { + // False Trigger, try again + + TNC->InPacket = FALSE; + } + + } + else + { + if (TNC->InData) + { + if (--TNC->MCASTLen == 0) + { + // Got a packet + + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr) + { + TNC->DataBuffer[TNC->DataLen++] = 13; // Keep Tidy + + buffptr[1] = TNC->DataLen; + memcpy(&buffptr[2], &TNC->DataBuffer[0], TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + TNC->InPacket = FALSE; + } + } + else + { + // Looking for > + + if (TNC->DataLen == 16) + { + // Not found it + + TNC->InPacket = FALSE; + } + else + { + if (c == '>') + { + // Got Header - extract Length + + char * ptr; + int len; + + ptr = strchr(TNC->DataBuffer, ' '); + + if (ptr) + { + len = atoi(ptr); + + if (len) + { + TNC->InData = TRUE; + TNC->MCASTLen = len; + } + } + } + } + } + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + } + else + { + // Look for '<' + + if (c == '<') + { + TNC->DataBuffer[0] = c; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + TNC->InData = FALSE; + } + } + Len--; + } + return; + } + // Look for SOH/EOT delimiters. May Have several SOH before EOTTNC->FL + + while(Len) + { + c = *(MPTR++); + + switch (c) + { + case 01: // New Packet + + if (TNC->InPacket) + CheckFLDigiData(TNC); + + TNC->DataBuffer[0] = 1; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + break; + + case 04: + + if (TNC->InPacket) + CheckFLDigiData(TNC); + TNC->DataLen = 0; + TNC->InPacket = FALSE; + + break; + + default: + + if (TNC->InPacket) + { + if (TNC->DataLen == 1) + { + if (c != '0' && c != '1') + { + // Drop if not Protocol '0' or '1' - this should eliminate almost all noise packets + + TNC->InPacket = 0; + break; + } + } + TNC->DataBuffer[TNC->DataLen++] = c; + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + + } + Len--; + } +} +VOID CheckFLDigiData(struct TNCINFO * TNC) +{ + UCHAR * Input = &TNC->DataBuffer[0]; + int Len = TNC->DataLen - 4; // Not including CRC + unsigned short CRC; + char crcstring[6]; + + if (Len < 0) + return; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // RAW format message, either from ARQ Scoket or RAW KISS + + // Check Checksum + + CRC = CalcCRC(Input , Len); + + sprintf(crcstring, "%04X", CRC); + + if (memcmp(&Input[Len], crcstring, 4) !=0) + { + // CRC Error - could just be noise + +// Debugprintf("%s %s", crcstring, Input); + return; + } + ProcessFLDigiData(TNC, &Input[3], Len - 3, Input[2], TRUE); // From RAW +} +/* +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // ARQ Packet from KISS-Like Hardware + + struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER]; + UCHAR * Input; + int Len; + + if (TNC == NULL) + { + // Set up TNC info + + TNC = TNCInfo[PORT->PORTNUMBER] = zalloc(sizeof(struct TNCINFO)); + TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 10 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + } + + Input = &Buffer->DEST[0]; + Len = Buffer->LENGTH - 7; // Not including CRC + + // Look for attach on any call + + ProcessFLDigiData(TNC, Input, Len); +} +*/ +static int Stuff(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * ptr = inbuff; + + // DLE Escape DLE, SOH, EOT + + for (i = 0; i < len; i++) + { + c = *(ptr++); + +// if (c == 0 || c == DLE || c == SOH || c == EOT) + if (c < 32 && c != 10 && c != 13 && c != 8) + { + outbuff[txptr++] = DLE; + + // if between 0 and 0x1F, Add 40, + // if > x80 and less than 0xa0 subtract 20 + + c += 0x40; + } + outbuff[txptr++]=c; + } + + return txptr; +} + + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * outbuff = inbuff; + UCHAR * ptr = inbuff; + + // This unstuffs into the input buffer + + for (i = 0; i < len; i++) + { + c = *(ptr++); + + if (c == DLE) + { + c = *(ptr++); + i++; + + // if between 0x40 and 0x5F, subtract 0x40, + // else add 0x20 (so we can send chars 80-9f without a double DLE) + + if (c < 0x60) + c -= 0x40; + else + c += 0x20; + } + outbuff[txptr++] = c; + } + + return txptr; +} + +unsigned int crcval = 0xFFFF; + +void update(char c) +{ + int i; + + crcval ^= c & 255; + for (i = 0; i < 8; ++i) + { + if (crcval & 1) + crcval = (crcval >> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} +/* + +00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06 +00kG8BPQ:24 G8BPQ 4 85F9B + +00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5 (128, 5) + +,00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 5 89FCA + +First no sees to be a connection counter. Next may be stream + + +08s___ABFC +08tG8BPQ:73 xxx 33FA +00tG8BPQ:73 yyy 99A3 +08dG8BPQ:90986C +00bG8BPQ:911207 + +call:90 for dis 91 for dis ack 73 for chat) + +08pG8BPQ?__645E +00s_??4235 + +08pG8BPQ?__645E +00s_??4235 + +i Ident +c Connect +k Connect Ack +r Connect NAK +d Disconnect req +s Data Ack/ Retransmit Req )status) +p Poll +f Format Fail +b dis ack +t talk + +a Abort +o Abort ACK + + +00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 6 49A3A +08s___ABFC +08 ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::90 +ARQ::STX +//FLARQ COMPOSER +Date: 09/01/2014 23:24:42 +To: gm8bpq +From: +SubjectA0E0 +08!: Test + +Test Message + +ARQ::ETX +F0F2 +08pG8BPQ!__623E +08pG8BPQ!__623E +08pG8BPQ!__623E + + + + +*/ +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW) +{ + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CTRL = Input[0]; + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + + int SendLen; + char Reply[80]; + + + // Process Message + + // This processes eitrher message from the KISS or RAW interfaces. + // Headers and RAW checksum have been removed, so packet starts with Control Byte + + // Only a connect request is allowed with no session, so check first + + if (CTRL == 'c') + { + // Connect Request + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = TNC->Window; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + if (FL->CONOK == FALSE) + return; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + // See if for us + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + } + + if (App > 31) + if (strcmp(TNC->NodeCall, call2) !=0) + return; // Not Appl or Port/Node Call + + ptr = strtok_s(NULL, " ", &context); + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + BlockSize = atoi(ptr); + + if (ARQ->ARQState) + { + // We have already received a connect request - just ACK it + + goto AckConnectRequest; + } + + // Get a Session + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, call1, 0, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + strcpy(STREAM->MyCall, call2); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, call2, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, call2); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connect Pending"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->FarStream = FarStream; + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->ARQState = ARQ_ACTIVE; + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = FarStream; // Not Yet defined + if (strcmp(port1, "1025") == 0) + { + FL->FLARQ = TRUE; // From FLARQ + ARQ->OurStream = '8'; // FLARQ Ignores what we send + } + else + FL->FLARQ = FALSE; // From other app (eg BPQ) + + FL->RAW = RAW; + + STREAM->NeedDisc = 0; + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char Buffer[32]; + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + STREAM->NeedDisc = 50; // 1 sec + } + } + + ARQ->TXWindow = Window; + + if (BlockSize < 4) BlockSize = 4; + if (BlockSize < 9) BlockSize = 9; + + ARQ->MaxBlock = Blocksizes[BlockSize]; + + + ARQ->ARQTimer = 10; // To force CTEXT to be Queued + + if (App == 32) + { + // Connect to Node - send CTEXT + + if (HFCTEXTLEN > 1) + { + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = HFCTEXTLEN; + memcpy(&buffptr[2], HFCTEXT, HFCTEXTLEN); + SendARQData(TNC, buffptr); + } + } + } + + if (STREAM->NeedDisc) + { + // Send Not Avail + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((char *)&buffptr[2], "Application Not Available\n"); + SendARQData(TNC, buffptr); + } + } + +AckConnectRequest: + + SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_CONNECTACK; + + return; + } + + // All others need a session + +// if (!STREAM->Connected && !STREAM->Connecting) +// return; + + if (CTRL == 'k') + { + // Connect ACK + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = 16; + + char Reply[80]; + int ReplyLen; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ptr = strtok_s(NULL, " ", &context); + if (ptr) + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + if (ptr) + BlockSize = atoi(ptr); + + if (STREAM->Connected) + goto SendKReply; // Repeated ACK + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + STREAM->Connected = TRUE; + + ARQ->ARQTimerState = 0; + ARQ->ARQTimer = 0; + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z'); + + ARQ->ARQTimerState = 0; + ARQ->FarStream = FarStream; + ARQ->TXWindow = TNC->Window; + ARQ->MaxBlock = Blocksizes[BlockSize]; + + ARQ->ARQState = ARQ_ACTIVE; + + STREAM->NeedDisc = 0; + + buffptr = GetBuff(); + + if (buffptr) + { + ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + +SendKReply: + + // Reply with status + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + // All others need a session + + //if (!STREAM->Connected) + // return; + + + if (CTRL == 's') + { + // Status + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; // Stop retry timer + Input[Len] = 0; + ProcessARQStatus(TNC, ARQ, &Input[1]); + + return; + } + + if (CTRL == 'p') + { + // Poll + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " \x1A", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + else + ARQ->TurnroundTimer = 15; // Allow us to send it all acked + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + return; + } + + + if (CTRL == 'a') + { + // Abort. Send Abort ACK - same as + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " :", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + if (CTRL == 'i') + { + // Ident + + return; + } + + if (CTRL == 't') + { + // Talk - not sure what to do with these + + return; + } + + if (CTRL == 'd') + { + // Disconnect Request + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + + // As the Disc ACK isn't repeated, we have to clear session now + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = 0; + + SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->Retries = 2; + return; + } + + if (CTRL == 'b') + { + // Disconnect ACK + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + ARQ->ARQState = 0; + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + FLReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + } + + if (CTRL == 'u') + { + // Beacon + + //>00uGM8BPQ:72 GM8BPQ TestingAD67 + + char * Call = &Input[1]; + strlop(Call, ':'); + + UpdateMH(TNC, Call, '!', 0); + return; + } + + if (STREAM->Connected) + { + if (Channel != ARQ->OurStream) + return; // Wrong Session + + if (CTRL >= ' ' && CTRL < 96) + { + // ARQ Data + + int Seq = CTRL - 32; + int Work; + +// if (rand() % 5 == 2) +// { +// Debugprintf("Dropping %d", Seq); +// return; +// } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return; // Sould never run out, but cant do much else + + // Remove any DLE transparency + + if (TNC->FLInfo->KISSMODE) + Len -= 1; + else + Len = UnStuff(&Input[1], Len - 1); + + buffptr[1] = Len; + memcpy(&buffptr[2], &Input[1], Len); + STREAM->BytesRXed += Len; + + UpdateStatsLine(TNC, STREAM); + + // Safest always to save, then see what we can process + + if (ARQ->RXHOLDQ[Seq]) + { + // Wot! Shouldn't happen + + ReleaseBuffer(ARQ->RXHOLDQ[Seq]); +// Debugprintf("ARQ Seq %d Duplicate"); + } + + ARQ->RXHOLDQ[Seq] = buffptr; +// Debugprintf("ARQ saving %d", Seq); + + // If this is higher that highest received, save. But beware of wrap' + + // Hi = 2, Seq = 60 dont save s=h = 58 + // Hi = 10 Seq = 12 save s-h = 2 + // Hi = 14 Seq = 10 dont save s-h = -4 + // Hi = 60 Seq = 2 save s-h = -58 + + Work = Seq - ARQ->RXHighest; + + if ((Work > 0 && Work < 32) || Work < -32) + ARQ->RXHighest = Seq; + + // We may now be able to process some + + Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need + + while (ARQ->RXHOLDQ[Work]) + { + // We have it + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]); +// ReleaseBuffer(ARQ->RXHOLDQ[Work]); + + ARQ->RXHOLDQ[Work] = NULL; +// Debugprintf("Processing %d from Q", Work); + + ARQ->RXNoGaps = Work; + Work = (Work + 1) & 63; // The next one we need + } + + ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data + return; + } + } +} + + +VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer) +{ + // Send Data, saving a copy until acked. + + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + UCHAR * ptr; + int Origlen = Buffer[1]; + int Stuffedlen; + + ARQ->TXSeq++; + ARQ->TXSeq &= 63; + + SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32); + + ptr = (UCHAR *)&Buffer[2]; // Start of data; + + ptr[Buffer[1]] = 0; + + if (memcmp(ptr, "ARQ:", 4) == 0) + { + // FLARQ Mail/FIle transfer. Turn off CR > LF translate (used for terminal mode) + + FL->FLARQ = FALSE; + } + + if (FL->FLARQ) + { + // Terminal Mode. Need to convert CR to LF so it displays in FLARQ Window + + ptr = strchr(ptr, 13); + + while (ptr) + { + *(ptr++) = 10; // Replace CR with LF + ptr = strchr(ptr, 13); + } + } + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Origlen); + SendLen += Origlen; + } + else + { + Stuffedlen = Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Origlen); + SendLen += Stuffedlen; + } + + TXBuffer[SendLen] = 0; + +// if (rand() % 5 == 2) +// Debugprintf("Dropping %d", ARQ->TXSeq); +// else + + ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer; + + STREAM->BytesTXed += Origlen; + + UpdateStatsLine(TNC, STREAM); + + // if waiting for ack, don't send, just queue. Will be sent when ack received + + if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA) + { + SendPacket(TNC, TXBuffer, SendLen); + ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + } + else + STREAM->BytesResent -= Origlen; // So wont be included in resent bytes +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Reply[80]; + int SendLen; + + struct ARQINFO * ARQ = TNC->ARQInfo; + + SendLen = sprintf(Reply, "d%s:90", TNC->Streams[0].MyCall); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_DISC; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + FLReleaseTNC(TNC); +} + +VOID FLReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // if a default Modem is defined, select it + + if (TNC->FLInfo->DefaultMode[0]) + { + char txbuff[80]; + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%d MODEM:%s MODEM: WFF:", TNC->FLInfo->DefaultFreq, TNC->FLInfo->DefaultMode); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", TNC->FLInfo->DefaultMode, 'S'); + SendXMLCommand(TNC, "modem.set_carrier", (char *)TNC->FLInfo->DefaultFreq, 'I'); + } + } + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Queue to be sent after TXDELAY + + memcpy(ARQ->TXMsg, Msg, MsgLen + 1); + ARQ->TXLen = MsgLen; + ARQ->TXDelay = 15; // Try 1500 ms +} + +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Used for Messages that need a reply. Save, send and set timeout + + memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null + ARQ->LastLen = MsgLen; + + // Delay the send for a short while Just use the timeout code + +// SendPacket(sock, Msg, MsgLen, 0); + ARQ->ARQTimer = 1; // Try 500 ms + ARQ->Retries = TNC->Retries + 1; // First timout is rthe real send + + return; +} + + +VOID ARQTimer(struct TNCINFO * TNC) +{ + struct ARQINFO * ARQ = TNC->ARQInfo; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int SendLen; + char Reply[80]; + struct FLINFO * FL = TNC->FLInfo; + + //Send frames, unless held by TurnroundTimer or Window + + int Outstanding; + + // Use new BUSY: poll to detect busy state + + if (FL->TX == FALSE) + if (TNC->FLInfo->KISSMODE) + SendKISSCommand(TNC, "BUSY:"); // Send every poll for now - may need to optimize later + + +/* +// Use Received chars as a rough channel active indicator + + FL->BusyTimer++; + + if (FL->BusyTimer > 4) + { + FL->BusyTimer = 0; + + if (FL->BusyCounter > 2) // 2 chars in last .3 secs + FL->Busy = TRUE; + else + FL->Busy = FALSE; + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + FL->BusyCounter = 0; + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + +*/ // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't + // need to check for busy (or anything else (I think!) + + if (ARQ->TXDelay) + { + ARQ->TXDelay--; + + if (ARQ->TXDelay) + return; + + SendPacket(TNC, ARQ->TXMsg, ARQ->TXLen); + } + + // if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end) + + if (ARQ->ARQTimerState == ARQ_WAITDATA) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + + ARQ->ARQTimer--; + + if (ARQ->ARQTimer > 0) + return; // Timer Still Running + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + +// Debugprintf("Sending Poll"); + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + } + + // TrunroundTimer is used to allow time for far end to revert to RX + + if (ARQ->TurnroundTimer && !FL->Busy) + ARQ->TurnroundTimer--; + + if (ARQ->TurnroundTimer == 0) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + } + + if (ARQ->ARQTimer) + { + if (FL->TX || FL->Busy) + { + // Only decrement if running send poll timer + + if (ARQ->ARQTimerState != ARQ_WAITDATA) + return; + } + + ARQ->ARQTimer--; + { + if (ARQ->ARQTimer) + return; // Timer Still Running + } + + ARQ->Retries--; + + if (ARQ->Retries) + { + // Retry Current Message + + SendPacket(TNC, ARQ->LastMsg, ARQ->LastLen); + ARQ->ARQTimer = TNC->Timeout + (rand() % 30); + + return; + } + + // Retried out. + + switch (ARQ->ARQTimerState) + { + case ARQ_WAITDATA: + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + case ARQ_CONNECTING: + + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + // Send Disc to TNC in case it got the Connects, but we missed the ACKs + + TidyClose(TNC, 0); + ARQ->Retries = 2; // First timout is the real send, only send once + STREAM->Connecting = FALSE; // Back to Command Mode + ARQ->ARQState = FALSE; + + break; + + case ARQ_WAITACK: + case ARQ_CONNECTACK: + case ARQ_DISC: + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; + ARQ->ARQState = FALSE; + + while (STREAM->PACTORtoBPQ_Q) + ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q)); + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + break; + + } + } +} + +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char * Input) +{ + // Release any acked frames and resend any outstanding + + int LastInSeq = Input[1] - 32; + int LastRXed = Input[2] - 32; + int FirstUnAcked = ARQ->TXLastACK; + int n = (int)strlen(Input) - 3; + char * ptr; + int NexttoResend; + int First, Last, Outstanding; + UINT * Buffer; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int Acked = 0; + + // First status is an ack of Connect ACK + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + { + ARQ->Retries = 0; + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + } + + // Release all up to LastInSeq + + while (FirstUnAcked != LastInSeq) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + ARQ->TXLastACK = FirstUnAcked; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (FirstUnAcked == ARQ->TXSeq) + { + UpdateStatsLine(TNC, STREAM); + ARQ->NoAckRetries = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; // All Acked + } + // Release any not in retry list up to LastRXed. + + ptr = &Input[3]; + + while (n) + { + NexttoResend = *(ptr++) - 32; + + FirstUnAcked++; + FirstUnAcked &= 63; + + while (FirstUnAcked != NexttoResend) + { + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + + FirstUnAcked++; + FirstUnAcked &= 63; + } + + // We don't ACK this one. Process any more resend values, then release up to LastRXed. + + n--; + } + + // Release rest up to LastRXed + + while (FirstUnAcked != LastRXed) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + // Resend anything in TX Buffer (From LastACK to TXSeq + + Last = ARQ->TXSeq + 1; + Last &= 63; + + First = LastInSeq; + + while (First != Last) + { + First++; + First &= 63; + + if(ARQ->TXHOLDQ[First]) + { + UINT * Buffer = ARQ->TXHOLDQ[First]; + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + +// Debugprintf("Resend %d", First); + + STREAM->BytesResent += Buffer[1]; + + SendLen = sprintf(TXBuffer, "%c", First + 32); + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Buffer[1]); + SendLen += Buffer[1]; + } + else + SendLen += Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Buffer[1]); + + TXBuffer[SendLen] = 0; + + SendPacket(TNC, TXBuffer, SendLen); + + ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + + if (Acked == 0) + { + // Nothing acked by this statis message + + Acked = 0; // Dont count more thna once + ARQ->NoAckRetries++; + if (ARQ->NoAckRetries > TNC->Retries) + { + // Too many retries - just disconnect + + TidyClose(TNC, 0); + return; + } + } + } + } + + UpdateStatsLine(TNC, STREAM); +} + +VOID FLSlowTimer(struct TNCINFO * TNC) +{ + struct FLINFO * FL = TNC->FLInfo; + + // Entered every 10 secs + + // if in MCAST mode, clear KILL timer (MCAST RX can run for a long time + + if (TNC->FLInfo->MCASTMODE) + { + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + SESS->L4KILLTIMER = 0; + } + + if (FL->KISSMODE) + { + if (FL->Responding) + FL->Responding--; + + if (FL->Responding == 0) + { + TNC->TNCOK = FALSE; + TNC->CONNECTED = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to FLDIGI lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Set basic params till it responds + } + + FL->CmdControl++; + + if (FL->CmdControl > 5) // Every Minute + { + FL->CmdControl = 0; + + SendKISSCommand(TNC, "FLSTAT: MODEM: WFF:"); + } + + SendKISSCommand(TNC, "TRXS: TXBUF:"); // In case TX/RX report is missed + } +} + +static int ProcessXMLData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + char * ptr1, * ptr2, *ptr3; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data. Assume for now we get a whole packet + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr3 = strstr(ptr1, ""); + + if (ptr3) + { + ptr1 = ptr3 + 4; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok Was %s\r", ptr1); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + } + + return 0; + } + + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr2 = strstr(ptr1, ""); + + if (ptr2) + { + ptr2 += 8; + ptr1 = ptr2; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (strcmp(FL->LastXML, "modem.get_name") == 0) + { + strcpy(TNC->WEB_MODE, ptr1); + SetWindowText(TNC->xIDC_MODE, ptr1); + } + else if (strcmp(FL->LastXML, "main.get_trx_state") == 0) + { + if (strcmp(ptr1, "TX") == 0) + FL->TX = TRUE; + else + FL->TX = FALSE; + + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (strcmp(FL->LastXML, "main.get_squelch") == 0) + { +/* + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); +*/ + return 0; + } +/* + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } +*/ + + } + + return (0); + +} + + + +char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + char ValueString[256] =""; + + if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE) + return; + + if (Value) + if (ParamType == 'S') + sprintf(ValueString, "%s", Value); + else + sprintf(ValueString, "%d", Value); + + strcpy(FL->LastXML, Command); + Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; +} + +VOID SendXMLPoll(struct TNCINFO * TNC) +{ + int Len; + char ReqBuf[256]; + char SendBuff[256]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + + if (!TNC->CONNECTED) + return; + + if (TNC->FLInfo->KISSMODE) + return; + + if (ARQ->ARQTimer) + { + // if timer is running, poll fot TX State + + strcpy(FL->LastXML, "main.get_trx_state"); + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; + } + + FL->XMLControl++; + + + if (FL->XMLControl > 9) + { + FL->XMLControl = 0; + strcpy(FL->LastXML, "modem.get_name"); + } + else + { + if (FL->XMLControl == 5) + strcpy(FL->LastXML, "main.get_trx_state"); + else + return; + } + + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); +} + +// sudo add-apt-repository ppa:kamalmostafa/fldigi + + diff --git a/FreeDATA.c b/FreeDATA.c index 9e4b349..ac1cb65 100644 --- a/FreeDATA.c +++ b/FreeDATA.c @@ -452,6 +452,11 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) TNC->lasttime = ltime; ConnecttoFreeData(port); } + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } } diff --git a/HALDriver64.c b/HALDriver64.c new file mode 100644 index 0000000..7c60f9f --- /dev/null +++ b/HALDriver64.c @@ -0,0 +1,1907 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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 +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 27; + memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + 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_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[80]; +#ifndef LINBPQ + HWND x; +#endif + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_HAL; + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ExtProc; +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + UINT * buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; + datalen=buffptr[1]; + MsgPtr = (UCHAR *)&buffptr[2]; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->BytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + UINT * buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->BytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr[1] = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->BytesRXed += Len; + + memcpy(&buffptr[2], TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + UINT * buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->BytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/HFCommon.c b/HFCommon.c index 1ecd775..7aa274c 100644 --- a/HFCommon.c +++ b/HFCommon.c @@ -1866,9 +1866,6 @@ static char ** SeparateMultiString(char * MultiString) return Value; } - - - extern int nextDummyInterlock; int standardParams(struct TNCINFO * TNC, char * buf) @@ -1917,7 +1914,7 @@ int standardParams(struct TNCINFO * TNC, char * buf) TNC->ActiveTXFreq = atof(&buf[13]); else if (_memicmp(buf, "ActiveRXFreq", 12) == 0) // Set at start of session TNC->ActiveRXFreq = atof(&buf[13]); - else if (_memicmp(buf, "DisconnectScript", 16) == 0) // Set at start of session + else if (_memicmp(buf, "DisconnectScript", 16) == 0) // Set at end of session TNC->DisconnectScript = SeparateMultiString(&buf[17]); else if (_memicmp(buf, "PTTONHEX", 8) == 0) { diff --git a/HTTPcode.c b/HTTPcode.c index 245ab3d..3145d80 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -105,9 +105,13 @@ extern int NumberofPorts; extern UCHAR ConfigDirectory[260]; +extern struct AXIPPORTINFO * Portlist[]; + VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); int CompareNode(const void *a, const void *b); int CompareAlias(const void *a, const void *b); +int CompareRoutes(const void * a, const void * b); + void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen, char * Token); void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); @@ -3794,12 +3798,82 @@ doHeader: */ + // AXIP Partners + + if (_stricmp(NodeURL, "/Node/AXIP.html") == 0) + { + int i; + char Normcall[10]; + int Width = 5; + int x = 0, n = 0, nd = 0; + struct arp_table_entry * List[1000]; + struct arp_table_entry * ListD[1000]; + char AXIPList[10000] = ""; + int ListLen = 0; + + struct AXIPPORTINFO * AXPORT = Portlist[0]; + struct PORTCONTROL * PORT = PORTTABLE; + struct arp_table_entry * arp; + time_t NOW = time(NULL); + + char AXIPHeader[] = + "" + "
AXIP UpAXIP Down
%s"; + + + while (PORT) + { + AXPORT = Portlist[PORT->PORTNUMBER]; + + if (AXPORT) + { + // Get ARP entries + + for (i = 0; i < AXPORT->arp_table_len; i++) + { + arp = &AXPORT->arp_table[i]; + + if (arp->LastHeard == 0 || (NOW - arp->LastHeard) > 3600) // Considered down + ListD[nd++] = arp; + else + List[n++] = arp; + } + } + PORT = PORT->PORTPOINTER; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareNode); + if (nd > 1) + qsort(ListD, nd, sizeof(void *), CompareNode); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->callsign, Normcall); + Normcall[len]=0; + + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (List[i]->LastHeard)?(NOW - List[i]->LastHeard):0); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], AXIPHeader, AXIPList); + + ListLen = 0; + + for (i = 0; i < nd; i++) + { + int len = ConvFromAX25(ListD[i]->callsign, Normcall); + Normcall[len]=0; + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (ListD[i]->LastHeard)?(NOW - ListD[i]->LastHeard):0); + } + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
%s", AXIPList); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
"); + } if (_stricmp(NodeURL, "/Node/Routes.html") == 0) { struct ROUTE * Routes = NEIGHBOURS; int MaxRoutes = MAXNEIGHBOURS; - int count; + int count, i; char Normcall[10]; char locked[4] = " "; int NodeCount; @@ -3807,12 +3881,34 @@ doHeader: int Iframes, Retries; char Active[10]; int Queued; + + int x = 0, n = 0; + struct ROUTE * List[1000]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); - for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + List[n++] = Routes; + + if (n > 999) + break; + } + + Routes++; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareRoutes); + + for (i = 0; i < n; i++) + { + Routes = List[i]; { int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); Normcall[len]=0; diff --git a/IPCode.c b/IPCode.c index f340d4b..4d9abca 100644 --- a/IPCode.c +++ b/IPCode.c @@ -100,7 +100,10 @@ TODo ?Multiple Adapters #ifdef WIN32 int pcap_sendpacket(pcap_t *p, u_char *buf, int size); #else - PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); +#ifndef PCAP_API +#define PCAP_API extern +#endif +PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); #endif #ifndef LINBPQ @@ -387,7 +390,7 @@ char * FormatIP(uint32_t Addr) return FormatIPWork; } -int CompareRoutes (const VOID * a, const VOID * b) +int CompareIPRoutes (const VOID * a, const VOID * b) { PROUTEENTRY x; PROUTEENTRY y; @@ -4972,7 +4975,7 @@ VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, str Bufferptr = Cmdprintf(Session, Bufferptr, "%d Entries\r", NumberofRoutes); if (NumberofRoutes) - qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareRoutes); + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareIPRoutes); for (i=0; i < NumberofRoutes; i++) { diff --git a/KAMPactor.c b/KAMPactor.c index 318c6e0..239b183 100644 --- a/KAMPactor.c +++ b/KAMPactor.c @@ -70,6 +70,8 @@ static RECT Rect; int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); static FILE * LogHandle[32] = {0}; @@ -517,6 +519,28 @@ ok: return 0; } +VOID KAMSuspendPort(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 + +} + +VOID KAMReleasePort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + + static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) { int Len = sprintf(Buff, "" @@ -595,6 +619,11 @@ void * KAMExtInit(EXTPORTDATA * PortEntry) PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; +// TNC->SuspendPortProc = KAMSuspendPort; +// TNC->ReleasePortProc = KAMReleasePort; + + + ptr=strchr(TNC->NodeCall, ' '); if (ptr) *(ptr) = 0; // Null Terminate @@ -886,24 +915,39 @@ VOID KAMPoll(int Port) calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); TNC->Streams[0].MyCall[calllen] = 0; - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - if (TNC->VeryOldMode) - datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall); - else - datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall); + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + + if (TNC->VeryOldMode) + { + datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall); EncodeAndSend(TNC, TXMsg, datalen); - TNC->InternalCmd = 'M'; + } + else + { + datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall); + EncodeAndSend(TNC, TXMsg, datalen); + + if (TNC->OldMode == 0) + { + EncodeAndSend(TNC, TXMsg, datalen); + datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->Streams[0].MyCall); + } + } + + TNC->InternalCmd = 'M'; - TNC->NeedPACTOR = 0; // Cancel enter Pactor + TNC->NeedPACTOR = 0; // Cancel enter Pactor - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SuspendOtherPorts(TNC); - // Stop Scanning + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - sprintf(Msg, "%d SCANSTOP", TNC->Port); + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); - Rig_Command( (TRANSPORTENTRY *) -1, Msg); + Rig_Command( (TRANSPORTENTRY *) -1, Msg); } } @@ -1036,9 +1080,22 @@ VOID KAMPoll(int Port) UCHAR TXMsg[80] = "D20"; if (TNC->VeryOldMode) + { datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall); + EncodeAndSend(TNC, TXMsg, datalen); + } else + { datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall); + EncodeAndSend(TNC, TXMsg, datalen); + + if (TNC->OldMode == 0) + { + datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->NodeCall); + EncodeAndSend(TNC, TXMsg, datalen); + } + } + EncodeAndSend(TNC, TXMsg, datalen); if (TNC->OldMode) @@ -1046,6 +1103,7 @@ VOID KAMPoll(int Port) else EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen + TNC->InternalCmd = 'T'; TNC->Timeout = 50; TNC->IntCmdDelay--; @@ -1266,6 +1324,51 @@ VOID KAMPoll(int Port) return; } + if (memcmp(MsgPtr, "GTOR ", 5) == 0) // GTOR Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[5], 9); + STREAM->Connecting = TRUE; + + // If Stream 0, Convert C CALL to PACTOR CALL + + if (Stream == 0) + { + datalen = sprintf(TXMsg, "C20GTOR %s", TNC->Streams[0].RemoteCall); + + // If Pactor, check busy detecters on any interlocked ports + + if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) + { + // Channel Busy. Wait + + TNC->ConnectCmd = _strdup(TXMsg); + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; + + return; + } + + TNC->OverrideBusy = FALSE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", + TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); + + EncodeAndSend(TNC, TXMsg, datalen); + TNC->Timeout = 50; + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + ReleaseBuffer(buffptr); + STREAM->Connecting = TRUE; + + return; + } + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect { if (Stream == 0) @@ -1644,6 +1747,10 @@ VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) return; } + + WritetoTrace(TNC, Buffer, Len); + + // Pass to Appl Stream = TNC->CmdStream; @@ -1680,6 +1787,76 @@ VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) if (Msg[0] == '?') // Status { TNC->Timeout = 0; + +/* + The response frame from the TNC will be: + ?0MSXY - where MSXY are coded as follows: + M One byte indicating the current mode of operation. + A=Packet + B=RTTY + C=ASCII + D=AMTOR + E=FEC + F=SELFEC + G=LAMTOR + H=PACTOR + I=PTLISTEN + J=GTOR + K=NAVTEX + L=CW + M=TOR Standby + N=GMON + O=PSK31 + S One byte indicating a sub-mode of operation. The byte + contains an ASCII character as follows: + 0=Standby + 1=Phasing + 2=Connected + 3=Disconnecting + 4=FEC + 5=SELFEC + 6=PTFEC + X One byte (called status byte X). This byte is bit- + encoded to indicate specific conditions as follows: + Bit 0 = (IDLE) set to 1 when receiving IDLE + characters in a frame. + Bit 1 = (ERR) set to 1 to indicate the received frame + failed CRC check, or was not a valid CS + response frame. + Bit 2 = (Combined receive) set to 1 to indicate that + the data was constructed through the use of + error correction (i.e. Golay error correction + for G-TOR or Memory ARQ for Pactor). + Bit 3 = (RQ) set to 1 to indicate an RQ frame. If you + are the ISS, it indicates that the receiving + station has asked for a repeat of the last + data due to received errors. When you are the + IRS, it indicates that the transmitting + station has sent the same data that you have + already received. This means that the sending + station did not properly copy your + acknowledgement (CS code). + Bit 4 = (Huffman) set to 1 to indicate that this + frame contains data which uses Huffman + compression. + Bit 5 = (ISS) set to 1 to indicate that your station + is currently the ISS. + Bit 6&7 = (Speed) these two bits indicate the current + speed of an ARQ link or the PSK31 mode. The + coding of the bits is: + 00 = 100 baud or BPSK31 + 01 = 200 baud or QPSK31 + 10 = 300 baud + Y One byte (called status byte Y). This byte is bit- + encoded to indicate specific conditions as follows: + Bit 0 = reserved (set to 0). + Bit 1 = (PTT) PTT is active. + Bit 2 = (Changeover) changeover in progress + Bits 3-7 = reserved (set to 0). +*/ + + + return; } @@ -1790,6 +1967,10 @@ VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) if (Msg[1] == '2' && Msg[2] == 'A') TNC->HFPacket = TRUE; + // Stop other ports in same group + + SuspendOtherPorts(TNC); + ProcessIncommingConnect(TNC, Call, Stream, TRUE); SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; @@ -2047,7 +2228,11 @@ VOID ForcedClose(struct TNCINFO * TNC, int Stream) VOID CloseComplete(struct TNCINFO * TNC, int Stream) { - TNC->NeedPACTOR = 50; + sprintf(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseOtherPorts(TNC); + TNC->NeedPACTOR = 50; } diff --git a/L2Code.c b/L2Code.c index f4c83e5..76924bf 100644 --- a/L2Code.c +++ b/L2Code.c @@ -33,6 +33,15 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "cheaders.h" #include "tncinfo.h" +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + + #define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE #define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY @@ -111,7 +120,8 @@ int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive); void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); void hookL2SessionDeleted(struct _LINKTABLE * LINK); void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); - +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); +VOID DeleteINP3Routes(struct ROUTE * Route); extern int REALTIMETICKS; @@ -126,7 +136,9 @@ extern int REALTIMETICKS; #define SDINVC 1 // INVALID COMMAND #define SDNRER 8 // INVALID N(R) - +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; UCHAR NO_CTEXT = 0; UCHAR ALIASMSG = 0; @@ -142,6 +154,8 @@ extern BOOL LogAllConnects; APPLCALLS * APPL; +int SUPPORT2point2 = 1; + void SendL2ToMonMap(struct PORTCONTROL * PORT, char * ReportCall, char Mode, char Direction) { @@ -842,6 +856,7 @@ VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESS while (xidlen > 0) { + unsigned char * typeptr = ptr; Type = *ptr++; Len = *ptr++; @@ -889,6 +904,23 @@ VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESS case 8: //RX Window break; + + case 16: + + // Compression + + if (L2Compress) + { + LINK->AllowCompress = 1; + // return as 17 + *typeptr = 17; + } + else + { + ptr = &ADJBUFFER->PID; + ptr[3] -= 2; // Length field - remove compress option + Buffer->LENGTH -=2; + } } } @@ -900,6 +932,8 @@ VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESS LINK->LINKPORT = PORT; + LINK->KILLTIMER = L2KILLTIME - 60*3; // Time out after 60 secs if SABM not received + // save calls so we can match up SABM when it comes memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); @@ -999,6 +1033,7 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * // MESSAGE ON AN ACTIVE LINK int CTLlessPF = CTL & ~PFBIT; + unsigned char * ptr; PORT->L2FRAMESFORUS++; @@ -1054,7 +1089,7 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * LINK->L2STATE = 2; LINK->Ver2point2 = FALSE; - LINK->L2TIMER = 1; // USe retry to send SABM + LINK->L2TIMER = 1; // Use retry to send SABM } else if (CTLlessPF == XID) { @@ -1062,7 +1097,49 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * LINK->L2STATE = 2; LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID - LINK->L2TIMER = 1; // USe retry to send SABM + + // if Compress enabled set it + + ptr = &ADJBUFFER->PID; + + 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 17: + + // Compression + + if (L2Compress) + LINK->AllowCompress = 1; + + } + } + + } + + LINK->L2TIMER = 1; // Use retry to send SABM } ReleaseBuffer(Buffer); @@ -1110,8 +1187,9 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * // 2. OTHER END THINKS LINK HAS DIED // 3. RECOVERY FROM FRMR CONDITION // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA + // 5. Other end has reloaded - // FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 + // FOR 1-3 and 5 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME @@ -1124,7 +1202,7 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * return; } - InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END LINK->CIRCUITPOINTER = 0; L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM @@ -1141,6 +1219,8 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe TRANSPORTENTRY * Session; int CONERROR; + struct ROUTE * ROUTE = NULL; + char toCall[12], fromCall[12]; @@ -1198,6 +1278,18 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) AttachKISSHF(PORT, Buffer); + // if it is an INP3 connection tell INP3 it is up + + + if (FindNeighbour(LINK->LINKCALL, PORT->PORTNUMBER, &ROUTE)) + { + if (ROUTE->INP3Node) + { + Debugprintf("INP3 Incoming connect from %s", fromCall); + DeleteINP3Routes(ROUTE); + } + } + if (NO_CTEXT == 1) return; @@ -1721,9 +1813,9 @@ BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries) else LINK->LINKWINDOW = PORT->PORTWINDOW; -// if (SUPPORT2point2) -// LINK->L2STATE = 1; // Send XID -// else + if (SUPPORT2point2) + LINK->L2STATE = 1; // Send XID + else LINK->L2STATE = 2; memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7); @@ -1881,11 +1973,23 @@ VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * B // RESPONSE TO SABM - SET LINK UP char fromCall[12]; + struct ROUTE * ROUTE; fromCall[ConvFromAX25(Buffer->ORIGIN, fromCall)] = 0; RESET2X(LINK); // LEAVE QUEUED STUFF + // See if INP3 route setup + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + if (ROUTE->INP3Node) + { + Debugprintf("INP3 Route to %s connected", fromCall); + } + } + + SendL2ToMonMap(PORT, fromCall, '+', 'O'); LINK->L2STATE = 5; @@ -2018,7 +2122,6 @@ VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION } - ReleaseBuffer(Buffer); } @@ -2358,12 +2461,11 @@ CheckNSLoop: { // Already have a copy, so discard old and keep this - Debugprintf ("Frame %d out of seq but already have copy - release it", NS); ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); } else { - Debugprintf ("Frame %d out of seq - save", NS); +// Debugprintf ("Frame %d out of seq - save", NS); } Buffer->CHAIN = 0; @@ -2450,6 +2552,8 @@ CheckPF: } +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer) { @@ -2479,6 +2583,7 @@ VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Info = &Buffer->PID; LINK->bytesRXed += Length; + LINK->Received += Length - 1; // Exclude PID // Adjust for DIGIS @@ -2495,6 +2600,111 @@ VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * switch(PID) { + case 0xf2: + + // Intermediate fragment of compressed. Save + + // Length and Info include pid + + Length--; + Info++; + + if (LINK->unCompress == 0) + LINK->unCompress = malloc(8192); + + // Save data + + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + + ReleaseBuffer(Buffer); + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + return; + + + case 0xf1: + + // Compressed last or only + + { + char exBuffer[8192]; + int Len; + int outLen; + int sendLen; + char * sendptr = exBuffer; + + Length--; + Info++; + + // we may have previous fragments + + if (LINK->unCompressLen) + { + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + Len = doinflate(LINK->unCompress, exBuffer, LINK->unCompressLen, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + + LINK->unCompressLen = 0; + } + else + { + Len = doinflate(Info, exBuffer, Length, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + } + sendLen = outLen; + + // Send first bit in input buffer. If still some left get new buffers for it + + if (sendLen > 257) + sendLen = 257; + + // First byte is original PID + + memcpy(&Msg->PID, exBuffer, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Msg); + + outLen -= sendLen; + sendptr += sendLen; + + while (outLen > 0) + { + sendLen = outLen; + + if (sendLen > 236) + sendLen = 236; + + Msg = GetBuff(); + + if (Msg) + { + // Just ignore if no buffers - shouldn't happen + + Msg->PID = exBuffer[0]; + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, sendptr, sendLen); + Length = sendLen + 1; + + Msg->LENGTH = Length + MSGHDDRLEN; + C_Q_ADD(&LINK->RX_Q, Msg); + } + + outLen -= sendLen; + sendptr += sendLen; + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + + return; + } + + case 0xcc: case 0xcd: @@ -2544,6 +2754,7 @@ VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * // Copy Data back over memmove(&Msg->PID, Info, Length); + LINK->ReceivedAfterExpansion += Length - 1; Buffer->LENGTH = Length + MSGHDDRLEN; @@ -2611,7 +2822,7 @@ VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS) int COUNT_AT_L2(struct _LINKTABLE * LINK) { - // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX) + // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN LINK) int count = 0, abovelink = 0; int n = 0; @@ -2758,7 +2969,7 @@ VOID SDETX(struct _LINKTABLE * LINK) UCHAR * ptr1, * ptr2; UCHAR CTL; int count; - MESSAGE * Msg; + struct DATAMESSAGE * Msg; MESSAGE * Buffer; // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS @@ -2766,11 +2977,6 @@ VOID SDETX(struct _LINKTABLE * LINK) // if (LINK->L2RESEQ_Q) // return; - if (LINK->LINKPORT->PORTNUMBER == 19) - { - int i = 0; - } - Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS if (Outstanding < 0) @@ -2783,13 +2989,150 @@ VOID SDETX(struct _LINKTABLE * LINK) while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL) { + // Try compressing here. Only Compress PID 0xF0 frames - NETROM doesn't treat L2 session as a byte stream + Msg = Q_REM(&LINK->TX_Q); Msg->CHAIN = NULL; - LINK->FRAMES[LINK->SDTSLOT] = Msg; - LINK->SDTSLOT ++; - LINK->SDTSLOT &= 7; - } + + if (LINK->AllowCompress && Msg->LENGTH > 20 && LINK->TX_Q && Msg->PID == 240) // if short and no more not worth trying compression + { + int complen = 0; + int dataLen; + int savePort = Msg->PORT; + int savePID = Msg->PID; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; + + int slots = 0; + int n = LINK->SDTSLOT; + int maxcompsize; + int PACLEN = LINK->LINKPORT->PORTPACLEN; + unsigned char * compdata; + int sendLen = complen; + int uncompressed = 0; + + if (PACLEN == 0) + PACLEN = 256; + + // I think I need to know how many slots are available, so I don't compress too much + // Then collect data, compressing after each frame to make sure will fit in available space + + while (LINK->FRAMES[n] == NULL && slots < 8) + { + slots++; + n++; + n &= 7; + } + + maxcompsize = slots * PACLEN; + + // Save first packet, then see if more on TX_Q + + toCompressLen = 0; + + dataLen = Msg->LENGTH - MSGHDDRLEN; + + LINK->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], &Msg->PID, dataLen); + toCompressLen += dataLen; + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + ReleaseBuffer(Msg); + + while (LINK->TX_Q) + { + Msg = LINK->TX_Q; // Leave on queue until sure it will fit + dataLen = Msg->LENGTH - MSGHDDRLEN -1; // PID only on 1st fragment + + memcpy(&toCompress[toCompressLen], &Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + // Need to make sure we don't go over maxcompsize + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + if (complen > maxcompsize) + { + // Remove last fragment and compress again + + toCompressLen -= dataLen; + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + break; + } + else + { + LINK->Sent += dataLen; + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + ReleaseBuffer(Msg); + } + } + + if (complen >= toCompressLen) + { + // Won't compress, so just send original data + // May still need to fragment + + memcpy(Compressed, toCompress, toCompressLen); + complen = toCompressLen - 1; // Remove leading PID + uncompressed = 1; + compdata = &Compressed[1]; + } + else + compdata = Compressed; + + // We now need to packetize and add to FRAMES + + LINK->SentAfterCompression += complen; + + sendLen = PACLEN; + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + else + sendLen = complen; + + if (uncompressed) + PID = Compressed[0]; + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = savePort; + Msg->PID = PID; + + memcpy(&Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; + + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + + compdata += sendLen; + complen -= sendLen; + } + + toCompressLen = 0; + + } + else + { + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + } + } + // dont send while poll outstanding while ((LINK->L2FLAGS & POLLSENT) == 0) @@ -2901,7 +3244,7 @@ VOID L2TimerProc() if (PORT == NULL) { LINK++; - continue; // just ion case!! + continue; // just in case!! } if (LINK->L2TIMER) @@ -3000,13 +3343,25 @@ VOID L2TimerProc() { // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN - LINK->KILLTIMER = 0; - LINK->L2TIMER = 1; // TO FORCE DISC - LINK->L2STATE = 4; // DISCONNECTING + // if in XID received state session was never established so don't send DISC - // TELL OTHER LEVELS + if (LINK->L2STATE == 1) + { + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); - InformPartner(LINK, NORMALCLOSE); + CLEAROUTLINK(LINK); + } + else + { + LINK->KILLTIMER = 0; + LINK->L2TIMER = 1; // TO FORCE DISC + LINK->L2STATE = 4; // DISCONNECTING + + // TELL OTHER LEVELS + + InformPartner(LINK, NORMALCLOSE); + } } LINK++; } @@ -3311,6 +3666,9 @@ VOID CLEAROUTLINK(struct _LINKTABLE * LINK) CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS + if (LINK->unCompress) + free(LINK->unCompress); + memset(LINK, 0, sizeof(struct _LINKTABLE)); } @@ -3345,7 +3703,11 @@ VOID L2SENDXID(struct _LINKTABLE * LINK) *ptr++ = 0x82; // FI *ptr++ = 0x80; // GI *ptr++ = 0x0; - *ptr++ = 0x10; // Length 16 + + if (L2Compress) + *ptr++ = 0x12; // Length 18 + else + *ptr++ = 0x10; // Length 16 *ptr++ = 0x02; // Classes of Procedures *ptr++ = 0x02; // Length @@ -3375,6 +3737,14 @@ VOID L2SENDXID(struct _LINKTABLE * LINK) *ptr++ = 0x01; // Len *ptr++ = 0x07; // 7 + // if L2Compress Enabled request it + + if (L2Compress) + { + *ptr++ = 0x10; // Compress + *ptr++ = 0x00; // Len + } + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER @@ -3578,7 +3948,6 @@ CheckNSLoop2: struct PORTCONTROL * PORT = LINK->LINKPORT; MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); - Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR); PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer // NR has been updated. @@ -3636,7 +4005,9 @@ VOID CONNECTREFUSED(struct _LINKTABLE * LINK) ConnectFailedOrRefused(LINK, "Busy from"); } -VOID L3CONNECTFAILED(struct _LINKTABLE * LINK); + +VOID L3LINKSETUPFAILED(struct _LINKTABLE * LINK); + VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) { @@ -3651,7 +4022,7 @@ VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) if (LINK->LINKTYPE == 3) { - L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3 + L3LINKSETUPFAILED(LINK); // REPORT TO LEVEL 3 return; } @@ -4178,6 +4549,33 @@ int seeifUnlockneeded(struct _LINKTABLE * LINK) return 0; } +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len) +{ + z_stream defstream; + int maxSize; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + if (maxSize > OutSize) + return 0; + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + return defstream.total_out; +} + diff --git a/L3Code.c b/L3Code.c index b4f3d65..48f71bc 100644 --- a/L3Code.c +++ b/L3Code.c @@ -67,6 +67,8 @@ extern dest_list * CURRENTNODE; int L3_10SECS = 10; +extern int PREFERINP3ROUTES; + VOID L3BG() { @@ -135,12 +137,14 @@ VOID L3BG() // Drop through to Activate } + // No Active Route + if (ACTIVATE_DEST(DEST) == FALSE) { // Node has no routes - get rid of it REMOVENODE(DEST); - return; // Avoid riskof looking at lod entries + return; // Avoid risk of looking at old entries } } } @@ -154,22 +158,32 @@ BOOL ACTIVATE_DEST(struct DEST_LIST * DEST) { int n = MAXDESTS; struct PORTCONTROL * PORT = PORTTABLE; - struct ROUTE * ROUTE; + struct ROUTE * ROUTE = NULL; struct _LINKTABLE * LINK; struct TNCINFO * TNC; int ActiveRoute; - if (DEST->DEST_ROUTE == 0) // ALREADY HAVE A SELECTED ROUTE? - DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + if (DEST->DEST_ROUTE == 0) // Don't ALREADY HAVE A SELECTED ROUTE? + { + DEST->DEST_ROUTE = 1; + if (PREFERINP3ROUTES) + { + // if we have any INP3 routes use the first. It will always be the fastest. The others are there for fallback if the first fails. + + if (ROUTE = DEST->INP3ROUTE[0].ROUT_NEIGHBOUR) + DEST->DEST_ROUTE = 4; // TRY TO ACTIVATE FIRST + } + } + ActiveRoute = DEST->DEST_ROUTE - 1; ROUTE = DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR; if (ROUTE == 0) { - // Currnet Route not present + // Current Route not present // If current route is 1, then we must have INP3 routes (or entry is corrupt) if (DEST->DEST_ROUTE != 1) @@ -177,13 +191,17 @@ BOOL ACTIVATE_DEST(struct DEST_LIST * DEST) // Current Route is 1 - if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + if (DEST->INP3ROUTE[0].ROUT_NEIGHBOUR == 0) return FALSE; // No INP3 so No Routes DEST->DEST_ROUTE = 4; // First INP3 - ROUTE = DEST->ROUTE[0].ROUT_NEIGHBOUR; + ROUTE = DEST->INP3ROUTE[0].ROUT_NEIGHBOUR; } + if (ROUTE == 0) // Shouldn't happen + return FALSE; + + // if NetROM over VARA conection is made by the driver TNC = TNCInfo[ROUTE->NEIGHBOUR_PORT]; @@ -204,7 +222,7 @@ BOOL ACTIVATE_DEST(struct DEST_LIST * DEST) return L2SETUPCROSSLINK(ROUTE); } - // We mst be waiting for link to come up + // We umst be waiting for link to come up return TRUE; @@ -707,11 +725,11 @@ int COUNTNODES(struct ROUTE * ROUTE) count++; else if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == ROUTE) count++; - else if (DEST->ROUTE[0].ROUT_NEIGHBOUR == ROUTE) + else if (DEST->INP3ROUTE[0].ROUT_NEIGHBOUR == ROUTE) count++; - else if (DEST->ROUTE[1].ROUT_NEIGHBOUR == ROUTE) + else if (DEST->INP3ROUTE[1].ROUT_NEIGHBOUR == ROUTE) count++; - else if (DEST->ROUTE[2].ROUT_NEIGHBOUR == ROUTE) + else if (DEST->INP3ROUTE[2].ROUT_NEIGHBOUR == ROUTE) count++; DEST++; @@ -845,7 +863,7 @@ VOID SENDNEXTNODESFRAGMENT() if (DEST->DEST_CALL[0] != 0x40 && DEST->NRROUTE[0].ROUT_QUALITY >= TXMINQUAL && DEST->NRROUTE[0].ROUT_OBSCOUNT >= OBSMIN && - (NODE == 1 || DEST->DEST_STATE & 0x80)) // Only send appl nodes if DEST = 0; + (NODE == 1 || DEST->DEST_STATE & 0x80)) // Only send appl nodes if NODE = 0; { // Send it @@ -878,6 +896,9 @@ VOID SENDNEXTNODESFRAGMENT() *(ptr1++) = (UCHAR)Qual; + if (Qual == 0) + continue; + Count--; } DEST++; @@ -924,7 +945,7 @@ VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason) VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason) { - // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // FIND ANY DESINATIONS WITH ROUTE AS ACTIVE NEIGHBOUR, AND // SET INACTIVE dest_list * DEST; @@ -945,7 +966,7 @@ VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason) if (DEST->DEST_ROUTE == 0) continue; - if (DEST->ROUTE[DEST->DEST_ROUTE].ROUT_NEIGHBOUR == ROUTE) // Is this the active route + if (DEST->INP3ROUTE[DEST->DEST_ROUTE].ROUT_NEIGHBOUR == ROUTE) // Is this the active route { // Yes, so clear @@ -1120,7 +1141,7 @@ UPDEST000: { // Any INP3 Routes? - if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + if (DEST->INP3ROUTE[0].ROUT_NEIGHBOUR == 0) { // NO ROUTES LEFT TO DEST - REMOVE IT @@ -1292,19 +1313,25 @@ VOID REMOVENODE(dest_list * DEST) NUMBEROFNODES--; } -VOID L3CONNECTFAILED(struct _LINKTABLE * LINK) +VOID L3LINKSETUPFAILED(struct _LINKTABLE * LINK) { // L2 LINK SETUP HAS FAILED - SEE IF ANOTHER NEIGHBOUR CAN BE USED struct PORTCONTROL * PORT = PORTTABLE; struct ROUTE * ROUTE; - ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR if (ROUTE == NULL) return; // NOTHING ??? - + + if (ROUTE->INP3Node) + { + char Normcall[10]; + Normcall[ConvFromAX25(ROUTE->NEIGHBOUR_CALL, Normcall)] = 0; + Debugprintf("INP3 Route to %s connect failed", Normcall); + } + TellINP3LinkSetupFailed(ROUTE); ROUTE->NEIGHBOUR_LINK = 0; // CLEAR IT @@ -1313,9 +1340,10 @@ VOID L3CONNECTFAILED(struct _LINKTABLE * LINK) } + VOID L3TRYNEXTDEST(struct ROUTE * ROUTE) { - // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // FIND ANY DESINATIONS WITH ROUTE AS ACTIVE NEIGHBOUR, AND // SET NEXT BEST NEIGHBOUR (IF ANY) ACTIVE int n = MAXDESTS; @@ -1328,7 +1356,7 @@ VOID L3TRYNEXTDEST(struct ROUTE * ROUTE) if (ActiveRoute) { - ActiveRoute --; // Routes numbered 1 - 6, idex from 0 + ActiveRoute --; // Routes numbered 1 - 6, index from 0 if (DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR == ROUTE) { diff --git a/L4Code.c b/L4Code.c index 1c92cb4..703168a 100644 --- a/L4Code.c +++ b/L4Code.c @@ -67,18 +67,23 @@ VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall); void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); void SendVARANetromMsg(struct TNCINFO * TNC, PL3MESSAGEBUFFER MSG); -unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); - +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); static UINT APPLMASK; extern BOOL LogL4Connects; extern BOOL LogAllConnects; + extern int L4Compress; extern int L4CompMaxframe; extern int L4CompPaclen; + +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; + // L4 Flags Values #define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED @@ -299,6 +304,7 @@ VOID SENDL4MESSAGE(TRANSPORTENTRY * L4, struct DATAMESSAGE * Msg) return; } + L3MSG = GetBuff(); if (L3MSG == 0) @@ -567,6 +573,55 @@ void sendChunk(TRANSPORTENTRY * L4, unsigned char * Compressed, int complen, int } } +void sendL2Chunk(struct _LINKTABLE * LINK, unsigned char * Compressed, int complen, int sendPacLen) +{ + unsigned char * compdata; + struct DATAMESSAGE * Msg; + int sendLen = complen; + int fragments; + + LINK->SentAfterCompression += complen; + + if (complen > L2CompPaclen) + { + fragments = (complen / sendPacLen); // Split to roughly equal sized fraagments + + if (fragments * sendPacLen != complen) + fragments++; + + sendLen = (complen / fragments) + 1; + } + + Debugprintf("L2 Chunk %d Bytes %d PACLEN %d Fragments %d FragSize", complen, sendPacLen, fragments, sendLen); + + compdata = Compressed; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; // 1 for pid field + Msg->PID = PID; // Not sent so use as a flag for compressed msg + + compdata += sendLen; + complen -= sendLen; + + C_Q_ADD(&LINK->TX_Q, Msg); + } +} + + VOID L4BG() { // PROCESS DATA QUEUED ON SESSIONS @@ -628,7 +683,7 @@ VOID L4BG() LINK = L4->L4TARGET.LINK; - if (COUNT_AT_L2(LINK) > 8) + if (COUNT_AT_L2(LINK) > 64) break; } @@ -666,36 +721,32 @@ VOID L4BG() if (L4->AllowCompress) { int complen = 0; - unsigned char * Compressed; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; int dataLen; int savePort = Msg->PORT; int maxCompSendLen; // Save first packet, then see if more on TX_Q - L4->toCompress = malloc(8192); - L4->toCompressLen = 0; - dataLen = Msg->LENGTH - MSGHDDRLEN - 1; // No header or pid L4->Sent += dataLen; - memcpy(&L4->toCompress[L4->toCompressLen], Msg->L2DATA, dataLen); - L4->toCompressLen += dataLen; + memcpy(&toCompress[toCompressLen], Msg->L2DATA, dataLen); + toCompressLen += dataLen; // See if first will compress. If not assume too short or already compressed data and just send - Compressed = Compressit(L4->toCompress, L4->toCompressLen, &complen); + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); if (complen >= dataLen) { - free(Compressed); L4->SentAfterCompression += dataLen; SENDL4MESSAGE(L4, Msg); ReleaseBuffer(Msg); - free(L4->toCompress); - L4->toCompress = 0; - L4->toCompressLen = 0; + toCompressLen = 0; continue; } @@ -705,7 +756,7 @@ VOID L4BG() { // no more, so just send the stuff we've just compressed. Compressed data will fit in input packet -// Debugprintf("%d %d %d%%", L4->toCompressLen, complen, ((L4->toCompressLen - complen) * 100) / L4->toCompressLen); +// Debugprintf("%d %d %d%%", toCompressLen, complen, ((toCompressLen - complen) * 100) / toCompressLen); memcpy(Msg->L2DATA, Compressed, complen); @@ -716,17 +767,13 @@ VOID L4BG() SENDL4MESSAGE(L4, Msg); ReleaseBuffer(Msg); - free(L4->toCompress); - L4->toCompressLen = 0; - L4->toCompress = 0; - free(Compressed); + toCompressLen = 0; continue; } - free(Compressed); ReleaseBuffer(Msg); // Not going to use it - while (L4->L4TX_Q && L4->toCompressLen < (8192 - 256)) // Make sure can't overrin buffer + while (L4->L4TX_Q && toCompressLen < (8192 - 256)) // Make sure can't overrin buffer { // Collect the data from L4TX_Q @@ -734,23 +781,22 @@ VOID L4BG() dataLen = Msg->LENGTH - MSGHDDRLEN - 1; // No header or pid L4->Sent += dataLen; - memcpy(&L4->toCompress[L4->toCompressLen], Msg->L2DATA, dataLen); - L4->toCompressLen += dataLen; + memcpy(&toCompress[toCompressLen], Msg->L2DATA, dataLen); + toCompressLen += dataLen; ReleaseBuffer(Msg); } - L4->toCompress[L4->toCompressLen] = 0; + toCompress[toCompressLen] = 0; - Compressed = Compressit(L4->toCompress, L4->toCompressLen, &complen); -// Debugprintf("%d %d %d%%", L4->toCompressLen, complen, ((L4->toCompressLen - complen) * 100) / L4->toCompressLen); + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + Debugprintf("%d %d %d%%", toCompressLen, complen, ((toCompressLen - complen) * 100) / toCompressLen); // Send compressed // Fragment if more than L4CompPaclen - // Entered with original first fragment in saveMsg; - // Check for too big a compressed frame size. Bigger compresses better but adds latency to link maxCompSendLen = L4CompPaclen * L4CompMaxframe; @@ -762,8 +808,8 @@ VOID L4BG() int Fragments; int ChunkSize; - unsigned char * CompressPtr = L4->toCompress; - int bytesleft = L4->toCompressLen; + unsigned char * CompressPtr = toCompress; + int bytesleft = toCompressLen; // Assume 10% worse compression on smaller input @@ -771,7 +817,7 @@ VOID L4BG() Fragments = j / maxCompSendLen; Fragments++; - ChunkSize = (L4->toCompressLen / Fragments) + 1; // 1 for rounding + ChunkSize = (toCompressLen / Fragments) + 1; // 1 for rounding while (bytesleft > 0) { @@ -779,9 +825,9 @@ VOID L4BG() if (Len > ChunkSize) Len = ChunkSize; - free (Compressed); - Compressed = Compressit(CompressPtr, Len, &complen); -// Debugprintf("Chunked %d %d %d%%", Len, complen, ((Len - complen) * 100) / Len); + complen = L2Compressit(Compressed, 8192, toCompress, Len); + + Debugprintf("Chunked %d %d %d%%", Len, complen, ((Len - complen) * 100) / Len); sendChunk(L4, Compressed, complen, savePort); @@ -793,10 +839,7 @@ VOID L4BG() else sendChunk(L4, Compressed, complen,savePort); - free(L4->toCompress); - L4->toCompressLen = 0; - L4->toCompress = 0; - free(Compressed); + toCompressLen = 0; } else { @@ -808,6 +851,8 @@ VOID L4BG() continue; } + // L2 Link + LINK = L4->L4TARGET.LINK; // If we want to enforce PACLEN this may be a good place to do it @@ -936,9 +981,6 @@ VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session) if (Session->PARTCMDBUFFER) ReleaseBuffer(Session->PARTCMDBUFFER); - if (Session->toCompress) - free(Session->toCompress); - if (Session->unCompress) free(Session->unCompress); diff --git a/LinBPQ.c b/LinBPQ.c index 295afd8..689b5f4 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -381,41 +381,48 @@ BOOL CtrlHandler(DWORD fdwCtrlType) #include // Linux Signal Handlers - static void segvhandler(int sig) { - void *array[10]; - size_t size; - char msg[] = "SIGSEGV Received\n"; + void *array[10]; + size_t size; + char msg[] = "\nSIGSEGV Received\n"; - write(STDERR_FILENO, msg, strlen(msg)); + write(STDERR_FILENO, msg, strlen(msg)); - // get void*'s for all entries on the stack - size = backtrace(array, 10); + // get void*'s for all entries on the stack + size = backtrace(array, 10); - // print out all the frames to stderr + // print out all the frames to stderr - backtrace_symbols_fd(array, size, STDERR_FILENO); + backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); } static void abrthandler(int sig) { - void *array[10]; - size_t size; - char msg[] = "SIGABRT Received\n"; + void *array[10]; + size_t size; + char msg[] = "\nSIGABRT Received\n"; - write(STDERR_FILENO, msg, strlen(msg)); + write(STDERR_FILENO, msg, strlen(msg)); + write(STDOUT_FILENO, msg, strlen(msg)); - // get void*'s for all entries on the stack + // get void*'s for all entries on the stack - size = backtrace(array, 10); - backtrace_symbols_fd(array, size, STDERR_FILENO); + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); } + static void sigterm_handler(int sig) { syslog(LOG_INFO, "terminating on SIGTERM\n"); @@ -787,6 +794,8 @@ int Redirected = 0; static void segvhandler(int sig); static void abrthandler(int sig); +void GetRestartData(); + int main(int argc, char * argv[]) { @@ -845,6 +854,8 @@ int main(int argc, char * argv[]) printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); printf("%s\n", VerCopyright); + srand(time(NULL)); + // look for optarg format parameters @@ -902,7 +913,10 @@ int main(int argc, char * argv[]) Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); -#ifndef MACBPQ +#if defined(MACBPQ) || defined(FREEBSD) + time_t dummy; + _MYTIMEZONE = -localtime(&dummy)->tm_gmtoff; +#else _MYTIMEZONE = _timezone; #endif @@ -1174,6 +1188,8 @@ int main(int argc, char * argv[]) chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); #endif + + // Make backup copies of Databases // CopyConfigFile(ConfigName); @@ -1195,6 +1211,7 @@ int main(int argc, char * argv[]) GetBadWordFile(); GetHTMLForms(); GetPGConfig(); + GetRestartData(); // Make sure there is a user record for the BBS, with BBS bit set. @@ -1442,6 +1459,7 @@ int main(int argc, char * argv[]) SaveMessageDatabase(); SaveBIDDatabase(); SaveConfig(ConfigName); + SaveRestartData(); } KEEPGOING--; // Give time for links to close diff --git a/MHSave.txt b/MHSave.txt deleted file mode 100644 index e69de29..0000000 diff --git a/MULTIPSK64.c b/MULTIPSK64.c new file mode 100644 index 0000000..8fe6a84 --- /dev/null +++ b/MULTIPSK64.c @@ -0,0 +1,1543 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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 +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use MultoPSK ALE400 Mode +// +// Uses BPQ EXTERNAL interface +// + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + + + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static void ConnecttoMPSKThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoMPSK(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); + +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port + +static int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK host + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[200]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd, wtext, 199); + + if (strstr(wtext,"* MULTIPSK")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + TNC->PID = ProcessId; + return FALSE; + } + + return (TRUE); +} + +#endif + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + int len; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, len, 0); + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoMPSK(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock,&readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock,&writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING ||TNC->CONNECTED) FD_SET(TNC->TCPSock,&errorfs); + + if (select((int)TNC->TCPSock+ 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // If required, send signon + + send(TNC->TCPSock,"\x1a", 1, 0); + send(TNC->TCPSock,"DIGITAL MODE ?", 14, 0); + send(TNC->TCPSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPSock,&errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = Stream; + buff->PID = 0xf0; + memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen += MSGHDDRLEN + 1; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + SendData(TNC, buff->L2DATA, txlen); + } + else + { + char Command[80]; + int len; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + if (_memicmp(&buff[8], "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len= sprintf((UCHAR *)&buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (STREAM->Connecting && _memicmp(&buff[8], "ABORT", 5) == 0) + { + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(&buff[8], "MODE", 4) == 0) + { + buff->L2DATA[txlen - 1] = 0; // Remove CR + + len = sprintf(Command,"%cDIGITAL MODE %s\x1b", '\x1a', &buff->L2DATA[5]); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + + if (_memicmp(&buff[8], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + UINT * buffptr = GetBuff(); + int s = 0; + + while(s <= TNC->MPSKInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + + len = sprintf(Command,"%cCALLSIGN_TO_CALL_ARQ_FAE %s%c%cSELECTIVE_CALL_ARQ_FAE\x1b", + '\x1a', STREAM->RemoteCall, '\x1b', '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + STREAM->Connecting = TNC->MPSKInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + + return 0; + } + + // Send any other command to Multipsk + + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + len = sprintf(Command,"%c%s\x1b", '\x1a', buff->L2DATA); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + + } + + return (0); + + case 3: + + Stream = buff->PORT; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->PID = 0; // So we don't try again + + return 0; +} + +static RestartTNC(struct TNCINFO * TNC) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + ret = CreateProcess(TNC->ProgramPath, "MultiPSK TCP_IP_ON", NULL, NULL, FALSE,0 ,NULL ,HomeDir, &SInfo, &PInfo); + + if (ret) + TNC->PID = PInfo.dwProcessId; + + return ret; + } + return 0; +} +#endif + +UINT MPSKExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each MPSK port to be mapped to a BPQ Port + // The MPSK port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_MPSK; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"MPSK Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + +#ifndef LINBPQ + if (MasterPort[port] == port) + { + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + ConnecttoMPSK(port); + } +#endif + time(&lasttime[port]); // Get initial time value + +// SendMessage(0x40eaa, WM_COMMAND, 0x03000eaa, 0x40eaa); + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct MPSKINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->MPSKInfo = zalloc(sizeof(struct MPSKINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "ALEBEACON", 9) == 0) // Send Beacon after each session + TNC->MPSKInfo->Beacon = TRUE; + else + if (_memicmp(buf, "DEFAULTMODE", 11) == 0) // Send Beacon after each session + strcpy(TNC->MPSKInfo->DefaultMode, &buf[12]); + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoMPSK(int port) +{ + _beginthread(ConnecttoMPSKThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoMPSKThread(void * portptr) +{ + + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for MPSK socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for MPSK socket - error code = %d\n", err); + WritetoConsole(Msg); + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connected to MPSK TNC"); + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "MPSK Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessMPSKPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC); +VOID ProcessMSPKComment(struct TNCINFO * TNC); +VOID ProcessMSPKData(struct TNCINFO * TNC); + +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + +/* +3) each text character transmitted by the client to the server (for the Multipsk TX text editor) must be preceded by the character CHR(25) or CHR(22) in the case of a special link (KISS in Packet or Pax, for example). + +4) each command string transmitted by the client to the server must be preceded by the character CHR(26) and finished by CHR(27), + +5) each character effectively transmitted by Multipsk to the transceiver and transmitted to the client is preceded by the character CHR(28), + +6) each character received by Multipsk and transmitted to the client is preceded by the character CHR(29), + +7) each command string transmitted by the server to the client must be preceded by the character CHR(30) and finished by CHR(31), + +8) all commands (written in readable text ) will have an answer (see further for details), + +9) each server comment (Call ID or RS ID reception, switch to RX or to TX) string transmitted by the server to the client must be preceded by a string: "CHR(23)RX CALL ID=", "CHR(23)RX RS ID=", "CHR(23)SWITCH=RX", "CHR(23) SWITCH=TX", and finished by CHR(24). + +10) each server command, for the transceiver control, transmitted by the server to the client must be preceded by the string "CHR(23) XCVR=" and finished by CHR(24). + +Data + +End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] call "THIS I[End of TX] end of link to GM8BPQ[End of TX] sounding "THIS WAS"[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQFAE BEACON OH5RM Kouvola KP30JR +[End of TX] ARQ FAE selective callGM8BPQ DE OH5RM + +[Connection made with OH5RM] + + +18103 but I have to go out to change antenna + +[End of connection with OH5RM]FAE BEACON OH5RM Kouvola KP30JR +S" to GM8BPQ + +10:23:55 AM Comment: SWITCH=RX +10:24:00 AM Comment: RX RS ID=10:24:00 UTC ALE400 1609 Hz 0 MHz +10:24:19 AM Comment: RX RS ID=10:24:19 UTC ALE400 1604 Hz 0 MHz +10:25:04 AM Comment: SWITCH=TX +10:25:07 AM Comment: SWITCH=RX +10:25:15 AM Comment: SWITCH=TX +:30:22 AM Comment: SWITCH=RX +10:30:25 AM Comment: SWITCH=TX +10:30:27 AM Comment: SWITCH=RX +10:30:35 AM Comment: RX RS ID=10:30:35 UTC ALE400 1598 Hz 0 MHz + + +*/ + + // Reuse the HAL CMD and Data Buffers to build messages from TCP stream + + // See if sequence split over a packet boundary + + if (TNC->CmdEsc == 23) + { + TNC->CmdEsc = 0; + goto CommentEsc; + } + + if (TNC->CmdEsc == 29) + { + TNC->CmdEsc = 0; + goto DataEsc; + } + + if (TNC->CmdEsc == 30) + { + TNC->CmdEsc = 0; + goto CmdEsc; + } + + // No Split + + while(Len) + { + switch (*(MPTR++)) + { + case 29: // Data Char + + Len--; + DataEsc: + if (Len) + { + TNC->DataBuffer[TNC->DataLen++] = *MPTR; + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdEsc = 29; + + if (TNC->DataLen) + ProcessMSPKData(TNC); + + + return; // Nothing left + + case 30: + + Len --; + CmdEsc: + while (Len) + { + if (*MPTR == 31) // End of String + { + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 30; + return; // Nothing left + + case 23: // Server Comment + + Len --; + CommentEsc: + while (Len) + { + if (*MPTR == 24) // End of String + { + // Process Comment + + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 23; + return; // Nothing left + + default: + + Len--; + + } +OuterLoop:; + } + + if (TNC->DataLen) + ProcessMSPKData(TNC); +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + + if (strcmp(TNC->CmdBuffer, "SWITCH=TX") == 0) + TNC->MPSKInfo->TX = TRUE; + else + { + if (strcmp(TNC->CmdBuffer, "SWITCH=RX") == 0) + { + TNC->MPSKInfo->TX = FALSE; + + // See if a command was queued while busy + + if (TNC->CmdSet) + { + send(TNC->TCPSock, TNC->CmdSet, (int)strlen(TNC->CmdSet), 0); + free (TNC->CmdSet); + TNC->CmdSet = NULL; + } + } + else + { + Debugprintf("MPSK CMD %s", TNC->CmdBuffer); + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + char * ptr = strstr(TNC->CmdBuffer, "OK"); + + if (ptr) + *(ptr+2) = 0; // Convert OKn to OK for BBS Connect Script + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} %s\r", TNC->CmdBuffer); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + if (strstr(TNC->CmdBuffer, "STOP_SELECTIVE_CALL_ARQ_FAE OK")) + TNC->Streams[0].Connecting = FALSE; + + } + } + } +} + +VOID ProcessMSPKComment(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + Debugprintf("MPSK Comment %s", TNC->CmdBuffer); +} + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i,txptr=0; + UCHAR c; + UCHAR * outbuff = inbuff; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + if (c == 0xc0) + c = inbuff[++i] - 0x20; + + outbuff[txptr++]=c; + } + + return txptr; +} + +VOID ProcessMSPKData(struct TNCINFO * TNC) +{ + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char * ptr; + int Len = TNC->DataLen; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // Process Data + + if (STREAM->Connected) + { + ptr = strstr(TNC->DataBuffer, "[End of connection"); + + if (ptr) + { + // Disconnect + + TNC->DataLen = 0; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + return; + } + + // Pass to Application. Remove any transparency (hex 0xc0 used as an escape) + + buffptr = GetBuff(); + + if (TNC->DataBuffer[TNC->DataLen - 1] == 0xc0) + return; // Last char is an escape, so wait for the escaped char to arrive + + if (buffptr) + { + if (memchr(TNC->DataBuffer, 0xc0, TNC->DataLen)) + TNC->DataLen = UnStuff(TNC->DataBuffer, TNC->DataLen); + + buffptr[1] = TNC->DataLen; + memcpy(&buffptr[2], TNC->DataBuffer, TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->BytesRXed += TNC->DataLen; + } + + TNC->DataLen = 0; + return; + } + + // Not Connected. We get various status messages, including Connection made, + // but they may be split across packets, or have more that one to a packet. + // I think they are all CR/LF terminated . No they aren't! + + // Look for [] this seems to be what is important + +DataLoop: + + if (memcmp(TNC->DataBuffer, "[End of TX] ARQ FAE CQ", 22) == 0) + { + // Remove string from buffer + + if (Len == 22) // Most Likely + { + TNC->DataLen = 0; + return; + } + + TNC->DataLen -= 22; + memmove(TNC->DataBuffer, &TNC->DataBuffer[22], Len - 21); //Copy Null + Len -= 22; + goto DataLoop; + + } + + ptr = strchr(TNC->DataBuffer, '['); + + if (ptr) + { + // Start of a significant Message + + char * eptr = strchr(TNC->DataBuffer, ']'); + char CallFrom[20]; + char * cptr ; + + if (eptr == 0) + return; // wait for matching [] + + cptr = strstr(TNC->DataBuffer, "[Connection made with "); + + // TNC->DataLen -= LineLen; + // memmove(TNC->DataBuffer, &TNC->DataBuffer[LineLen], 1 + Len - LineLen); //Copy Null + // Len -= LineLen; + // goto DataLoop; + + + if (cptr) // Have a connection + { + + // Connected + + memcpy(CallFrom, &cptr[22], 18); + cptr = strchr(CallFrom, ']'); + if (cptr) + *cptr = 0; + + if (STREAM->Connecting) + { + // Connect Complete + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", CallFrom); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + else + { + // Incoming. Look for a free Stream + + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + UpdateMH(TNC, CallFrom, '+', 'I'); + + ProcessIncommingConnect(TNC, CallFrom, Stream, FALSE); + + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(TNC, HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(TNC, &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(TNC, &CTEXTMSG[Next], Len); + } + } + } + } + + } + + // Doesnt contain [ - just discard + + TNC->DataLen = 0; + Debugprintf(TNC->DataBuffer); + return; + +} + + + +/* + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = RXHeader->DataLength; + memcpy(&buffptr[2], Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + return; + + + case 'd': // Disconnected + + + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + */ + + +VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen) +{ + // Preceed each data byte with 25 (decimal) + + char * NewMsg = malloc (MsgLen * 4); + int n; + UCHAR c; + int ExtraLen = 0; + char * ptr = NewMsg; + char * inptr = Msg; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + + TNC->Streams[0].BytesTXed += MsgLen; + + for (n = 0; n < MsgLen; n++) + { + *(ptr++) = 25; + c = *inptr++; + + if (c < 0x20 || c == 0xc0) + { + if (c != 0x0d) + { + *ptr++ = 0x0c0; + *(ptr++) = 25; + *ptr++ = c + 0x20; + ExtraLen += 2; + continue; + } + } + + *ptr++ = c; + } + + send(sock, NewMsg, MsgLen * 2 + ExtraLen, 0); + + free(NewMsg); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Command[80]; + int len; + + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Savde till not transmitting + else + send(TNC->TCPSock, Command, len, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Cmd[80]; + int Len; + + sprintf(Cmd, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Cmd); + + Cmd[0] = 0; + + if (TNC->MPSKInfo->DefaultMode[0]) + sprintf(Cmd, "%cDIGITAL MODE %s\x1b", '\x1a', TNC->MPSKInfo->DefaultMode); + + if (TNC->MPSKInfo->Beacon) + sprintf(Cmd, "%s%cBEACON_ARQ_FAE\x1b", Cmd, '\x1a'); + + Len = (int)strlen(Cmd); + + if(Len) + { + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, Len, 0); + } +} + diff --git a/Moncode.c b/Moncode.c index 1de165b..b539f1f 100644 --- a/Moncode.c +++ b/Moncode.c @@ -535,6 +535,16 @@ KC6OAR*>ID: Output += sprintf((char *)Output, " RX Window=%d", value); break; + + case 16: + + Output += sprintf((char *)Output, " Can Compress"); + break; + + case 17: + + Output += sprintf((char *)Output, " Compress ok"); + break; } } } @@ -551,6 +561,15 @@ KC6OAR*>ID: switch (ADJBUFFER->PID) { + case 0xF1: + case 0xF2: + + // Compressed L2 Data + + Output += sprintf((char *)Output, " <%d Bytes of Compressed L2 Data>", MsgLen - (19 + sizeof(void *))); + + break; + case 0xF0: // Normal Data { char Infofield[257]; diff --git a/MultiConsole.c b/MultiConsole.c index 81b3530..f574b53 100644 --- a/MultiConsole.c +++ b/MultiConsole.c @@ -919,6 +919,9 @@ LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) DoRefresh(Cinfo); + Cinfo->Console->bytesRxed += Cinfo->kbptr+1; + + if (Cinfo->Console->SysopChatStream) SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, &Cinfo->kbbuf[0], Cinfo->kbptr+1); else diff --git a/RHP.c b/RHP.c index 18b933d..a82b091 100644 --- a/RHP.c +++ b/RHP.c @@ -451,8 +451,13 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) int ID; char * Data; char * ptr; + unsigned char * uptr; int c; int Len; + unsigned int HexCode1; + unsigned int HexCode2; + + int n; int Handle = 1; @@ -470,9 +475,10 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) RHPSession = RHPSessions[Handle - 1]; - // Look for \ escapes + // Look for \ escapes, Can now also get \u00c3 ptr = Data; + Len = strlen(Data); // in case no escapes while (ptr = strchr(ptr, '\\')) { @@ -483,23 +489,60 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) case 'r': *ptr = 13; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; + + case 'u': + + HexCode1 = HexCode2 = 0; + + n = toupper(ptr[2]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n << 4; + + n = toupper(ptr[3]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n; + + n = toupper(ptr[4]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n << 4; + + n = toupper(ptr[5]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n; + + if (HexCode1 == 0 || HexCode1 == 0xC2) + { + uptr = ptr; + *uptr = HexCode2; + } + else if (HexCode1 == 0xc2) + { + uptr = ptr; + *uptr = HexCode2 + 0x40; + } + + memmove(ptr + 1, ptr + 6, strlen(ptr + 5)); + break; + case '\\': *ptr = '\\'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; case '"': *ptr = '"'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; } - memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); ptr++; + Len = ptr - Data; } - Len = strlen(Data); ptr = Data; while (Len > RHPPaclen) @@ -640,7 +683,7 @@ void RHPPoll() // Message is JSON so Convert CR to \r, \ to \\ " to \" - // Looks like I need to escape everything not between 0x20 and 0x7f eg \U00c3 + // Looks like I need to escape everything not between 0x20 and 0x7f eg \u00c3 while (c = *(ptr)) diff --git a/RTKnown.txt b/RTKnown.txt deleted file mode 100644 index e69de29..0000000 diff --git a/RigControl.c b/RigControl.c index 90349fe..c2b7c4d 100644 --- a/RigControl.c +++ b/RigControl.c @@ -5293,6 +5293,7 @@ BOOL DecodeModePtr(char * Param, double * Dwell, double * Freq, char * Mode, if (ptr == NULL || strlen(ptr) > 8) return FALSE; // Mode Missing + // If channel, dont need mode if (*MemoryNumber == 0) { diff --git a/SCSTrackeMulti64.c b/SCSTrackeMulti64.c new file mode 100644 index 0000000..a7dc3bf --- /dev/null +++ b/SCSTrackeMulti64.c @@ -0,0 +1,1714 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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 +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface DED Host Mode TNCs to BPQ32 switch +// +// Uses BPQ EXTERNAL interface + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#define MaxStreams 10 + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +static char ClassName[]="TRACKERSTATUS"; +static char WindowTitle[] = "SCS Tracker"; +static int RigControlRow = 140; + +#define NARROWMODE 30 +#define WIDEMODE 30 // PIII only + +extern UCHAR BPQDirectory[]; + +extern char * PortConfig[33]; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +VOID __cdecl Debugprintf(const char * format, ...); +char * strlop(char * buf, char delim); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); + +char NodeCall[11]; // Nodecall, Null Terminated +void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + char * p_port = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + TNC->PacketChannels = 10; // Default + + goto ConfigLine; + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "APPL", 4) == 0) + { + } + else + if (_memicmp(buf, "RIGCONTROL", 10) == 0) + { + } + else + + if (_memicmp(buf, "SWITCHMODES", 11) == 0) + { + } + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0) + { +// TNC->UseAPPLCalls = TRUE; + } + else if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log + TNC->WRITELOG = atoi(&buf[8]); + else + if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) + { + } + else + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + } + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) + + // Packet Channels + + TNC->PacketChannels = atoi(&buf[14]); + else + strcat (TNC->InitScript, buf); + } + return (TRUE); + +} + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void DEDCheckRX(struct TNCINFO * TNC); +static VOID DEDPoll(int Port); +VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +static VOID ProcessDEDFrame(struct TNCINFO * TNC); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID ExitHost(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type); +VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToRPacket(struct TNCINFO * TNC); +VOID SwitchToNormPacket(struct TNCINFO * TNC); + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + unsigned int txlen = 0; + + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; + + if (TNC->hDevice == 0) + { + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (fn > 3 && fn < 6) + goto ok; + + // Try to reopen every 30 secs + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 300) + return 0; + + TNC->ReopenTimer = 0; + + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + if (TNC->hDevice == 0) + return 0; + } +ok: + switch (fn) + { + case 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + DEDCheckRX(TNC); + DEDPoll(port); + DEDCheckRX(TNC); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); // Neded for arm5 portability + + // buff[5]=(datalen & 0xff); + // buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = 21; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + TNCOK = (TNC->HostMode == 1 && TNC->ReinitState != 10); + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (STREAM->FramesOutstanding > 4) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + ExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: + + return 0; // No scan interface +} + return 0; +} + +void * TrackerMExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + char YCmd[10]; + + // + // Will be called once for each DED Host TNC Port + // The COM port number is in IOBASE + // + + sprintf(msg,"SCSTRK M %s", PortEntry->PORTCONTROL.SerialPortName); + + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_TRKM; + + // Set up DED addresses for streams + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; // DED Stream = BPQ Stream (We don't use Stream 0) + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; //TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = NONE; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PORTCONTROL.UICAPABLE = 1; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TempScript = malloc(1000); + + strcpy(TempScript, "M UISC\r"); + strcat(TempScript, "F 200\r"); // Sets SABM retry time to about 5 secs + strcat(TempScript, "%F 1500\r"); // Tones may be changed but I want this as standard + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "Z 0\r"); // No Flow Control + sprintf(YCmd, "Y %d\r", TNC->PacketChannels); + strcat(TNC->InitScript, YCmd); + strcat(TNC->InitScript, "E 1\r"); // Echo - Restart process needs echo + + sprintf(msg, "I %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + + OpenCOMMPort(TNC,PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + TNC->InitPtr = TNC->InitScript; + + WritetoConsole("\n"); + + return ExtProc; +} + +static void DEDCheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * ptr; + UCHAR character; + UCHAR * CURSOR; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; // Nothing doing + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + ptr = TNC->RXBuffer; + + CURSOR = &TNC->DEDBuffer[TNC->InputLen]; + + if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) + { + // Probably Signon Message + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + ptr[Length] = 0; + Debugprintf("TRK %s", ptr); + TNC->RXLen = 0; + return; + } + + if (TNC->HostMode == 0) + { + // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back + + if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) + { + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + TNC->HostMode = TRUE; + TNC->HOSTSTATE = 0; + TNC->Timeout = 0; + TNC->RXLen = 0; + return; + } + + // Command is echoed as * command * + + if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 + { + ProcessTermModeResponse(TNC); + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + return; + } + } + + if (TNC->ReinitState == 10) + { + if (Length == 1 && *(ptr) == '.') // 01 echoed as . + { + // TNC is in Term Mode + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + TNC->ReinitState = 0; + TNC->HostMode = 0; + + return; + } + } + + while (Length--) + { + character = *(ptr++); + + if (TNC->HostMode) + { + // n 0 Success (nothing follows) + // n 1 Success (message follows, null terminated) + // n 2 Failure (message follows, null terminated) + // n 3 Link Status (null terminated) + // n 4 Monitor Header (null terminated) + // n 5 Monitor Header (null terminated) + // n 6 Monitor Information (preceeded by length-1) + // n 7 Connect Information (preceeded by length-1) + + + switch(TNC->HOSTSTATE) + { + case 0: // SETCHANNEL + + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + break; + + case 1: // SETMSGTYPE + + TNC->MSGTYPE = character; + + if (character == 0) + { + // Success, no more info + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + break; + } + + if (character > 0 && character < 6) + { + // Null Terminated Response) + + TNC->HOSTSTATE = 5; + CURSOR = &TNC->DEDBuffer[0]; + break; + } + + if (character > 5 && character < 8) + { + TNC->HOSTSTATE = 2; // Get Length + break; + } + + // Invalid + + Debugprintf("TRK - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); + break; + + case 2: // Get Length + + TNC->MSGCOUNT = character; + TNC->MSGCOUNT++; // Param is len - 1 + TNC->MSGLENGTH = TNC->MSGCOUNT; + CURSOR = &TNC->DEDBuffer[0]; + TNC->HOSTSTATE = 3; // Get Data + + break; + + case 5: // Collecting Null Terminated Response + + *(CURSOR++) = character; + + if (character) + continue; // MORE TO COME + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + + break; + + default: + + // RECEIVING Counted Response + + *(CURSOR++) = character; + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + continue; // MORE TO COME + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + } + } + } + + // End of Input - Save buffer position + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + TNC->RXLen = 0; +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'T', TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return TRUE; +} + +static VOID DEDPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach. Set call my session callsign + + int calllen=0; + + TNC->Streams[Stream].Attached = TRUE; + + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER[6] |= 0x60; // Ensure P or T aren't used on ax.25 + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream) //Leave Stream 0 call alone + { + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s", Stream, 1, 1, TNC->Streams[Stream].MyCall); + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Can't use retries, as we have no way of detecting lost chars. Have to re-init on timeout + + if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + Debugprintf("DEDHOST - Link to TNC Lost Port %d", TNC->Port); + TNC->TNCOK = FALSE; + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + TNC->InitPtr = TNC->InitScript; + TNC->HOSTSTATE = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->InitPtr) + { + char * start, * end; + int len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + TNC->InitPtr = NULL; + Debugprintf("TRK - Init Complete Port %d", TNC->Port); + } + else + { + end = strchr(start, 13); + len = (int)(++end - start - 1); // exclude cr + + TNC->InitPtr = end; + + Poll[0] = 0; // Channel + Poll[1] = 1; // Command + Poll[2] = len - 1; + memcpy(&Poll[3], start, len); + + StuffAndSend(TNC, Poll, len + 3); + + return; + + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start + 2) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + end = strchr(start + 3, 0); + len = (int)(++end - start - 1); // exclude cr + TNC->Streams[Stream].CmdSet = end; + + memcpy(&Poll[0], start, len); + Poll[2] = len - 4; + + StuffAndSend(TNC, Poll, len); + + return; + } + } + } + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + UINT * buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen=buffptr[1]; + Buffer = (char *)&buffptr[2]; // Data portion of frame + + Poll[0] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[1] = 0; // Data + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[2] = datalen - 1; + memcpy(&Poll[3], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[1] = 1; // Command + datalen--; // Exclude CR + + if (datalen == 0) // Null Command + { + ReleaseBuffer(buffptr); + return; + } + + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (Stream == 0) + { + // No connects on Stream zero - for mgmt only + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "TRK} Can't Connect after ATTACH\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + return; + + } + + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s%c%c%c%c%s", Stream, 1, 1, + TNC->Streams[Stream].MyCall, 0, Stream, 1, 1, (char *)buffptr+8); + + ReleaseBuffer(buffptr); + + TNC->Streams[Stream].InternalCmd = FALSE; + return; + } + + Poll[2] = datalen - 1; + memcpy(&Poll[3], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(500); + +// sprintf(TNC->Streams[Stream].CmdSet, "I%s\r%s\r", TNC->Streams[Stream].MyCall, buffptr+2); + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[0] = 0; // UI Channel + Poll[1] = 1; // Data + Poll[2] = (int)strlen(CCMD) - 1; + strcpy(&Poll[3], CCMD); + StuffAndSend(TNC, Poll, Poll[2] + 4); + + sprintf(TNC->Streams[0].CmdSet, "%c%c%c%s", 0, 0, 1, Buffer); + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + // if frames outstanding, issue a poll (but not too often) + + TNC->IntCmdDelay++; + + if (TNC->IntCmdDelay > 10) + { + TNC->IntCmdDelay = 0; + + Poll[0] = TNC->Streams[0].DEDStream; + Poll[1] = 0x1; // Command + TNC->Streams[0].InternalCmd = TRUE; + + Poll[2] = 1; // Len-1 + Poll[3] = '@'; + Poll[4] = 'B'; // Buffers + StuffAndSend(TNC, Poll, 5); + return; + } + + // Need to poll all channels . Just Poll zero here, the ProcessMessage will poll next + + Poll[0] = 0; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + + return; + + + Stream = TNC->StreamtoPoll; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + + return; + } + } + + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + TNC->TNCOK = FALSE; + + memcpy(&TNC->TXBuffer[0], "\x18\x1b\r", 2); + TNC->TXLen = 2; + + if (WriteCommBlock(TNC) == FALSE) + { + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + } + + return; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + // Put into Host Mode + + memcpy(Poll, "\x18\x1bJHOST1\r", 9); + + TNC->TXLen = 9; + WriteCommBlock(TNC); + + TNC->ReinitState = 5; + return; + } + + if (TNC->ReinitState == 5) + TNC->ReinitState = 0; + +} + +static VOID DoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + ExitHost(TNC); + + return; + } + + if (TNC->ReinitState == 1) + { + // No Response to trying to enter term mode - do error recovery + + Debugprintf("TRK - Starting Resync Port %d", TNC->Port); + + TNC->ReinitState = 10; + TNC->ReinitCount = 256; + TNC->HostMode = TRUE; // Must be in Host Mode if we need recovery + + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 10; // 2 secs + + return; + } + + if (TNC->ReinitState == 10) + { + // Continue error recovery + + TNC->ReinitCount--; + + if (TNC->ReinitCount) + { + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 3; // 0.3 secs + + return; + } + + // Try Again + + Debugprintf("TRK Continuing recovery Port %d", TNC->Port); + + TNC->ReinitState = 0; + + // Close and re-open TNC + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 290; + TNC->HostMode = FALSE; + + return; + } + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +static VOID ExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[0] = 1; + TNC->TXBuffer[1] = 1; + TNC->TXBuffer[2] = 1; + memcpy(&TNC->TXBuffer[3], "%R", 2); + + StuffAndSend(TNC, Poll, 5); + + return; +} + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', TNC->RXBuffer, TNC->RXLen); + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + } + + if (TNC->ReinitState == 1) + { + // trying to set term mode + + // If already in Term Mode, TNC echos command, with control chars replaced with '.' + + if (memcmp(TNC->RXBuffer, "....%R", 6) == 0) + { + // In term mode, Need to put into Host Mode + + TNC->ReinitState = 2; + DoTNCReinit(TNC); + return; + } + } + + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } + + if (TNC->ReinitState == 5) // Waiting for response to JHOST1 + { + if (TNC->RXBuffer[TNC->RXLen-1] == 10 || TNC->RXBuffer[TNC->RXLen-1] == 13) // NewLine + { + TNC->HostMode = TRUE; + TNC->Timeout = 0; + } + return; + } +} + +static VOID ProcessDEDFrame(struct TNCINFO * TNC) +{ + UINT * buffptr; + char * Buffer; // Data portion of frame + UINT Stream = 0; + UCHAR * Msg = TNC->DEDBuffer; + int framelen = TNC->InputLen; + struct STREAMINFO * STREAM; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', Msg, framelen); + + if (TNC->ReinitState == 10) + { + // Recovering from Sync Failure + + // Any Response indicates we are in host mode, and back in sync + + TNC->HostMode = TRUE; + TNC->Timeout = 0; + TNC->ReinitState = 0; + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + return; + } + + // Any valid frame is an ACK + + TNC->Timeout = 0; + TNC->TNCOK = TRUE; + + if (TNC->InitPtr) // Response to Init Script + return; + + if (TNC->MSGCHANNEL > 26) + return; + + Stream = TNC->MSGCHANNEL; + + // See if Poll Reply or Data + + if (TNC->MSGTYPE == 0) + { + // Success - Nothing Follows + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set or Init Script + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + { + UCHAR * Poll = TNC->TXBuffer; + + // Poll Next Channel (we need to scan all channels every DEDPOLL cycle + + Stream++; + + if (Stream > MaxStreams) + return; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + return; + } + } + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + } + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"TRK} Ok\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE > 0 &&TNC->MSGTYPE < 6) + { + // Success with message - null terminated + + char * ptr; + int len; + + Buffer = Msg; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (TNC->MSGTYPE < 3) // 1 or 2 - Success or Fail + { + // See if a response to internal command + + if (TNC->Streams[Stream].InternalCmd) + { + // Process it + + char LastCmd = TNC->TXBuffer[3]; + + if (LastCmd == 'L') // Status + { + int s1, s2, s3, s4, s5, s6, num; + + num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); + + TNC->Streams[Stream].FramesOutstanding = s3; + return; + } + + if (LastCmd == '@') // @ Commands + { + if (TNC->TXBuffer[4]== 'B') // Buffer Status + { + TNC->Buffers = atoi(Buffer); +// SetDlgItemText(TNC->hDlg, IDC_BUFFERS, Buffer); + return; + } + } + + return; + } + + // Not Internal Command, so send to user + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + return; + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"TRK} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE") || strstr(Buffer, "BUSY")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + return; + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + if (strstr(Buffer, "BUSY")) + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Busy from %s\r", TNC->Streams[Stream].RemoteCall); + else + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + STREAM->DiscWhenAllSent = 15; // Dont want to leave session attached. Causes too much confusion + + return; + } + + // Must Have been connected or disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesOutstanding = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + return; + } + + if (strstr(Buffer, "CONNECTED")) + { + char * Call = strstr(Buffer, " to "); + char * ptr; + char MHCall[30]; + + Call += 4; + + if (Call[1] == ':') + Call +=2; + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strchr(Call, 13); + if (ptr) *ptr = 0; + + STREAM->Connected = TRUE; // Subsequent data to data channel + STREAM->Connecting = FALSE; + + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + memcpy(MHCall, Call, 9); + MHCall[9] = 0; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + +// APPLCALLS * APPL; +// char * ApplPtr = &APPLS; +// int App; +// char Appl[10]; +// char DestCall[10]; + + UpdateMH(TNC, MHCall, '+', 'I'); + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = CTPaclen; + memcpy(&buffptr[2], &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = Len; + memcpy(&buffptr[2], &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + + return; + } + else + { + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + } + } + return; + } + + if (TNC->MSGTYPE == 4 || TNC->MSGTYPE == 5) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; // RP Stream + + // Monitor + +/* + if (TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Attached == FALSE) + { + // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL + + char DestCall[10] = "NOCALL "; + char * ptr1 = strstr(&Msg[7], "to "); + int i; + APPLCALLS * APPL; + char Appl[11]; + char Status[80]; + + if (ptr1) memcpy(DestCall, &ptr1[3], 10); + + ptr1 = strchr(DestCall, ' '); + if (ptr1) *(ptr1) = 0; // Null Terminate + + Debugprintf("RP SABM Received for %s" , DestCall); + + if (strcmp(TNC->NodeCall, DestCall) != 0) + { + // Not Calling NodeCall/Portcall + + if (strcmp(NodeCall, DestCall) == 0) + goto SetThisCall; + + // See if to one of our ApplCalls + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (strcmp(Appl, DestCall) == 0) + { + SetThisCall: + Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); + + sprintf(Status, "%d SCANSTART 60", TNC->Port); // Pause scan for 60 secs + Rig_Command(-1, Status); + TNC->SwitchToPactor = 600; // Don't change modes for 60 secs + + strcpy(STREAM->MyCall, DestCall); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "I%s\r", DestCall); + break; + } + } + } + } + } +*/ + DoMonitorHddr(TNC, Msg, framelen, TNC->MSGTYPE); + return; + + } + + // 1, 2, 4, 5 - pass to Appl + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"Trk} %s", &Msg[4]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 6) + { + // Monitor Data With length) + + DoMonitorData(TNC, Msg, framelen); + return; + } + + if (TNC->MSGTYPE == 7) + { + //char StatusMsg[60]; + //int Status, ISS, Offset; + + // Connected Data + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = framelen; // Length + TNC->Streams[Stream].BytesRXed += buffptr[1]; + memcpy(&buffptr[2], Msg, buffptr[1]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cD", Stream, 1, 1); +} + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ +} + + diff --git a/TNCCode.c b/TNCCode.c index 37cd8e2..5103cfe 100644 --- a/TNCCode.c +++ b/TNCCode.c @@ -322,7 +322,10 @@ Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port) EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; - C_Q_ADD(&EXTPORT->UI_Q, Copy); + if (EXTPORT->UI_Q) + C_Q_ADD(&EXTPORT->UI_Q, Copy); + else + C_Q_ADD(&EXTPORT->UI_Q, Copy); return; } diff --git a/TNCEmulators.c b/TNCEmulators.c index 135d318..7fce453 100644 --- a/TNCEmulators.c +++ b/TNCEmulators.c @@ -241,8 +241,14 @@ typedef struct _SERIAL_STATUS { #ifndef WIN32 -#ifdef MACBPQ +#if defined(MACBPQ) #include +#elif defined(FREEBSD) +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#else +#include +#endif #endif extern int posix_openpt (int __oflag); @@ -262,7 +268,7 @@ HANDLE LinuxOpenPTY(char * Name) u_long param=1; struct termios term; -#ifdef MACBPQ +#if defined(MACBPQ) || defined(FREEBSD) // Create a pty pair diff --git a/TelnetV6.c b/TelnetV6.c index 62b0885..a0a0272 100644 --- a/TelnetV6.c +++ b/TelnetV6.c @@ -1498,6 +1498,8 @@ void * TelnetExtInit(EXTPORTDATA * PortEntry) PortEntry->PORTCONTROL.TNC = TNC; + PortEntry->PORTCONTROL.Hardware = TNC->Hardware; + TNC->WebWindowProc = WebProc; TNC->WebWinX = 260; TNC->WebWinY = 325; @@ -6342,26 +6344,80 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S struct ConnectionInfo * sockptr; SOCKET sock; struct sockaddr_in sinx; - struct sockaddr_in destaddr; int addrlen=sizeof(sinx); - int i; + char PortString[20]; + struct addrinfo hints, *res = 0, *saveres; + + + sprintf(PortString, "%d", Port); + + // get host info, make socket, and connect it + + memset(&hints, 0, sizeof hints); + + if (TCP->IPV6 == 0 && TCP->IPV4) + hints.ai_family = AF_INET; + else if (TCP->IPV4 == 0 && TCP->IPV6) + hints.ai_family = AF_INET6; + else if (TCP->IPV4 && TCP->IPV6) + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + + else + { + ReportError(STREAM, "Neither IPv4 nor IPv5 are enabled"); + return FALSE; // Resolve failed + } + + + hints.ai_socktype = SOCK_STREAM; + getaddrinfo(Host, PortString, &hints, &res); + + if (!res) + { + char Msg[256]; + err = WSAGetLastError(); + sprintf(Msg, "Resolve HostName Failed - Error %d", err); + ReportError(STREAM, Msg); + return FALSE; // Resolve failed + } + + // Step thorough the list of hosts + + saveres = res; // Save for free + +/* if (res->ai_next) // More than one + { + int n = ISHostIndex; + + while (n && res->ai_next) + { + res = res->ai_next; + n--; + } + + if (n) + { + // We have run off the end of the list + + ISHostIndex = 0; // Back to start + res = saveres; + } + else + ISHostIndex++; + + } +*/ +// getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0); sockptr = STREAM->ConnectionInfo; - sock = sockptr->socket = socket(AF_INET, SOCK_STREAM, 0); + sock = sockptr->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (sock == INVALID_SOCKET) - { - ReportError(STREAM, "Create Socket Failed"); - return FALSE; - } - sockptr->SocketActive = TRUE; sockptr->InputLen = 0; sockptr->LoginState = 2; sockptr->UserPointer = 0; sockptr->DoEcho = FALSE; - sockptr->FBBMode = FBB; // Raw Data if (sockptr->ADIF == NULL) @@ -6370,49 +6426,19 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S memset(sockptr->ADIF, 0, sizeof(struct ADIF)); - // Resolve Name if needed - - sockptr->sin.sin_family = AF_INET; - sockptr->sin.sin_port = htons(Port); - - sockptr->sin.sin_addr.s_addr = inet_addr(Host); - - if (sockptr->sin.sin_addr.s_addr == INADDR_NONE) + if (sock == INVALID_SOCKET) { - struct hostent * HostEnt; - - // Resolve name to address - - HostEnt = gethostbyname(Host); - - if (!HostEnt) - { - ReportError(STREAM, "Resolve HostName Failed"); - return FALSE; // Resolve failed - } - i = 0; - while (HostEnt->h_addr_list[i] != 0) - { - struct in_addr addr; - addr.s_addr = *(u_long *) HostEnt->h_addr_list[i++]; - } - memcpy(&sockptr->sin.sin_addr.s_addr, HostEnt->h_addr, 4); + ReportError(STREAM, "Create Socket Failed"); + return FALSE; } - ioctl (sockptr->socket, FIONBIO, ¶m); - - setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + ioctl (sock, FIONBIO, ¶m); - sinx.sin_family = AF_INET; - sinx.sin_addr.s_addr = INADDR_ANY; - sinx.sin_port = 0; + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); - if (bind(sockptr->socket, (struct sockaddr *) &sinx, addrlen) != 0 ) - { - ReportError(STREAM, "Bind Failed"); - return FALSE; - } +// memcpy(work, res->ai_addr->sa_data, 4); + if (LogEnabled) { char logmsg[512]; @@ -6421,21 +6447,21 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S WriteLog (logmsg); } - - if (connect(sockptr->socket,(struct sockaddr *) &sockptr->sin, sizeof(destaddr)) == 0) + if (connect(sock, res->ai_addr, (int)res->ai_addrlen) == 0) { // // Connected successful // ReportError(STREAM, "*** Connected"); - - // Get Send Buffer Size + freeaddrinfo(saveres); return TRUE; } else { + freeaddrinfo(saveres); + err=WSAGetLastError(); if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK @@ -6461,7 +6487,6 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S } return FALSE; - } diff --git a/UZ7HODrv.c b/UZ7HODrv.c index 8c36e2b..ac33278 100644 --- a/UZ7HODrv.c +++ b/UZ7HODrv.c @@ -685,6 +685,11 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) ConnecttoUZ7HO(port); lasttime[port] = ltime; } + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } } else { diff --git a/VARA.c b/VARA.c index 4d46984..4574fa4 100644 --- a/VARA.c +++ b/VARA.c @@ -1720,7 +1720,6 @@ VConnected: GetSemaphore(&Semaphore, 52); VARAProcessReceivedControl(TNC); FreeSemaphore(&Semaphore); - Debugprintf("VARA Returned from processing control packet"); } if (FD_ISSET(TNC->TCPDataSock, &readfs)) diff --git a/Versions.h b/Versions.h index bf6fb8a..061e9eb 100644 --- a/Versions.h +++ b/Versions.h @@ -10,14 +10,15 @@ #endif -#define KVers 6,0,24,66 -#define KVerstring "6.0.24.66\0" +#define KVers 6,0,24,77 +#define KVerstring "6.0.24.77\0" + #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "February 2025" +#define Datestring "July 2025" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/WINMOR.c b/WINMOR.c index f0f9d98..2a3d766 100644 --- a/WINMOR.c +++ b/WINMOR.c @@ -139,6 +139,10 @@ VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) int LineLen, i; UCHAR Save; int SaveLen = Len; + char Time[16]; + time_t T; + struct tm * tm; + if (Len < 0) return; @@ -206,10 +210,16 @@ lineloop: #endif // Write to Web Buffer + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + + strcat(TNC->WebBuffer, Time); strcat(TNC->WebBuffer, Line); strcat(TNC->WebBuffer, "\r\n"); if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved Skip: ptr1 = ptr2; @@ -248,10 +258,16 @@ lineloop: #else index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); #endif + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + strcat(TNC->WebBuffer, Time); + strcat(TNC->WebBuffer, ptr1); strcat(TNC->WebBuffer, "\r\n"); if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved } } diff --git a/WPRoutines.c b/WPRoutines.c index 0658ecc..f703662 100644 --- a/WPRoutines.c +++ b/WPRoutines.c @@ -37,6 +37,7 @@ VOID Do_Save_WPRec(HWND hDlg); VOID SaveInt64Value(config_setting_t * group, char * name, long long value); VOID SaveIntValue(config_setting_t * group, char * name, int value); VOID SaveStringValue(config_setting_t * group, char * name, char * value); +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); void MQTTMessageEvent(void* message); WPRec * AllocateWPRecord() diff --git a/WebMail.c b/WebMail.c index a3ff5b1..2c97f84 100644 --- a/WebMail.c +++ b/WebMail.c @@ -1446,7 +1446,12 @@ void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL // Neither do js or file downloads - if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + // This could be a request for a Template file + // WebMail/Local_Templates/My Forms/inc/logo_ad63.png + // WebMail/Standard Templates/ + + + if (_memicmp(NodeURL, "/WebMail/Local", 14) == 0 || (_memicmp(NodeURL, "/WebMail/Standard", 17) == 0)) { int FileSize; char * MsgBytes; @@ -1456,9 +1461,10 @@ void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL char TimeString[64]; char FileTimeString[64]; struct stat STAT; - char * FN = &NodeURL[16]; + char * FN = &NodeURL[9]; char * fileBit = FN; char * ext; + char Type[64] = "Content-Type: text/html\r\n"; UndoTransparency(FN); ext = strchr(FN, '.'); @@ -1491,27 +1497,112 @@ void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL FormatTime2(FileTimeString, STAT.st_ctime); FormatTime2(TimeString, time(NULL)); - if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0) - { - *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" - "Content-Type: text/css\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes); - } - else - { - *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" - "Content-Type: application/octet-stream\r\n" - "Content-Disposition: attachment; filename=\"%s\"\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "\r\n%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes); + ext++; + + if (_stricmp(ext, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ext, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); - } + if (_stricmp(ext, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 || + _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + // File may be binary so output header then copy in message + + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n", FileSize, Type,TimeString, FileTimeString); + + memcpy(&Reply[*RLen], MsgBytes, FileSize); + *RLen += FileSize; free (MsgBytes); return; - } + } + + // + + if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[16]; + char * fileBit = FN; + char * ext; + char Type[64] = "Content-Type: text/html\r\n"; + + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + ext++; + + if (_stricmp(ext, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ext, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); + + if (_stricmp(ext, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 || + _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + // File may be binary so output header then copy in message + + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n", FileSize, Type,TimeString, FileTimeString); + + memcpy(&Reply[*RLen], MsgBytes, FileSize); + *RLen += FileSize; + free (MsgBytes); + return; + } Session = NULL; @@ -2731,6 +2822,18 @@ VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * R strcpy(Msg->via, ToUser->HomeBBS); sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); } + else + { + // No HomeBBS - check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } } else { @@ -2883,7 +2986,7 @@ VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * R // RMS Express Forms Support -char * GetHTMLViewerTemplate(char * FN) +char * GetHTMLViewerTemplate(char * FN, struct HtmlFormDir ** FormDir) { int i, j, k, l; @@ -2897,6 +3000,7 @@ char * GetHTMLViewerTemplate(char * FN) { if (strcmp(FN, Dir->Forms[j]->FileName) == 0) { + *FormDir = Dir; return CheckFile(Dir, FN); } } @@ -2917,6 +3021,7 @@ char * GetHTMLViewerTemplate(char * FN) { if (_stricmp(FN, SDir->Forms[k]->FileName) == 0) { + *FormDir = SDir; return CheckFile(SDir, SDir->Forms[k]->FileName); } } @@ -2980,6 +3085,13 @@ VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL) ptr = strchr(&NodeURL[17], ','); Dir = HtmlFormDirs[DirNo]; + + if (DirNo == -1) + { + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + SubDir = strlop(&NodeURL[17], ':'); if (SubDir) { @@ -3293,6 +3405,13 @@ BOOL ParseXML(WebMailInfo * WebMail, char * XMLOrig) // Extract Fields (stuff between < and >. Ignore Whitespace between fields + // Add FormFolder Key with our folder + +// XMLKeys->Key = "FormFolder"; +// XMLKeys->Value = _strdup(FormDir); + +// XMLKeys++; + ptr1 = strstr(XML, ""); while (ptr1) @@ -3396,6 +3515,8 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch size_t varlen, xmllen; char var[100] = "<"; KeyValues * KeyValue; + struct HtmlFormDir * Dir; + char FormDir[MAX_PATH]; if (ParseXML(WebMail, XML)) ptr = FindXMLVariable(WebMail, "display_form"); @@ -3409,7 +3530,11 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch strcpy(FN, ptr); - Form = GetHTMLViewerTemplate(FN); + Form = GetHTMLViewerTemplate(FN, &Dir); + + sprintf(FormDir, "WMFile/%s/%s/", Dir->FormSet, Dir->DirName); + + if (Form == NULL) { @@ -3425,6 +3550,15 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch // Don't know why, but {MsgOriginalBody} is sent instead of {var MsgOriginalBody} + // So is {FormFolder} instread of {var FormFolder} + + // As a fiddle replace {FormFolder} with {var Folder} and look for that + + while (varptr = stristr(Form, "{FormFolder}")) + { + memcpy(varptr, "{var ", 5); + } + varptr = stristr(Form, "{MsgOriginalBody}"); { char * temp, * tempsave; @@ -3564,6 +3698,23 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch while (KeyValue->Key) { + if (_stricmp(var, "Folder") == 0) + { + // Local form folder, not senders + + xmllen = strlen(FormDir); + + // Ok, we have the position of the variable and the substitution text. + // Copy message up to variable to Result, then copy value + + memcpy(Reply, formptr, varptr - formptr - 5); // omit "{var " + Reply += (varptr - formptr - 5); + + strcpy(Reply, FormDir); + Reply += xmllen; + break; + } + if (_stricmp(var, KeyValue->Key) == 0) { xmllen = strlen(KeyValue->Value); @@ -3579,6 +3730,8 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch break; } + KeyValue++; + if (KeyValue->Key == NULL) { // Not found in XML @@ -3588,7 +3741,7 @@ int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, ch sprintf(Err, VarNotFoundMsg, var, "%s"); return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err); } - KeyValue++; + } formptr = endptr + 1; @@ -4098,6 +4251,18 @@ VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, cha strcpy(Msg->via, ToUser->HomeBBS); sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); } + else + { + // No HomeBBS - Check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } } else { @@ -4318,6 +4483,18 @@ VOID BuildMessageFromHTMLInput(struct HTTPConnectionInfo * Session, char * Reply strcpy(Msg->via, ToUser->HomeBBS); sprintf(Prompt, "%s added from HomeBBS", Msg->via); } + else + { + // No HomeBBS - Check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } } else { @@ -5448,8 +5625,6 @@ char * CheckFile(struct HtmlFormDir * Dir, char * FN) #endif - printf("%s\n", MsgFile); - if (stat(MsgFile, &STAT) != -1) { hFile = fopen(MsgFile, "rb"); @@ -5466,8 +5641,6 @@ char * CheckFile(struct HtmlFormDir * Dir, char * FN) MsgBytes[FileSize] = 0; fclose(hFile); - printf("%d %s\n", strlen(MsgBytes), MsgBytes); - return MsgBytes; } return NULL; diff --git a/WinRPR.c b/WinRPR.c index fd4fb94..f2d8791 100644 --- a/WinRPR.c +++ b/WinRPR.c @@ -1580,8 +1580,6 @@ TNCRunning: // Send INIT script - // VARA needs each command in a separate send - ptr1 = &TNC->InitScript[0]; GetSemaphore(&Semaphore, 52); @@ -1606,7 +1604,6 @@ TNCRunning: c = *(ptr2 + 1); // Save next char *(ptr2 + 1) = 0; // Terminate string } -// VARASendCommand(TNC, ptr1, TRUE); if (ptr2) *(1 + ptr2++) = c; // Put char back diff --git a/asmstrucs.h b/asmstrucs.h index 343ea83..79e9a26 100644 --- a/asmstrucs.h +++ b/asmstrucs.h @@ -176,8 +176,6 @@ typedef struct _TRANSPORTENTRY // We collect as much data as possible before compressing and re-packetizing int AllowCompress; - unsigned char * toCompress; // Data being saved to compress - int toCompressLen; unsigned char * unCompress; // Data being saved to uncompress int unCompressLen; @@ -255,6 +253,7 @@ typedef struct ROUTE #define GotRTTResponse 2 // Other end has sent us a RTT Response #define GotRIF 4 // Other end has sent RIF, so is probably an INP3 Node // (could just be monitoring RTT for some reason +#define SentRTTRequest 8 #define SentOurRIF 16 // Set when we have sent a rif for our Call and any ApplCalls // (only sent when we have seen both a request and response) @@ -462,16 +461,18 @@ typedef struct _APPLCALLS // This way our times adjust to changes of neighbour SRTT. We can't cater for changes to other hop RTTs, // But if these are significant (say 25% or 100 ms) they will be retransmitted +// We treat the Routes as an array of 6. First 3 are NODES routes, next 3 are INP3 Routes. This works, but maybe is not ideal + typedef struct NR_DEST_ROUTE_ENTRY { struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH UCHAR ROUT_QUALITY; // QUALITY UCHAR ROUT_OBSCOUNT; UCHAR ROUT_LOCKED; - UCHAR Padding[4]; // SO Entries are the same length + UCHAR Padding[4]; // So Entries are the same length } *PNR_DEST_ROUTE_ENTRY; -typedef struct DEST_ROUTE_ENTRY +typedef struct INP3_DEST_ROUTE_ENTRY { struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH USHORT LastRTT; // Last Value Reported @@ -495,8 +496,8 @@ typedef struct DEST_LIST UCHAR DEST_ROUTE; // CURRENTY ACTIVE DESTINATION UCHAR INP3FLAGS; - struct NR_DEST_ROUTE_ENTRY NRROUTE[3];// Best 3 NR neighbours for this dest - struct DEST_ROUTE_ENTRY ROUTE[3]; // Best 3 INP neighbours for this dest + struct NR_DEST_ROUTE_ENTRY NRROUTE[3]; // Best 3 NR neighbours for this dest + struct INP3_DEST_ROUTE_ENTRY INP3ROUTE[3]; // Best 3 INP neighbours for this dest void * DEST_Q; // QUEUE OF FRAMES FOR THIS DESTINATION @@ -700,6 +701,7 @@ typedef struct PORTCONTROL BOOL NormalizeQuality; // Normalise Node Qualities BOOL IgnoreUnlocked; // Ignore Unlocked routes BOOL INP3ONLY; // Default to INP3 and disallow NODES + BOOL ALLOWINP3; void (* UIHook)(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); // Used for KISSARQ struct PORTCONTROL * HookPort; @@ -725,6 +727,9 @@ typedef struct PORTCONTROL UCHAR * TX; // % Sending UCHAR * BUSY; // % Active (Normally DCD active or TX) + int Hardware; // TNC H_TYPE. Copied here for access from application context + + } PORTCONTROLX, *PPORTCONTROL; typedef struct FULLPORTDATA @@ -959,6 +964,21 @@ typedef struct _LINKTABLE int bytesRXed; // Info bytes only int bytesTXed; + // Now support compressing L2 Sessions. + // We collect as much data as possible before compressing and re-packetizing + + int AllowCompress; + + unsigned char * unCompress; // Data being saved to uncompress + int unCompressLen; + + int Sent; + int SentAfterCompression; + + int Received; + int ReceivedAfterExpansion; + + } LINKTABLE; #pragma pack(1) @@ -1067,7 +1087,11 @@ struct SEM int Gets; int Rels; DWORD SemProcessID; +#ifdef WIN32 DWORD SemThreadID; +#else + pthread_t SemThreadID; +#endif int Line; // caller file and line char File[MAX_PATH]; }; @@ -1354,6 +1378,8 @@ struct arp_table_entry // SOCKET SourceSocket; struct AXIPPORTINFO * PORT; BOOL noUpdate; // Don't update dest address from incoming packet + BOOL replytoSourcePort; // Update map entry dest port from source port of each packet. + time_t LastHeard; // Last Packet received from this ststiom }; diff --git a/bpq32.h b/bpq32.h index bdc5a20..2bab90a 100644 --- a/bpq32.h +++ b/bpq32.h @@ -22,6 +22,8 @@ count on BPQ32.dll gets messed up, and the code will not unload cleanly. struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portslot); struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int APIENTRY GetPortHardwareType(struct PORTCONTROL *PORT); + // Returns number of free buffers // (BPQHOST function 7 (part)). diff --git a/bpqaxip.c b/bpqaxip.c index 07083dd..d5afb62 100644 --- a/bpqaxip.c +++ b/bpqaxip.c @@ -204,7 +204,7 @@ int Update_MH_KeepAlive(struct AXIPPORTINFO * PORT, struct in_addr ipad, char pr unsigned short int compute_crc(unsigned char *buf,int l); unsigned int find_arp(unsigned char * call); BOOL add_arp_entry(struct AXIPPORTINFO * PORT, unsigned char * call, UCHAR * ip, int len, int port,unsigned char * name, -int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPMode, int SourcePort, BOOL IPv6, int noUpdate); + int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPMode, int SourcePort, BOOL IPv6, int noUpdate, int useSourcePort); BOOL add_bc_entry(struct AXIPPORTINFO * PORT, unsigned char * call, int len); BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call, int * calllen); static BOOL ReadConfigFile(int Port); @@ -213,7 +213,7 @@ int CheckKeepalives(struct AXIPPORTINFO * PORT); BOOL CopyScreentoBuffer(char * buff, struct AXIPPORTINFO * PORT); int DumpFrameInHex(unsigned char * msg, int len); VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, UCHAR * buff, int txlen); -BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int Port, VOID * rxaddr); +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int FromPort, VOID * rxaddr, int ToPort); int DataSocket_Read(struct arp_table_entry * sockptr, SOCKET sock); int GetMessageFromBuffer(struct AXIPPORTINFO * PORT, char * Buffer); int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); @@ -429,7 +429,7 @@ static size_t ExtProc(int fn, int port, PMESSAGE buff) if (PORT->Checkifcanreply) { - if (CheckSourceisResolvable(PORT, call, 0, &RXaddr)) + if (CheckSourceisResolvable(PORT, call, 0, &RXaddr, 0)) return 1; @@ -437,7 +437,7 @@ static size_t ExtProc(int fn, int port, PMESSAGE buff) // Can't reply. If AutoConfig is set, add to table and accept, else reject if (PORT->AutoAddARP) - return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, 0, inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, 0, FALSE, 0); + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, 0, inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, 0, FALSE, 0, 0); else { char From[11] = "|"; @@ -536,7 +536,7 @@ static size_t ExtProc(int fn, int port, PMESSAGE buff) if (PORT->Checkifcanreply) { - if (CheckSourceisResolvable(PORT, call, htons(RXaddr.rxaddr.sin_port), &RXaddr)) + if (CheckSourceisResolvable(PORT, call, htons(RXaddr.rxaddr.sin_port), &RXaddr, PORT->udpport[i])) return 1; else { @@ -547,10 +547,10 @@ static size_t ExtProc(int fn, int port, PMESSAGE buff) { char Addr[80]; Format_Addr((UCHAR *)&RXaddr.rxaddr6.sin6_addr, Addr, TRUE); - return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, 7, htons(RXaddr.rxaddr6.sin6_port), Addr, 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], TRUE, 0); + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, 7, htons(RXaddr.rxaddr6.sin6_port), Addr, 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], TRUE, 0, 0); } else - return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, htons(RXaddr.rxaddr.sin_port), inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], FALSE, 0); + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, htons(RXaddr.rxaddr.sin_port), inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], FALSE, 0, 0); else { char From[11] = "|"; @@ -733,7 +733,7 @@ VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, U return; } - // Seelcte source port by choosing right socket + // Select source port by choosing right socket // First Set Default for Protocol @@ -1343,6 +1343,7 @@ LRESULT FAR PASCAL ConfigWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lPa char axcall[7]; BOOL UDPFlag, BCFlag; struct AXIPPORTINFO * PORT; + int useSourcePort = 0; for (i=1; i <= MAXBPQPORTS; i++) { @@ -1391,7 +1392,7 @@ LRESULT FAR PASCAL ConfigWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lPa { if (convtoax25(call,axcall,&calllen)) { - add_arp_entry(PORT, axcall,0,calllen,port,host,Interval, BCFlag, FALSE, 0, port, FALSE, 0); + add_arp_entry(PORT, axcall,0,calllen,port,host,Interval, BCFlag, FALSE, 0, port, FALSE, 0, useSourcePort); ResolveDelay = 2; return(DestroyWindow(hWnd)); } @@ -2147,6 +2148,7 @@ static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) int Interval; int noUpdate=FALSE; int TCPMode; + int useSourcePort = 0; ptr = strtok(buf, " \t\n\r"); @@ -2234,6 +2236,7 @@ static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) bcflag=0; TCPMode=0; SourcePort = 0; + useSourcePort = 0; // // Look for (optional) KEEPALIVE, DYNAMIC, UDP or BROADCAST params @@ -2264,7 +2267,14 @@ static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) if (p_udpport == NULL) return (FALSE); - port = atoi(p_udpport); + if (_stricmp(p_udpport,"FROMPORT") == 0) + { + useSourcePort = TRUE; + port = 0; + } + else + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); continue; } @@ -2327,7 +2337,7 @@ static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) if (SourcePort == 0) SourcePort = port; - add_arp_entry(PORT, axcall, 0, calllen, port, p_ipad, Interval, bcflag, FALSE, TCPMode, SourcePort, FALSE, noUpdate); + add_arp_entry(PORT, axcall, 0, calllen, port, p_ipad, Interval, bcflag, FALSE, TCPMode, SourcePort, FALSE, noUpdate, useSourcePort); return (TRUE); } } // End of Process MAP @@ -2438,7 +2448,7 @@ BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call,int * calllen } BOOL add_arp_entry(struct AXIPPORTINFO * PORT, UCHAR * call, UCHAR * ip, int len, int port, - UCHAR * name, int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPFlag, int SourcePort, BOOL IPv6, int noUpdate) + UCHAR * name, int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPFlag, int SourcePort, BOOL IPv6, int noUpdate, int useSourcePort) { struct arp_table_entry * arp; @@ -2457,7 +2467,8 @@ BOOL add_arp_entry(struct AXIPPORTINFO * PORT, UCHAR * call, UCHAR * ip, int len arp->PORT = PORT; - if (port == 0) PORT->needip = 1; // Enable Raw IP Mode + if (port == 0 && arp->replytoSourcePort == 0) + PORT->needip = 1; // Enable Raw IP Mode arp->ResolveFlag=TRUE; PORT->NeedResolver=TRUE; @@ -2476,6 +2487,7 @@ BOOL add_arp_entry(struct AXIPPORTINFO * PORT, UCHAR * call, UCHAR * ip, int len arp->TCPMode = TCPFlag; arp->noUpdate = noUpdate; PORT->arp_table_len++; + arp->replytoSourcePort = useSourcePort; if (PORT->MaxResWindowlength < (PORT->arp_table_len * 14) + 70) PORT->MaxResWindowlength = (PORT->arp_table_len * 14) + 70; @@ -2576,7 +2588,7 @@ int CheckKeepalives(struct AXIPPORTINFO * PORT) return (0); } -BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int Port, VOID * rxaddr) +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int FromPort, VOID * rxaddr, int ToPort) { // Makes sure we can reply to call before accepting message @@ -2606,10 +2618,17 @@ BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int Port, struct sockaddr_in * SA = rxaddr; memcpy(&arp->destaddr.sin_addr.s_addr, &SA->sin_addr, 4); } - // Dont think I should update port + // Dont think I should update port unless using source port for dest - //arp->port = Port; + if (arp->replytoSourcePort) + { + arp->port = FromPort; + arp->destaddr.sin_port = htons(arp->port); + if (arp->SourcePort == 0) + arp->SourcePort = ToPort; + } } + arp->LastHeard = time(NULL); return 1; // Ok to process } index++; diff --git a/bpqmail.h b/bpqmail.h index 0a8bf06..a2d85b3 100644 --- a/bpqmail.h +++ b/bpqmail.h @@ -290,6 +290,9 @@ typedef struct ConnectionInfo_S struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to + int bytesSent; + int bytesRxed; + } ConnectionInfo, CIRCUIT; // Flags Equates @@ -336,11 +339,13 @@ typedef struct ConnectionInfo_S struct FBBRestartData { - struct MsgInfo * TempMsg; // Header while message is being received - struct UserInfo * UserPointer; + char Call[10]; + char bid[13]; + int length; UCHAR * MailBuffer; // Mail Message being received int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct int Count; // Give up if too many restarts + time_t TimeCreated; }; // We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so @@ -1387,6 +1392,7 @@ BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSFo BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); +void SaveRestartData(); VOID ReRouteMessages(); diff --git a/cMain.c b/cMain.c index f370490..c87fac2 100644 --- a/cMain.c +++ b/cMain.c @@ -144,6 +144,12 @@ int L4Compress = 0; int L4CompMaxframe = 3; int L4CompPaclen = 236; +int L2Compress = 0; +int L2CompMaxframe = 3; +int L2CompPaclen = 236; + +int PREFERINP3ROUTES = 0; + BOOL LogL4Connects = FALSE; BOOL LogAllConnects = FALSE; BOOL AUTOSAVEMH = TRUE; @@ -161,6 +167,8 @@ char MQTT_PASS[80] = ""; int MQTT_Connecting = 0; int MQTT_Connected = 0; +int PoolBuilt = 0; + //TNCTABLE DD 0 //NUMBEROFSTREAMS DD 0 @@ -836,6 +844,23 @@ BOOL Start() if (L4CompPaclen < 64 || L4CompPaclen > 236) L4CompPaclen = 236; + + L2Compress = cfg->C_L2Compress; + L2CompMaxframe = cfg->C_L2CompMaxframe; + L2CompPaclen = cfg->C_L2CompPaclen; + + if (L2CompMaxframe < 1 || L2CompMaxframe > 16) + L2CompMaxframe = 3; + + if (L2CompPaclen < 64 || L2CompPaclen > 236) + L2CompPaclen = 236; + + + PREFERINP3ROUTES = cfg->C_PREFERINP3ROUTES; + + if (cfg->C_OnlyVer2point0) + SUPPORT2point2 = 0; + // Get pointers to PASSWORD and APPL1 commands @@ -962,6 +987,7 @@ BOOL Start() PORT->NormalizeQuality = !PortRec->NoNormalize; PORT->IgnoreUnlocked = PortRec->IGNOREUNLOCKED; PORT->INP3ONLY = PortRec->INP3ONLY; + PORT->ALLOWINP3 = PortRec->AllowINP3; PORT->PORTWINDOW = (UCHAR)PortRec->MAXFRAME; @@ -986,6 +1012,9 @@ BOOL Start() PORT->PORTPACLEN = (UCHAR)PortRec->PACLEN; PORT->QUAL_ADJUST = (UCHAR)PortRec->QUALADJUST; + if (PORT->QUAL_ADJUST < 0 || PORT->QUAL_ADJUST > 100) + PORT->QUAL_ADJUST = 100; + PORT->DIGIFLAG = PortRec->DIGIFLAG; if (PortRec->DIGIPORT && CanPortDigi(PortRec->DIGIPORT)) PORT->DIGIPORT = PortRec->DIGIPORT; @@ -1443,6 +1472,8 @@ BOOL Start() ENDBUFFERPOOL = NEXTFREEDATA; + PoolBuilt = 1; + // Copy Bridge Map @@ -1560,6 +1591,7 @@ BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE) struct ROUTE * ROUTE = NEIGHBOURS; struct ROUTE * FIRSTSPARE = NULL; int n = MAXNEIGHBOURS; + char Normcall[10]; while (n--) { @@ -1572,6 +1604,10 @@ BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE) ROUTE++; continue; } + + + Normcall[ConvFromAX25(ROUTE->NEIGHBOUR_CALL, Normcall)] = 0; + if (CompareCalls(ROUTE->NEIGHBOUR_CALL, Call)) { *REQROUTE = ROUTE; diff --git a/cheaders.h b/cheaders.h index 4b765a2..4951cac 100644 --- a/cheaders.h +++ b/cheaders.h @@ -105,7 +105,7 @@ VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session); VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len); -struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum); +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum); int cCOUNT_AT_L2(struct _LINKTABLE * LINK); VOID SENDL4CONNECT(TRANSPORTENTRY * Session); @@ -202,8 +202,6 @@ int TrytoGuessCode(unsigned char * Char, int Len); #define XID 0xAF #define TEST 0xE3 -#define SUPPORT2point2 1 - // XID Optional Functions #define OPMustHave 0x02A080 // Sync TEST 16 bit FCS Extended Address @@ -438,6 +436,9 @@ extern int MQTT_PORT; extern char MQTT_USER[80]; extern char MQTT_PASS[80]; +extern int SUPPORT2point2; + + DllExport uint64_t APIENTRY GetPortFrequency(int PortNo, char * FreqStringMhz); diff --git a/config.c b/config.c index 8ca9b68..a1b39a2 100644 --- a/config.c +++ b/config.c @@ -308,7 +308,8 @@ static char *keywords[] = "BTEXT:", "NETROMCALL", "C_IS_CHAT", "MAXRTT", "MAXHOPS", // IPGATEWAY= no longer allowed "LogL4Connects", "LogAllConnects", "SAVEMH", "ENABLEADIFLOG", "ENABLEEVENTS", "SAVEAPRSMSGS", "EnableM0LTEMap", "MQTT", "MQTT_HOST", "MQTT_PORT", "MQTT_USER", "MQTT_PASS", -"L4Compress", "L4CompMaxframe", "L4CompPaclen" +"L4Compress", "L4CompMaxframe", "L4CompPaclen", "L2Compress", "L2CompMaxframe", +"L2CompPaclen", "PREFERINP3ROUTES", "OnlyVer2point0" }; /* parameter keywords */ static void * offset[] = @@ -330,7 +331,8 @@ static void * offset[] = &xxcfg.C_BTEXT, &xxcfg.C_NETROMCALL, &xxcfg.C_C, &xxcfg.C_MAXRTT, &xxcfg.C_MAXHOPS, // IPGATEWAY= no longer allowed &xxcfg.C_LogL4Connects, &xxcfg.C_LogAllConnects, &xxcfg.C_SaveMH, &xxcfg.C_ADIF, &xxcfg.C_EVENTS, &xxcfg.C_SaveAPRSMsgs, &xxcfg.C_M0LTEMap, &xxcfg.C_MQTT, &xxcfg.C_MQTT_HOST, &xxcfg.C_MQTT_PORT, &xxcfg.C_MQTT_USER, &xxcfg.C_MQTT_PASS, -&xxcfg.C_L4Compress, &xxcfg.C_L4CompMaxframe, &xxcfg.C_L4CompPaclen}; /* offset for corresponding data in config file */ +&xxcfg.C_L4Compress, &xxcfg.C_L4CompMaxframe, &xxcfg.C_L4CompPaclen, &xxcfg.C_L2Compress, &xxcfg.C_L2CompMaxframe, +&xxcfg.C_L2CompPaclen, &xxcfg.C_PREFERINP3ROUTES, &xxcfg.C_OnlyVer2point0}; /* offset for corresponding data in config file */ static int routine[] = { @@ -351,6 +353,7 @@ static int routine[] = 15, 0, 2, 9, 9, 2, 2, 1, 2, 2, 2, 2, 2, 0, 1, 20, 20, +1, 1, 1, 1, 1, 1, 1, 1} ; // Routine to process param int PARAMLIM = sizeof(routine)/sizeof(int); @@ -373,7 +376,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", "M0LTEMapInfo", "QTSMPort"}; /* parameter keywords */ +"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq", "M0LTEMapInfo", "QTSMPort", "ALLOWINP3"}; /* parameter keywords */ static void * poffset[] = { @@ -387,7 +390,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, &xxp.M0LTEMapInfo, &xxp.QtSMPort}; /* offset for corresponding data in config file */ +&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq, &xxp.M0LTEMapInfo, &xxp.QtSMPort, &xxp.AllowINP3}; /* offset for corresponding data in config file */ static int proutine[] = { @@ -401,7 +404,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, 22, 1}; /* routine to process parameter */ +1, 20, 1, 21, 22, 1, 1}; /* routine to process parameter */ int PPARAMLIM = sizeof(proutine)/sizeof(int); @@ -535,6 +538,8 @@ BOOL ProcessConfig() // xxcfg.SaveMH = TRUE; // Default to save + xxcfg.C_PREFERINP3ROUTES = 0; // Default to false + GetNextLine(rec); while (rec[0]) @@ -621,7 +626,11 @@ BOOL ProcessConfig() paramok[86]=1; // L4Compress paramok[87]=1; // L4Compress Maxframe paramok[88]=1; // L4Compress Paclen - + paramok[89]=1; // L2Compress + paramok[90]=1; // L2Compress Maxframe + paramok[91]=1; // L2Compress Paclen + paramok[92]=1; // PREFERINP3ROUTES + paramok[93]=1; // C_ONLYVer2point0 for (i=0; i < PARAMLIM; i++) { diff --git a/configstructs.h b/configstructs.h index a7e51a8..9fb75cf 100644 --- a/configstructs.h +++ b/configstructs.h @@ -81,6 +81,7 @@ struct PORTCONFIG uint64_t PortFreq; char * M0LTEMapInfo; int QtSMPort; + int AllowINP3; }; struct ROUTECONFIG @@ -173,6 +174,11 @@ struct CONFIGTABLE int C_L4Compress; int C_L4CompMaxframe; int C_L4CompPaclen; + int C_L2Compress; + int C_L2CompMaxframe; + int C_L2CompPaclen; + int C_PREFERINP3ROUTES; + int C_OnlyVer2point0; //#define ApplOffset 80000 // Applications offset in config buffer diff --git a/kiss.c b/kiss.c index b511296..2f2f071 100644 --- a/kiss.c +++ b/kiss.c @@ -2294,7 +2294,7 @@ VOID QtSMThread(struct PORTCONTROL * PORT) char Msg[64]; int Len; - // We need tp send a QtSMPort message for each Channel sharing thia connection + // We need to send a QtSMPort message for each Channel sharing this connection // Note struct KISSINFO and struct PORTCONTROL are different mappings of the same data struct KISSINFO * KISS = (struct KISSINFO *)PORT; diff --git a/linether.c b/linether.c index fb3410c..b354465 100644 --- a/linether.c +++ b/linether.c @@ -22,17 +22,29 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Normally uses a Raw socket, but that can't send to other apps on same machine. // so can use a TAP device instead (or maybe as well??) + +// Not suported on BSD based systems + +#ifndef FREEBSD #ifndef MACBPQ + +#if !(defined(MACBPQ) || defined(FREEBSD)) + #include - #include "cheaders.h" - #include + +#if !(defined(FREEBSD)) + #include #include #include +#endif +#endif + + extern int tap_fd; typedef struct PCAPStruct @@ -489,5 +501,11 @@ static int ProcessLine(char * buf, int Port, BOOL CheckPort) return (FALSE); } + + +// + +#endif // MACBPQ +#endif // FREEBSD -#endif + diff --git a/lzhuf32.c b/lzhuf32.c index f1ef0a4..c1aea82 100644 --- a/lzhuf32.c +++ b/lzhuf32.c @@ -1406,7 +1406,7 @@ File: 5566 NEWBOAT.HOMEPORT.JPG if (SendBBStoSYSOPCall) if (_stricmp(FullTo, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); + strcpy(FullTo, SYSOPCall); if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0) && (conn->Paclink || conn->RMSExpress)) diff --git a/makefile b/makefile index 7f870ff..cdb6298 100644 --- a/makefile +++ b/makefile @@ -17,22 +17,44 @@ OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pn # Configuration: -CC = gcc - -all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon -all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a +#Default to Linux + CC = gcc + EXTRA_LDFLAGS = -Xlinker -Map=output.map -lrt -l:libpaho-mqtt3a.a -l:libjansson.a + +#other OS + +OS_NAME = $(shell uname -s) +ifeq ($(OS_NAME),NetBSD) + CC = cc + EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/pkg/include + EXTRA_LDFLAGS = -Xlinker -Map=output.map -Wl,-R/usr/pkg/lib -L/usr/pkg/lib -lrt -lutil -lexecinfo +endif +ifeq ($(OS_NAME),FreeBSD) + CC = cc + EXTRA_CFLAGS = -DFREEBSD -DNOMQTT -I/usr/local/include + EXTRA_LDFLAGS = -Xlinker -Map=output.map -L/usr/local/lib -lrt -liconv -lutil -lexecinfo +endif +ifeq ($(OS_NAME),Darwin) + CC = gcc + EXTRA_CFLAGS = -DMACBPQ -DNOMQTT + EXTRA_LDFLAGS = -liconv +endif + +$(info OS_NAME is $(OS_NAME)) + +all: CFLAGS = -DLINBPQ -MMD -g -fcommon -fasynchronous-unwind-tables $(EXTRA_CFLAGS) all: linbpq -nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT +nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables nomqtt: linbpq -noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables noi2c: linbpq linbpq: $(OBJS) - gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lrt -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq + cc $(OBJS) $(EXTRA_LDFLAGS) -lminiupnpc -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq -include *.d diff --git a/mqtt.c b/mqtt.c index ff1c716..a9e91b4 100644 --- a/mqtt.c +++ b/mqtt.c @@ -429,6 +429,6 @@ void MQTTKISSRX(void *message) {}; void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {}; void MQTTTimer() {}; void MQTTReportSession(char * Msg) {}; +void MQTTMessageEvent(void* message) {}; #endif - diff --git a/templatedefs.c b/templatedefs.c index 47bdfc3..2fa2e13 100644 --- a/templatedefs.c +++ b/templatedefs.c @@ -520,7 +520,8 @@ char * MainConfigtxt() "\r\n" "\r\n" "

" - " FBB reject.sys type filters (all fields must match, wildcards allowed)\r\n" + " FBB reject.sys type filters (all fields must match, wildcards allowed)
" + " 'A' action accepts message if all fields match without checking following lines\r\n" "

" "
%s
" "
" diff --git a/upnp.c b/upnp.c index 96cb9d3..714e04d 100644 --- a/upnp.c +++ b/upnp.c @@ -93,7 +93,11 @@ int i; const char * rootdescurl = 0; const char * multicastif = 0; const char * minissdpdpath = 0; +#ifdef UPNP_LOCAL_PORT_ANY int localport = UPNP_LOCAL_PORT_ANY; +#else +int localport = 0; +#endif int retcode = 0; int error = 0; int ipv6 = 0; @@ -119,15 +123,18 @@ int upnpInit() { if (devlist == NULL) { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - +#endif if (devlist == NULL) { Consoleprintf("Failed to find a UPNP device"); return 0; } -#if MINIUPNPC_API_VERSION == 18 +#if MINIUPNPC_API_VERSION >= 18 i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr)); #else i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); @@ -150,15 +157,18 @@ int upnpClose() { if (devlist == NULL) { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - +#endif if (devlist == NULL) { Consoleprintf("Failed to find a UPNP device"); return 0; } -#if MINIUPNPC_API_VERSION == 18 +#if MINIUPNPC_API_VERSION >= 18 i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr)); #else i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));