/* Copyright 2001-2022 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 the V4 TNC as a Port Driver // // Uses BPQ EXTERNAL interface // // Uses a number of routines in WINMOR.c #define _CRT_SECURE_NO_DEPRECATE #include #include #define SD_RECEIVE 0x00 #define SD_SEND 0x01 #define SD_BOTH 0x02 #include "CHeaders.h" #include "tncinfo.h" #include "bpq32.h" int (WINAPI FAR *GetModuleFileNameExPtr)(); #define WSA_ACCEPT WM_USER + 1 #define WSA_DATA WM_USER + 2 #define WSA_CONNECT WM_USER + 3 static int Socket_Data(int sock, int error, int eventcode); INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); KillTNC(struct TNCINFO * TNC); RestartTNC(struct TNCINFO * TNC); KillPopups(struct TNCINFO * TNC); VOID MoveWindows(struct TNCINFO * TNC); char * CheckAppl(struct TNCINFO * TNC, char * Appl); static VOID ChangeMYC(struct TNCINFO * TNC, char * Call); int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); static char ClassName[]="V4STATUS"; static char WindowTitle[] = "V4TNC"; static int RigControlRow = 147; #define V4 #define NARROWMODE 0 #define WIDEMODE 0 #include extern int SemHeldByAPI; static RECT Rect; extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd static int ProcessLine(char * buf, int Port); // RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 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; 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] = malloc(sizeof(struct TNCINFO)); memset(TNC, 0, sizeof(struct TNCINFO)); 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); WINMORport = atoi(p_port); TNC->destaddr.sin_family = AF_INET; TNC->destaddr.sin_port = htons(WINMORport); TNC->Datadestaddr.sin_family = AF_INET; TNC->Datadestaddr.sin_port = htons(WINMORport+1); 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 (_stricmp(ptr, "PTT") == 0) { ptr = strtok(NULL, " \t\n\r"); if (ptr) { DecodePTTString(TNC, ptr); 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, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) {} // Ignore else if (_memicmp(buf, "WL2KREPORT", 10) == 0) {} // Ignore else strcat (TNC->InitScript, buf); } return (TRUE); } void ConnecttoWINMORThread(int port); VOID V4ProcessDataSocketData(int port); int ConnecttoWINMOR(); int ProcessReceivedData(struct TNCINFO * TNC); VOID ReleaseTNC(struct TNCINFO * TNC); VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); #define MAXBPQPORTS 32 static time_t ltime; #pragma pack() static SOCKADDR_IN sinx; static SOCKADDR_IN rxaddr; static int addrlen=sizeof(sinx); static fd_set readfs; static fd_set writefs; static fd_set errorfs; static struct timeval timeout; static VOID ChangeMYC(struct TNCINFO * TNC, char * Call) { UCHAR TXMsg[100]; int datalen; if (strcmp(Call, TNC->CurrentMYC) == 0) return; // No Change strcpy(TNC->CurrentMYC, Call); // send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); datalen = sprintf(TXMsg, "MYCALL %s\r\n", Call); send(TNC->TCPSock,TXMsg, datalen, 0); // send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); // TNC->StartSent = TRUE; // send(TNC->TCPSock, "MYCALL\r\n", 8, 0); } static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { int datalen; PMSGWITHLEN buffptr; int i,winerr; char txbuff[500]; char Status[80]; unsigned int bytes,txlen=0; char ErrMsg[255]; int Param; HKEY hKey=0; struct TNCINFO * TNC = TNCInfo[port]; struct STREAMINFO * STREAM = &TNC->Streams[0]; if (TNC == NULL) return 0; // Port not defined 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); } if (TNC->BusyDelay) { // Still Busy? if ((TNC->Busy & CDBusy) == 0) { // No, so send send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); TNC->Streams[0].Connecting = TRUE; memset(TNC->Streams[0].RemoteCall, 0, 10); memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[11], strlen(TNC->ConnectCmd)-13); sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); free(TNC->ConnectCmd); TNC->BusyDelay = 0; } else { // Wait Longer TNC->BusyDelay--; if (TNC->BusyDelay == 0) { // Timed out - Send Error Response PMSGWITHLEN buffptr = GetBuff(); if (buffptr == 0) return (0); // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); free(TNC->ConnectCmd); } } } if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected { TNC->HeartBeat = 0; if (TNC->CONNECTED) // Probe link send(TNC->TCPSock, "BUFFER\r\n", 8, 0); } if (TNC->FECMode) { if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins { if (!TNC->Busy) { TNC->FECIDTimer = 0; send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); } } if (TNC->FECPending) // Check if FEC Send needed { if (!TNC->Busy) { TNC->FECPending = 0; if (TNC->FEC1600) send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); else send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); } } } if (STREAM->NeedDisc) { STREAM->NeedDisc--; if (STREAM->NeedDisc == 0) { // Send the DISCONNECT send(TNC->TCPSock,"ARQEND\r\n", 8, 0); TNC->Streams[0].ARQENDSent = TRUE; } } if (TNC->DiscPending) { TNC->DiscPending--; if (TNC->DiscPending == 0) { // Too long in Disc Pending - Kill and Restart TNC if (TNC->PID) { KillTNC(TNC); RestartTNC(TNC); } } } /* if (TNC->UpdateWL2K) { TNC->UpdateWL2KTimer--; if (TNC->UpdateWL2KTimer == 0) { TNC->UpdateWL2KTimer = 32910/2; // Every Hour if (CheckAppl(TNC, "RMS ")) // Is RMS Available? SendReporttoWL2K(TNC); } } */ if (TNC->RIG) { if (TNC->RIG->RigFreq != TNC->LastFreq) { char FREQMsg[80]; int Len; TNC->LastFreq = TNC->RIG->RigFreq; Len = sprintf(FREQMsg, "DISPLAY CF:%1.4f\r\n", TNC->LastFreq + .0015); send(TNC->TCPSock,FREQMsg, Len, 0); } } if (TNC->TimeSinceLast++ > 700) // Allow 10 secs for Keepalive { // Restart TNC if (TNC->ProgramPath) { if (strstr(TNC->ProgramPath, "V4 TNC")) { struct tm * tm; char Time[80]; TNC->Restarts++; TNC->LastRestart = time(NULL); tm = gmtime(&TNC->LastRestart); sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); MySetWindowText(TNC->xIDC_RESTARTTIME, Time); strcpy(TNC->WEB_RESTARTTIME, Time); sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); MySetWindowText(TNC->xIDC_RESTARTS, Time); strcpy(TNC->WEB_RESTARTS, Time); KillTNC(TNC); RestartTNC(TNC); TNC->TimeSinceLast = 0; } } } if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) { // New Attach int calllen; char Msg[80]; TNC->Streams[0].Attached = TRUE; TNC->Streams[0].ARQENDSent = FALSE; calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); TNC->Streams[0].MyCall[calllen] = 0; // Stop Listening, and set MYCALL to user's call // send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); ChangeMYC(TNC, TNC->Streams[0].MyCall); // Stop other ports in same group SuspendOtherPorts(TNC); sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); // Stop Scanning sprintf(Msg, "%d SCANSTOP", TNC->Port); Rig_Command( (TRANSPORTENTRY *) -1, Msg); } if (TNC->Streams[0].Attached) CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); if (TNC->Streams[0].ReportDISC) { TNC->Streams[0].ReportDISC = FALSE; buff->PORT = 0; return -1; } if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) { // See if time to reconnect time(<ime); if (ltime - TNC->lasttime >9 ) { TNC->LastFreq = 0; // so display will be updated ConnecttoWINMOR(port); TNC->lasttime = ltime; } } FD_ZERO(&readfs); if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); FD_ZERO(&writefs); if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing FD_ZERO(&errorfs); if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); if (select((int)TNC->TCPSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) { // See what happened if (readfs.fd_count == 1) V4ProcessDataSocketData(port); if (writefs.fd_count == 1) { // Write block has cleared. Send rest of packet buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); txlen = (unsigned int)buffptr->Len; memcpy(txbuff,buffptr->Data, txlen); bytes=send(TNC->TCPSock,(const char FAR *)&txbuff,txlen,0); ReleaseBuffer(buffptr); } if (errorfs.fd_count == 1) { i=sprintf(ErrMsg, "V4 Data Connection lost for BPQ Port %d\n", port); WritetoConsole(ErrMsg); TNC->CONNECTING = FALSE; TNC->CONNECTED = FALSE; TNC->Streams[0].ReportDISC = TRUE; } } // See if any frames for this port if (TNC->WINMORtoBPQ_Q != 0) { buffptr = Q_REM(&TNC->WINMORtoBPQ_Q); datalen = (int)buffptr->Len; buff->PORT = 0; // Compatibility with Kam Driver buff->PID = 0xf0; memcpy(&buff->L2DATA[0], buffptr->Data, datalen); datalen += sizeof(void *) + 4; PutLengthinBuffer(buff, (int)datalen); ReleaseBuffer(buffptr); return (1); } return (0); case 2: // send if (!TNC->CONNECTED) { // Send Error Response PMSGWITHLEN buffptr = GetBuff(); if (buffptr == 0) return (0); // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "No Connection to V4 TNC\r"); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); return 0; // Don't try if not connected } if (TNC->BPQtoWINMOR_Q) return 0; // Socket is blocked - just drop packets till it clears if (TNC->SwallowSignon) { TNC->SwallowSignon = FALSE; // Discard *** connected return 0; } txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID if (TNC->Streams[0].Connected) bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen,0); else { if (_memicmp(buff->L2DATA, "D\r", 2) == 0) { TNC->Streams[0].ReportDISC = TRUE; // Tell Node return 0; } if (TNC->FECMode) { char Buffer[300]; int len; // Send FEC Data buff->L2DATA[txlen] = 0; len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, buff->L2DATA); send(TNC->TCPDataSock, Buffer, len, 0); /* if (TNC->Busy) { TNC->FECPending = 1; } else { if (TNC->FEC1600) send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); else send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); } */ 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, buff->L2DATA)) { } else { PMSGWITHLEN buffptr = GetBuff(); if (buffptr == 0) return 1; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, buff->L2DATA); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); } return 1; } if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) TNC->StartSent = TRUE; if (_memicmp(buff->L2DATA, "D\r", 2) == 0) { TNC->Streams[0].ReportDISC = TRUE; // Tell Node return 0; } if (_memicmp(&buff[8], "FEC\r", 4) == 0 || _memicmp(&buff[8], "FEC ", 4) == 0) { TNC->FECMode = TRUE; TNC->FECIDTimer = 0; send(TNC->TCPSock,"MODE FEC\r\n", 10, 0); strcpy(TNC->WEB_MODE, "FEC"); MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); return 0; } // See if a Connect Command. If so, start codec and set Connecting if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect { char Connect[80] = "ARQCONNECT "; memcpy(&Connect[11], &buff->L2DATA[2], txlen); txlen += 9; Connect[txlen++] = 0x0a; Connect[txlen] = 0; _strupr(Connect); // See if Busy if (TNC->Busy & CDBusy) { // Channel Busy. Unless override set, wait if (TNC->OverrideBusy == 0) { // Save Command, and wait up to 10 secs TNC->ConnectCmd = _strdup(Connect); TNC->BusyDelay = 100; // 10 secs return 0; } } TNC->OverrideBusy = FALSE; bytes=send(TNC->TCPSock, Connect, txlen, 0); TNC->Streams[0].Connecting = TRUE; memset(TNC->Streams[0].RemoteCall, 0, 10); memcpy(TNC->Streams[0].RemoteCall, &Connect[11], txlen-13); sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); } else { buff->L2DATA[txlen++] = 0x0a; bytes=send(TNC->TCPSock, buff->L2DATA, txlen, 0); } } if (bytes != txlen) { // WINMOR doesn't seem to recover from a blocked write. For now just reset winerr = WSAGetLastError(); sprintf(ErrMsg, "V4 Write Failed for port %d - error code = %d\n", port, winerr); WritetoConsole(ErrMsg); closesocket(TNC->TCPSock); TNC->CONNECTED = FALSE; return (0); } return (0); case 3: // CHECK IF OK TO SEND (And check TNC Status) if (TNC->Streams[0].Attached == 0) return TNC->CONNECTED << 8 | 1; return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK break; case 4: // reinit return (0); case 5: // Close send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); Sleep(100); shutdown(TNC->TCPDataSock, SD_BOTH); shutdown(TNC->TCPSock, SD_BOTH); Sleep(100); closesocket(TNC->TCPDataSock); closesocket(TNC->TCPSock); if (TNC->PID && TNC->WeStartedTNC) { KillTNC(TNC); } return (0); case 6: // Scan Stop Interface Param = (int)(size_t)buff; if (Param == 1) // Request Permission { if (!TNC->ConnectPending) return 0; // OK to Change // send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); return TRUE; } if (Param == 2) // Check Permission { if (TNC->ConnectPending) return -1; // Skip Interval return 1; // OK to change } if (Param == 3) // Release Permission { // send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); return 0; } if (Param == 4) // Set Wide Mode { send(TNC->TCPSock, "BW 1600\r\n", 9, 0); return 0; } if (Param == 5) // Set Narrow Mode { send(TNC->TCPSock, "BW 500\r\n", 8, 0); return 0; } return 0; } return 0; } VOID V4ProcessDataSocketData(int port) { // Info on Data Socket - just packetize and send on struct TNCINFO * TNC = TNCInfo[port]; int InputLen, PacLen = 236, i; PMSGWITHLEN buffptr; char * msg; TNC->TimeSinceLast = 0; loop: buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore InputLen=recv(TNC->TCPDataSock, (char *)&buffptr[2], PacLen, 0); if (InputLen == -1) { ReleaseBuffer(buffptr); return; } //Debugprintf("Winmor: RXD %d bytes", InputLen); if (InputLen == 0) { // Does this mean closed? strcpy(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); TNC->CONNECTING = FALSE; TNC->CONNECTED = FALSE; TNC->Streams[0].ReportDISC = TRUE; ReleaseBuffer(buffptr); return; } msg = buffptr->Data; // Message should always be received in 17 char chunks. 17th is a status byte // In ARQ, 6 = "Echo as sent" ack if (InputLen != 17) { Debugprintf("V4 TNC incorrect RX Len = %d", InputLen); goto loop; } if (msg[16] == 0x06) goto loop; InputLen = 16; for (i = 0; i < 16; i++) { if (msg[i] == 0) break; if (msg[i] == 10) continue; if (msg[i] < 0x20 || msg[i] > 0x7e) msg[i] = '?'; } msg[InputLen] = 0; WritetoTrace(TNC, msg, InputLen); // V4 Sends null padded blocks InputLen = (int)strlen(buffptr->Data); if (msg[InputLen - 1] == 10) // LF { // Replace with CRLF msg[InputLen-1] = 13; // Add CR msg[InputLen++] = 10; } buffptr->Len = InputLen; C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); goto loop; } static VOID ReleaseTNC(struct TNCINFO * TNC) { // Set mycall back to Node or Port Call ChangeMYC(TNC, TNC->NodeCall); // send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); MySetWindowText(TNC->xIDC_TNCSTATE, "Free"); strcpy(TNC->WEB_TNCSTATE, "Free"); // Start Scanner ReleaseOtherPorts(TNC); } static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) { int Len = sprintf(Buff, "" "" "V4 Status" "

