/* 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); } }