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));