V4 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_CHANSTATE); Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); // Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); Len += sprintf(&Buff[Len], "", TNC->WebBuffer); Len = DoScanLine(TNC, Buff, Len); return Len; } void * V4ExtInit(EXTPORTDATA * PortEntry) { int i, port; char Msg[255]; char * ptr; struct TNCINFO * TNC; char * TempScript; 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; if (TNC->ProgramPath) TNC->WeStartedTNC = RestartTNC(TNC); TNC->Hardware = H_V4; TNC->PortRecord = PortEntry; if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) memcpy(TNC->NodeCall, MYNODECALL, 10); else ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; PortEntry->PORTCONTROL.PROTOCOL = 10; PortEntry->PORTCONTROL.PORTQUALITY = 0; PortEntry->MAXHOSTMODESESSIONS = 1; // PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only if (PortEntry->PORTCONTROL.PORTPACLEN == 0) PortEntry->PORTCONTROL.PORTPACLEN = 236; ptr=strchr(TNC->NodeCall, ' '); if (ptr) *(ptr) = 0; // Null Terminate // Set Essential Params and MYCALL // Put overridable ones on front, essential ones on end TempScript = malloc(1000); strcpy(TempScript, "DebugLog True\r\n"); strcat(TempScript, "AUTOID FALSE\r\n"); strcat(TempScript, "CODEC FALSE\r\n"); strcat(TempScript, "TIMEOUT 90\r\n"); strcat(TempScript, "MODE ARQ\r\n"); strcat(TempScript, "TUNING 100\r\n"); strcat(TempScript, TNC->InitScript); free(TNC->InitScript); TNC->InitScript = TempScript; // Set MYCALL // strcat(TNC->InitScript,"FECRCV True\r\n"); sprintf(Msg, "MYCALL %s\r\nCODEC TRUE\r\nMYCALL\r\n", TNC->NodeCall); strcat(TNC->InitScript, Msg); strcat(TNC->InitScript,"PROCESSID\r\n"); strcpy(TNC->CurrentMYC, TNC->NodeCall); if (TNC->destaddr.sin_family == 0) { // not defined in config file, so use localhost and port from IOBASE TNC->destaddr.sin_family = AF_INET; TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); TNC->Datadestaddr.sin_family = AF_INET; TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); TNC->HostName=malloc(10); if (TNC->HostName != NULL) strcpy(TNC->HostName,"127.0.0.1"); } 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(20); TNC->WEB_TRAFFIC = zalloc(100); #ifndef LINBPQ CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 450, 500, 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", 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", "Traffic", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,116,100,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,116,40,20 , TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,116,100,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,116,200,20, TNC->hDlg, NULL, hInstance, NULL); TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, 0,138,250,300, TNC->hDlg, NULL, hInstance, NULL); TNC->ClientHeight = 450; TNC->ClientWidth = 500; TNC->hMenu = CreatePopupMenu(); AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill V4 TNC"); AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart V4 TNC"); // AppendMenu(TNC->hPopMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); // CheckMenuItem(TNC->hPopMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); MoveWindows(TNC); #endif i=sprintf(Msg,"V4 Host %s %d\n", TNC->HostName, htons(TNC->destaddr.sin_port)); WritetoConsole(Msg); strcpy(TNC->WEB_MODE, "ARQ"); MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); ConnecttoWINMOR(port); time(&TNC->lasttime); // Get initial time value return ExtProc; } #ifndef LINBPQ static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) { char wtext[100]; struct TNCINFO * TNC = (struct TNCINFO *)lParam; UINT ProcessId; GetWindowText(hwnd,wtext,99); if (memcmp(wtext,"Registration", 12) == 0) { SendMessage(hwnd, WM_CLOSE, 0, 0); return TRUE; } if (memcmp(wtext,"V4 Sound Card TNC", 17) == 0) { GetWindowThreadProcessId(hwnd, &ProcessId); if (TNC->PID == ProcessId) { // Our Process sprintf (wtext, "V4 Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); MySetWindowText(hwnd, wtext); // return FALSE; } } return (TRUE); } #endif static VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) { // Response on WINMOR control channel. Could be a reply to a command, or // an Async Response PMSGWITHLEN buffptr; char Status[80]; struct STREAMINFO * STREAM = &TNC->Streams[0]; if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) { // Force a restart send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); } else { TNC->TimeSinceLast = 0; } Buffer[MsgLen - 2] = 0; // Remove CRLF if (_memicmp(Buffer, "PTT T", 5) == 0) { TNC->Busy |= PTTBusy; if (TNC->PTTMode) Rig_PTT(TNC, TRUE); return; } if (_memicmp(Buffer, "PTT F", 5) == 0) { TNC->Busy &= ~PTTBusy; if (TNC->PTTMode) Rig_PTT(TNC, FALSE); return; } if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) { TNC->Busy |= CDBusy; MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); strcpy(TNC->WEB_CHANSTATE, "Busy"); return; } if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) { TNC->Busy &= ~CDBusy; MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); strcpy(TNC->WEB_CHANSTATE, "Clear"); return; } if (_memicmp(Buffer, "OFFSET", 6) == 0) { // WritetoTrace(TNC, Buffer, MsgLen - 2); // memcpy(TNC->TargetCall, &Buffer[7], 10); return; } if (_memicmp(Buffer, "CONNECTED", 9) == 0) { char Call[11]; char * ptr; char * ApplPtr = APPLS; APPLCALLS * APPL; int App; char Appl[10]; WritetoTrace(TNC, Buffer, MsgLen - 2); STREAM->ConnectTime = time(NULL); memcpy(Call, &Buffer[10], 10); ptr = strchr(Call, ' '); if (ptr) *ptr = 0; TNC->HadConnect = TRUE; if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) { // Incomming Connect // Stop other ports in same group SuspendOtherPorts(TNC); ProcessIncommingConnect(TNC, Call, 0, TRUE); TNC->Streams[0].ARQENDSent = FALSE; if (TNC->RIG) sprintf(Status, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); else sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); // See which application the connect is for for (App = 0; App < 32; App++) { APPL=&APPLCALLTABLE[App]; memcpy(Appl, APPL->APPLCALL_TEXT, 10); ptr=strchr(Appl, ' '); if (ptr) *ptr = 0; if (_stricmp(TNC->CurrentMYC, Appl) == 0) break; } 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)) { MsgLen = sprintf(Buffer, "%s\r", AppName); GetSemaphore(&Semaphore, 50); buffptr = GetBuff(); if (buffptr == 0) { FreeSemaphore(&Semaphore); return; // No buffers, so ignore } buffptr->Len = MsgLen; memcpy(buffptr+2, Buffer, MsgLen); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); FreeSemaphore(&Semaphore); TNC->SwallowSignon = TRUE; } else { char Msg[] = "Application not available\r\n"; // Send a Message, then a disconenct send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); STREAM->NeedDisc = 100; // 10 secs } } return; } else { // Connect Complete char Reply[80]; int ReplyLen; buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); buffptr->Len = ReplyLen; memcpy(buffptr+2, Reply, ReplyLen); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); TNC->Streams[0].Connecting = FALSE; TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel if (TNC->RIG) sprintf(Status, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); else sprintf(Status, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); UpdateMH(TNC, Call, '+', 'O'); return; } } if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) { if (TNC->FECMode) return; if (TNC->StartSent) { TNC->StartSent = FALSE; // Disconnect reported following start codec return; } if (TNC->Streams[0].Connecting) { // Report Connect Failed, and drop back to command mode TNC->Streams[0].Connecting = FALSE; buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "V4} Failure with %s\r", TNC->Streams[0].RemoteCall); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); MySetWindowText(TNC->xIDC_TNCSTATE, Status); strcpy(TNC->WEB_TNCSTATE, Status); return; } WritetoTrace(TNC, Buffer, MsgLen - 2); // Release Session TNC->Streams[0].Connecting = FALSE; TNC->Streams[0].Connected = FALSE; // Back to Command Mode TNC->Streams[0].ReportDISC = TRUE; // Tell Node if (TNC->Streams[0].Disconnecting) // ReleaseTNC(TNC); TNC->Streams[0].Disconnecting = FALSE; return; } if (_memicmp(Buffer, "CMD", 3) == 0) { return; } if (_memicmp(Buffer, "PENDING", 6) == 0) return; /* if (_memicmp(Buffer, "FAULT Not connected!", 20) == 0) { // If in response to ARQEND, assume Disconnected was missed if (TNC->Streams[0].Disconnecting) { TNC->Streams[0].Connecting = FALSE; TNC->Streams[0].Connected = FALSE; // Back to Command Mode TNC->Streams[0].ReportDISC = TRUE; // Tell Node ReleaseTNC(TNC); TNC->Streams[0].Disconnecting = FALSE; } } */ if (_memicmp(Buffer, "FAULT", 5) == 0) { WritetoTrace(TNC, Buffer, MsgLen - 2); return; } if (_memicmp(Buffer, "BUFFER", 6) == 0) { sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); if (TNC->Streams[0].BytesOutstanding == 0) { // all sent if (TNC->Streams[0].Disconnecting) // Disconnect when all sent { if (TNC->Streams[0].ARQENDSent == FALSE) { send(TNC->TCPSock,"ARQEND\r\n", 8, 0); TNC->Streams[0].ARQENDSent = TRUE; } } } else { // Make sure Node Keepalive doesn't kill session. TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; if (SESS) { SESS->L4KILLTIMER = 0; SESS = SESS->L4CROSSLINK; if (SESS) SESS->L4KILLTIMER = 0; } } MySetWindowText(TNC->xIDC_TRAFFIC, &Buffer[7]); strcpy(TNC->WEB_TRAFFIC, &Buffer[7]); return; } if (_memicmp(Buffer, "PROCESSID", 9) == 0) { HANDLE hProc; char ExeName[256] = ""; TNC->PID = atoi(&Buffer[10]); // Get the File Name in case we want to restart it. if (GetModuleFileNameExPtr) { hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); if (hProc) { GetModuleFileNameExPtr(hProc, 0, ExeName, 255); CloseHandle(hProc); if (TNC->ProgramPath) free(TNC->ProgramPath); TNC->ProgramPath = _strdup(ExeName); } } // Set Window Title to reflect BPQ Port Description #ifndef LINBPQ EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); #endif } if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) { TNC->PlaybackDevices = _strdup(&Buffer[16]); } // Others should be responses to commands if (_memicmp(Buffer, "BLOCKED", 6) == 0) { WritetoTrace(TNC, Buffer, MsgLen - 2); return; } if (_memicmp(Buffer, "CONREQ", 6) == 0) { // if to one of our APPLCALLS, change TNC MYCALL APPLCALLS * APPL; char Appl[11]; char Target[20]; char * ptr; int i; memcpy(Target, &Buffer[7], 12); ptr = memchr(Target, ' ', 12); if (ptr) *ptr = 0; if (strcmp(Target, TNC->NodeCall) == 0) ChangeMYC(TNC, Target); else { for (i = 0; i < 32; i++) { APPL=&APPLCALLTABLE[i]; if (APPL->APPLCALL_TEXT[0] > ' ') { memcpy(Appl, APPL->APPLCALL_TEXT, 10); ptr=strchr(Appl, ' '); if (ptr) *ptr = 0; if (strcmp(Appl, Target) == 0) { ChangeMYC(TNC, Target); break; } } } } WritetoTrace(TNC, Buffer, MsgLen - 2); // Update MH ptr = strstr(Buffer, " de "); if (ptr) UpdateMH(TNC, ptr + 4, '!', 'O'); } buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "V4} %s\r", Buffer); C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); } int V4ProcessReceivedData(struct TNCINFO * TNC) { char ErrMsg[255]; int InputLen, MsgLen; char * ptr, * ptr2; char Buffer[2000]; // May have several messages per packet, or message split over packets if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection TNC->InputLen=0; InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); if (InputLen == 0 || InputLen == SOCKET_ERROR) { // Does this mean closed? if (!TNC->CONNECTING) { sprintf(ErrMsg, "V4TNC Connection lost for BPQ Port %d\r\n", TNC->Port); WritetoConsole(ErrMsg); } TNC->CONNECTING = FALSE; TNC->CONNECTED = FALSE; TNC->Streams[0].ReportDISC = TRUE; return 0; } TNC->InputLen += InputLen; loop: ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); if (ptr) // CR in buffer { ptr2 = &TNC->TCPBuffer[TNC->InputLen]; ptr++; // Assume LF Follows CR if (ptr == ptr2) { // Usual Case - single meg in buffer ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); TNC->InputLen=0; } else { // buffer contains more that 1 message MsgLen = TNC->InputLen - (int)(ptr2 - ptr); memcpy(Buffer, TNC->TCPBuffer, MsgLen); ProcessResponse(TNC, Buffer, MsgLen); memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); TNC->InputLen -= MsgLen; goto loop; } } return 0; } /* INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { int Cmd = LOWORD(wParam); switch (message) { case WM_INITDIALOG: { struct TNCINFO * TNC = (struct TNCINFO * )lParam; char * ptr1, *ptr2; int ptr3 = 0; char Line[1000]; int len; ptr1 = TNC->CaptureDevices; if (!ptr1) return 0; // No Devices while (ptr2 = strchr(ptr1, ',')) { len = ptr2 - ptr1; memcpy(&Line[ptr3], ptr1, len); ptr3 += len; Line[ptr3++] = '\r'; Line[ptr3++] = '\n'; ptr1 = ++ptr2; } Line[ptr3] = 0; strcat(Line, ptr1); SetDlgItemText(hDlg, IDC_CAPTURE, Line); ptr3 = 0; ptr1 = TNC->PlaybackDevices; if (!ptr1) return 0; // No Devices while (ptr2 = strchr(ptr1, ',')) { len = ptr2 - ptr1; memcpy(&Line[ptr3], ptr1, len); ptr3 += len; Line[ptr3++] = '\r'; Line[ptr3++] = '\n'; ptr1 = ++ptr2; } Line[ptr3] = 0; strcat(Line, ptr1); SetDlgItemText(hDlg, IDC_PLAYBACK, Line); SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); // KillTNC(TNC); return TRUE; } case WM_SIZING: { return TRUE; } case WM_ACTIVATE: // SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); break; case WM_COMMAND: if (Cmd == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } return (INT_PTR)TRUE; break; } return (INT_PTR)FALSE; } */ static VOID TidyClose(struct TNCINFO * TNC, int Stream) { // If all acked, send disc if (TNC->Streams[0].BytesOutstanding == 0) { send(TNC->TCPSock,"ARQEND\r\n", 8, 0); TNC->Streams[0].ARQENDSent = TRUE; } } static VOID ForcedClose(struct TNCINFO * TNC, int Stream) { send(TNC->TCPSock,"ABORT\r\n", 7, 0); } VOID CloseComplete(struct TNCINFO * TNC, int Stream) { ReleaseTNC(TNC); ChangeMYC(TNC, TNC->NodeCall); // In case changed to an applcall if (TNC->FECMode) { TNC->FECMode = FALSE; send(TNC->TCPSock,"MODE ARQ\r\n", 10, 0); strcpy(TNC->WEB_MODE, "ARQ"); MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); } }