// // cysfprotocol.cpp // xlxd // // Created by Jean-Luc Deltombe (LX3JL) on 20/05/2018. // Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. // // ---------------------------------------------------------------------------- // This file is part of xlxd. // // xlxd 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. // // xlxd 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 Foobar. If not, see . // ---------------------------------------------------------------------------- #include "main.h" #include #include "ccrc.h" #include "cysfpayload.h" #include "cysfclient.h" #include "cysfnodedirfile.h" #include "cysfnodedirhttp.h" #include "cysfutils.h" #include "cysfprotocol.h" #include "creflector.h" #include "cgatekeeper.h" //////////////////////////////////////////////////////////////////////////////////////// // constructor CYsfProtocol::CYsfProtocol() { m_seqNo = 0; } //////////////////////////////////////////////////////////////////////////////////////// // operation bool CYsfProtocol::Init(void) { bool ok; // base class ok = CProtocol::Init(); // update the reflector callsign m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"YSF", 3); // create our socket ok &= m_Socket.Open(YSF_PORT); if ( !ok ) { std::cout << "Error opening socket on port UDP" << YSF_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl; } // init the wiresx cmd handler ok &= m_WiresxCmdHandler.Init(); // update time m_LastKeepaliveTime.Now(); // done return ok; } void CYsfProtocol::Close(void) { // base class CProtocol::Close(); // and close wiresx handler m_WiresxCmdHandler.Close(); } //////////////////////////////////////////////////////////////////////////////////////// // task void CYsfProtocol::Task(void) { CBuffer Buffer; CIp Ip; CCallsign Callsign; CYSFFICH Fich; CDvHeaderPacket *Header; CDvFramePacket *Frames[5]; CWiresxCmd WiresxCmd; int iWiresxCmd; int iWiresxArg; // handle outgoing packets { // any packet to go ? CWiresxPacketQueue *queue = m_WiresxCmdHandler.GetPacketQueue(); while ( !queue->empty() ) { CWiresxPacket packet = queue->front(); queue->pop(); m_Socket.Send(packet.GetBuffer(), packet.GetIp()); } m_WiresxCmdHandler.ReleasePacketQueue(); } // handle incoming packets if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 ) { // crack the packet if ( IsValidDvPacket(Buffer, &Fich) ) { //std::cout << "FN = " << (int)Fich.getFN() << " FT = " << (int)Fich.getFT() << std::endl; if ( IsValidDvFramePacket(Ip, Fich, Buffer, Frames) ) { //std::cout << "YSF DV frame" << std::endl; // handle it OnDvFramePacketIn(Frames[0], &Ip); OnDvFramePacketIn(Frames[1], &Ip); OnDvFramePacketIn(Frames[2], &Ip); OnDvFramePacketIn(Frames[3], &Ip); OnDvFramePacketIn(Frames[4], &Ip); } else if ( IsValidDvHeaderPacket(Ip, Fich, Buffer, &Header, Frames) ) { //std::cout << "YSF DV header:" << std::endl << *Header << std::endl; //std::cout << "YSF DV header:" << std::endl; // node linked and callsign muted? if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_YSF, Header->GetRpt2Module()) ) { // handle it OnDvHeaderPacketIn(Header, Ip); //OnDvFramePacketIn(Frames[0], &Ip); //OnDvFramePacketIn(Frames[1], &Ip); } else { delete Header; } } else if ( IsValidDvLastFramePacket(Ip, Fich, Buffer, Frames) ) { //std::cout << "YSF last DV frame" << std::endl; // handle it OnDvFramePacketIn(Frames[0], &Ip); OnDvLastFramePacketIn((CDvLastFramePacket *)Frames[1], &Ip); } } else if ( IsValidConnectPacket(Buffer, &Callsign) ) { //std::cout << "YSF keepalive/connect packet from " << Callsign << " at " << Ip << std::endl; // callsign authorized? if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_YSF) ) { // acknowledge the request EncodeConnectAckPacket(&Buffer); m_Socket.Send(Buffer, Ip); // add client if needed CClients *clients = g_Reflector.GetClients(); CClient *client = clients->FindClient(Callsign, Ip, PROTOCOL_YSF); // client already connected ? if ( client == NULL ) { std::cout << "YSF connect packet from " << Callsign << " at " << Ip << std::endl; // create the client CYsfClient *newclient = new CYsfClient(Callsign, Ip); // aautolink, if enabled #if YSF_AUTOLINK_ENABLE newclient->SetReflectorModule(YSF_AUTOLINK_MODULE); #endif // and append clients->AddClient(newclient); } else { client->Alive(); } // and done g_Reflector.ReleaseClients(); } } else if ( IsValidwirexPacket(Buffer, &Fich, &Callsign, &iWiresxCmd, &iWiresxArg) ) { //std::cout << "YSF Wires-x frame" << std::endl; // prepare the cmd object WiresxCmd = CWiresxCmd(Ip, Callsign, iWiresxCmd, iWiresxArg); // and post it to hadler's queue m_WiresxCmdHandler.GetCmdQueue()->push(WiresxCmd); m_WiresxCmdHandler.ReleaseCmdQueue(); } else if ( IsValidServerStatusPacket(Buffer) ) { std::cout << "YSF server status enquiry from " << Ip << std::endl; // reply EncodeServerStatusPacket(&Buffer); m_Socket.Send(Buffer, Ip); } else { // invalid packet //std::cout << "YSF packet (" << Buffer.size() << ") from " << Callsign << " at " << Ip << std::endl; //Buffer.DebugDump(g_Reflector.m_DebugFile); } } // handle end of streaming timeout CheckStreamsTimeout(); // handle queue from reflector HandleQueue(); // keep client alive if ( m_LastKeepaliveTime.DurationSinceNow() > YSF_KEEPALIVE_PERIOD ) { // HandleKeepalives(); // update time m_LastKeepaliveTime.Now(); } } //////////////////////////////////////////////////////////////////////////////////////// // streams helpers bool CYsfProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip) { bool newstream = false; // find the stream CPacketStream *stream = GetStream(Header->GetStreamId()); if ( stream == NULL ) { // no stream open yet, open a new one CCallsign via(Header->GetRpt1Callsign()); // find this client CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_YSF); if ( client != NULL ) { // get client callsign via = client->GetCallsign(); if ( Header->GetRpt2Module() == ' ' ) { // module not filled, get module it's linked to Header->SetRpt2Module(client->GetReflectorModule()); } else { // handle changing linked module to the one set on rpt2 if ( client->GetReflectorModule() != Header->GetRpt2Module() ) { std::cout << "YSF client " << client->GetCallsign() << " linking on module " << Header->GetRpt2Module() << std::endl; client->SetReflectorModule(Header->GetRpt2Module()); } } // and try to open the stream if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL ) { // keep the handle m_Streams.push_back(stream); newstream = true; } } // release g_Reflector.ReleaseClients(); // update last heard if ( g_Reflector.IsValidModule(Header->GetRpt2Module()) ) { g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign()); g_Reflector.ReleaseUsers(); } // delete header if needed if ( !newstream ) { delete Header; } } else { // stream already open // skip packet, but tickle the stream stream->Tickle(); // and delete packet delete Header; } // done return newstream; } //////////////////////////////////////////////////////////////////////////////////////// // queue helper void CYsfProtocol::HandleQueue(void) { m_Queue.Lock(); while ( !m_Queue.empty() ) { // get the packet CPacket *packet = m_Queue.front(); m_Queue.pop(); // get our sender's id int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); // encode CBuffer buffer; // check if it's header if ( packet->IsDvHeader() ) { // update local stream cache // this relies on queue feeder setting valid module id m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet); // encode it EncodeDvHeaderPacket((const CDvHeaderPacket &)*packet, &buffer); } // check if it's a last frame else if ( packet->IsLastPacket() ) { // encode it EncodeDvLastPacket(m_StreamsCache[iModId].m_dvHeader, &buffer); } // otherwise, just a regular DV frame else { // update local stream cache or send triplet when needed uint8 sid = packet->GetYsfPacketSubId(); if ( (sid >= 0) && (sid <= 4) ) { //std::cout << (int)sid; m_StreamsCache[iModId].m_dvFrames[sid] = CDvFramePacket((const CDvFramePacket &)*packet); if ( sid == 4 ) { EncodeDvPacket(m_StreamsCache[iModId].m_dvHeader, m_StreamsCache[iModId].m_dvFrames, &buffer); } } } // send it if ( buffer.size() > 0 ) { // and push it to all our clients linked to the module and who are not streaming in CClients *clients = g_Reflector.GetClients(); int index = -1; CClient *client = NULL; while ( (client = clients->FindNextClient(PROTOCOL_YSF, &index)) != NULL ) { // is this client busy ? if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) { // no, send the packet m_Socket.Send(buffer, client->GetIp()); } } g_Reflector.ReleaseClients(); } // done delete packet; } m_Queue.Unlock(); } //////////////////////////////////////////////////////////////////////////////////////// // keepalive helpers void CYsfProtocol::HandleKeepalives(void) { // YSF protocol keepalive request is client tasks // here, just check that all clients are still alive // and disconnect them if not // iterate on clients CClients *clients = g_Reflector.GetClients(); int index = -1; CClient *client = NULL; while ( (client = clients->FindNextClient(PROTOCOL_YSF, &index)) != NULL ) { // is this client busy ? if ( client->IsAMaster() ) { // yes, just tickle it client->Alive(); } // check it's still with us else if ( !client->IsAlive() ) { // no, remove it std::cout << "YSF client " << client->GetCallsign() << " keepalive timeout" << std::endl; clients->RemoveClient(client); } } g_Reflector.ReleaseClients(); } //////////////////////////////////////////////////////////////////////////////////////// // DV packet decoding helpers bool CYsfProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign) { uint8 tag[] = { 'Y','S','F','P' }; bool valid = false; if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) { callsign->SetCallsign(Buffer.data()+4, 8); callsign->SetModule(YSF_MODULE_ID); valid = (callsign->IsValid()); } return valid; } bool CYsfProtocol::IsValidDvPacket(const CBuffer &Buffer, CYSFFICH *Fich) { uint8 tag[] = { 'Y','S','F','D' }; bool valid = false; if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) { // decode YSH fich if ( Fich->decode(&(Buffer.data()[40])) ) { valid = (Fich->getDT() == YSF_DT_VD_MODE2); } } return valid; } bool CYsfProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, CDvHeaderPacket **header, CDvFramePacket **frames) { bool valid = false; *header = NULL; frames[0] = NULL; frames[1] = NULL; // DV header ? if ( Fich.getFI() == YSF_FI_HEADER ) { // get stream id uint32 uiStreamId = IpToStreamId(Ip); // get header data CYSFPayload ysfPayload; if ( ysfPayload.processHeaderData((unsigned char *)&(Buffer.data()[35])) ) { // build DVHeader char sz[YSF_CALLSIGN_LENGTH+1]; ::memcpy(sz, &(Buffer.data()[14]), YSF_CALLSIGN_LENGTH); sz[YSF_CALLSIGN_LENGTH] = 0; CCallsign csMY = CCallsign((const char *)sz); ::memcpy(sz, &(Buffer.data()[4]), YSF_CALLSIGN_LENGTH); sz[YSF_CALLSIGN_LENGTH] = 0; CCallsign rpt1 = CCallsign((const char *)sz); rpt1.SetModule(YSF_MODULE_ID); CCallsign rpt2 = m_ReflectorCallsign; if ( (Fich.getSQ() >= 10) && (Fich.getSQ() < 10+NB_OF_MODULES) ) { // set module based on DG-ID value rpt2.SetModule( 'A' + (char)(Fich.getSQ() - 10) ); } else { // as YSF protocol does not provide a module-tranlatable // destid, set module to none and rely on OnDvHeaderPacketIn() // to later fill it with proper value rpt2.SetModule(' '); } // and packet *header = new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, Fich.getFN()); } // and 2 DV Frames { uint8 uiAmbe[AMBE_SIZE]; ::memset(uiAmbe, 0x00, sizeof(uiAmbe)); frames[0] = new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 0, 0); frames[1] = new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 1, 0); } // check validity of packets if ( ((*header) == NULL) || !(*header)->IsValid() || (frames[0] == NULL) || !(frames[0]->IsValid()) || (frames[1] == NULL) || !(frames[1]->IsValid()) ) { delete *header; *header = NULL; delete frames[0]; delete frames[1]; frames[0] = NULL; frames[1] = NULL; } else { valid = true; } } // done return valid; } bool CYsfProtocol::IsValidDvFramePacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, CDvFramePacket **frames) { bool valid = false; frames[0] = NULL; frames[1] = NULL; frames[2] = NULL; frames[3] = NULL; frames[4] = NULL; // is it DV frame ? if ( Fich.getFI() == YSF_FI_COMMUNICATIONS ) { // get stream id uint32 uiStreamId = IpToStreamId(Ip); // get DV frames uint8 ambe0[AMBEPLUS_SIZE]; uint8 ambe1[AMBEPLUS_SIZE]; uint8 ambe2[AMBEPLUS_SIZE]; uint8 ambe3[AMBEPLUS_SIZE]; uint8 ambe4[AMBEPLUS_SIZE]; uint8 *ambes[5] = { ambe0, ambe1, ambe2, ambe3, ambe4 }; CYsfUtils::DecodeVD2Vchs((unsigned char *)&(Buffer.data()[35]), ambes); // get DV frames uint8 fid = Buffer.data()[34]; frames[0] = new CDvFramePacket(ambe0, uiStreamId, Fich.getFN(), 0, fid); frames[1] = new CDvFramePacket(ambe1, uiStreamId, Fich.getFN(), 1, fid); frames[2] = new CDvFramePacket(ambe2, uiStreamId, Fich.getFN(), 2, fid); frames[3] = new CDvFramePacket(ambe3, uiStreamId, Fich.getFN(), 3, fid); frames[4] = new CDvFramePacket(ambe4, uiStreamId, Fich.getFN(), 4, fid); // check validity of packets if ( (frames[0] == NULL) || !(frames[0]->IsValid()) || (frames[1] == NULL) || !(frames[1]->IsValid()) || (frames[2] == NULL) || !(frames[2]->IsValid()) || (frames[3] == NULL) || !(frames[3]->IsValid()) || (frames[4] == NULL) || !(frames[4]->IsValid()) ) { delete frames[0]; delete frames[1]; delete frames[2]; delete frames[3]; delete frames[4]; frames[0] = NULL; frames[1] = NULL; frames[2] = NULL; frames[3] = NULL; frames[4] = NULL; } else { valid = true; } } // done return valid; } bool CYsfProtocol::IsValidDvLastFramePacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, CDvFramePacket **frames) { bool valid = false; frames[0] = NULL; frames[1] = NULL; // DV header ? if ( Fich.getFI() == YSF_FI_TERMINATOR ) { // get stream id uint32 uiStreamId = IpToStreamId(Ip); // get DV frames { uint8 uiAmbe[AMBE_SIZE]; ::memset(uiAmbe, 0x00, sizeof(uiAmbe)); frames[0] = new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 0, 0); frames[1] = new CDvLastFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 1, 0); } // check validity of packets if ( (frames[0] == NULL) || !(frames[0]->IsValid()) || (frames[1] == NULL) || !(frames[1]->IsValid()) ) { delete frames[0]; delete frames[1]; frames[0] = NULL; frames[1] = NULL; } else { valid = true; } } // done return valid; } //////////////////////////////////////////////////////////////////////////////////////// // DV packet encoding helpers void CYsfProtocol::EncodeConnectAckPacket(CBuffer *Buffer) const { uint8 tag[] = { 'Y','S','F','P','R','E','F','L','E','C','T','O','R',0x20 }; Buffer->Set(tag, sizeof(tag)); } bool CYsfProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Header, CBuffer *Buffer) const { uint8 tag[] = { 'Y','S','F','D' }; uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; char sz[YSF_CALLSIGN_LENGTH]; uint8 fichd[YSF_FICH_LENGTH_BYTES]; // tag Buffer->Set(tag, sizeof(tag)); // rpt1 ::memset(sz, ' ', sizeof(sz)); Header.GetRpt1Callsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // my ::memset(sz, ' ', sizeof(sz)); Header.GetMyCallsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // dest Buffer->Append(dest, 10); // net frame counter Buffer->Append((uint8)0x00); // FS Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); // FICH CYSFFICH fich; fich.setFI(YSF_FI_HEADER); fich.setCS(2U); //fich.setFN(Header.GetYsfPacketId()); fich.setFN(0U); fich.setFT(7U); fich.setDev(0U); fich.setMR(YSF_MR_BUSY); fich.setDT(YSF_DT_VD_MODE2); fich.setSQL(0U); fich.setSQ(0U); fich.encode(fichd); Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); // payload unsigned char csd1[20U], csd2[20U]; ::memset(csd1, '*', YSF_CALLSIGN_LENGTH); ::memset(csd1 + YSF_CALLSIGN_LENGTH, ' ', YSF_CALLSIGN_LENGTH); Header.GetMyCallsign().GetCallsignString(sz); ::memcpy(csd1 + YSF_CALLSIGN_LENGTH, sz, ::strlen(sz)); ::memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); CYSFPayload payload; uint8 temp[120]; payload.writeHeader(temp, csd1, csd2); Buffer->Append(temp+30, 120-30); // done return true; } bool CYsfProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFramePacket *DvFrames, CBuffer *Buffer) const { uint8 tag[] = { 'Y','S','F','D' }; uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; uint8 gps[] = { 0x52,0x22,0x61,0x5F,0x27,0x03,0x5E,0x20,0x20,0x20 }; char sz[YSF_CALLSIGN_LENGTH]; uint8 fichd[YSF_FICH_LENGTH_BYTES]; // tag Buffer->Set(tag, sizeof(tag)); // rpt1 ::memset(sz, ' ', sizeof(sz)); Header.GetRpt1Callsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // my ::memset(sz, ' ', sizeof(sz)); Header.GetMyCallsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // dest Buffer->Append(dest, 10); // net frame counter Buffer->Append(DvFrames[0].GetYsfPacketFrameId()); // FS Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); // FICH CYSFFICH fich; fich.setFI(YSF_FI_COMMUNICATIONS); fich.setCS(2U); fich.setFN(DvFrames[0].GetYsfPacketId()); fich.setFT(6U); fich.setDev(0U); fich.setMR(YSF_MR_BUSY); fich.setDT(YSF_DT_VD_MODE2); fich.setSQL(0U); fich.setSQ(0U); fich.encode(fichd); Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); // payload CYSFPayload payload; uint8 temp[120]; ::memset(temp, 0x00, sizeof(temp)); // DV for ( int i = 0; i < 5; i++ ) { CYsfUtils::EncodeVD2Vch((unsigned char *)DvFrames[i].GetAmbePlus(), temp+35+(18*i)); } // data switch (DvFrames[0].GetYsfPacketId()) { case 0: // Dest payload.writeVDMode2Data(temp, (const unsigned char*)"**********"); break; case 1: // Src ::memset(sz, ' ', sizeof(sz)); Header.GetMyCallsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; payload.writeVDMode2Data(temp, (const unsigned char*)sz); break; case 2: // Down ::memset(sz, ' ', sizeof(sz)); Header.GetRpt1Callsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; payload.writeVDMode2Data(temp, (const unsigned char*)sz); break; case 5: // Rem3+4 // we need to provide a fake radioid for radios // to display src callsign payload.writeVDMode2Data(temp, (const unsigned char*)" G0gBJ"); break; case 6: // DT1 // we need to issue a fake gps string with proper terminator // and crc for radios to display src callsign payload.writeVDMode2Data(temp, gps); break; default: payload.writeVDMode2Data(temp, (const unsigned char*)" "); break; } Buffer->Append(temp+30, 120-30); // done return true; } bool CYsfProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Header, CBuffer *Buffer) const { uint8 tag[] = { 'Y','S','F','D' }; uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; char sz[YSF_CALLSIGN_LENGTH]; uint8 fichd[YSF_FICH_LENGTH_BYTES]; // tag Buffer->Set(tag, sizeof(tag)); // rpt1 ::memset(sz, ' ', sizeof(sz)); Header.GetRpt1Callsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // my ::memset(sz, ' ', sizeof(sz)); Header.GetMyCallsign().GetCallsignString(sz); sz[::strlen(sz)] = ' '; Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); // dest Buffer->Append(dest, 10); // net frame counter Buffer->Append((uint8)0x00); // FS Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); // FICH CYSFFICH fich; fich.setFI(YSF_FI_TERMINATOR); fich.setCS(2U); //fich.setFN(Header.GetYsfPacketId()); fich.setFN(0U); fich.setFT(7U); fich.setDev(0U); fich.setMR(YSF_MR_BUSY); fich.setDT(YSF_DT_VD_MODE2); fich.setSQL(0U); fich.setSQ(0U); fich.encode(fichd); Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); // payload unsigned char csd1[20U], csd2[20U]; ::memset(csd1, '*', YSF_CALLSIGN_LENGTH); ::memset(csd1 + YSF_CALLSIGN_LENGTH, ' ', YSF_CALLSIGN_LENGTH); Header.GetMyCallsign().GetCallsignString(sz); ::memcpy(csd1 + YSF_CALLSIGN_LENGTH, sz, ::strlen(sz)); ::memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); CYSFPayload payload; uint8 temp[120]; payload.writeHeader(temp, csd1, csd2); Buffer->Append(temp+30, 120-30); // done return true; } //////////////////////////////////////////////////////////////////////////////////////// // Wires-X packet decoding helpers bool CYsfProtocol::IsValidwirexPacket(const CBuffer &Buffer, CYSFFICH *Fich, CCallsign *Callsign, int *Cmd, int *Arg) { uint8 tag[] = { 'Y','S','F','D' }; uint8 DX_REQ[] = {0x5DU, 0x71U, 0x5FU}; uint8 CONN_REQ[] = {0x5DU, 0x23U, 0x5FU}; uint8 DISC_REQ[] = {0x5DU, 0x2AU, 0x5FU}; uint8 ALL_REQ[] = {0x5DU, 0x66U, 0x5FU}; uint8 command[300]; CYSFPayload payload; bool valid = false; if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) { // decode YSH fich if ( Fich->decode(&(Buffer.data()[40])) ) { //std::cout << (int)Fich->getDT() << "," // << (int)Fich->getFI() << "," // << (int)Fich->getFN() << "," // << (int)Fich->getFT() // << std::endl; valid = (Fich->getDT() == YSF_DT_DATA_FR_MODE); valid &= (Fich->getFI() == YSF_FI_COMMUNICATIONS); if ( valid ) { // get callsign Callsign->SetCallsign(&(Buffer.data()[4]), CALLSIGN_LEN, false); Callsign->SetModule(YSF_MODULE_ID); // decode payload if ( Fich->getFN() == 0U ) { valid = false; } else if ( Fich->getFN() == 1U ) { valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + 0U); } else { valid &= payload.readDataFRModeData1(&(Buffer.data()[35]), command + (Fich->getFN() - 1U) * 20U + 0U); if ( valid ) { valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + (Fich->getFN() - 1U) * 20U + 20U); } } // check crc if end found if ( Fich->getFN() == Fich->getFT() ) { valid = false; // Find the end marker for (unsigned int i = Fich->getFN() * 20U; i > 0U; i--) { if (command[i] == 0x03U) { unsigned char crc = CCRC::addCRC(command, i + 1U); if (crc == command[i + 1U]) valid = true; break; } } } // and crack the command if ( valid ) { // get argument char buffer[4U]; ::memcpy(buffer, command + 5U + 2U, 3U); buffer[3U] = 0x00U; *Arg = ::atoi(buffer); // and decode command if (::memcmp(command + 1U, DX_REQ, 3U) == 0) { *Cmd = WIRESX_CMD_DX_REQ; *Arg = 0; } else if (::memcmp(command + 1U, ALL_REQ, 3U) == 0) { // argument is start index of list if ( *Arg > 0 ) (*Arg)--; // check if all or search if ( ::memcmp(command + 5U, "01", 2) == 0 ) { *Cmd = WIRESX_CMD_ALL_REQ; } else if ( ::memcmp(command + 5U, "11", 2) == 0 ) { *Cmd = WIRESX_CMD_SEARCH_REQ; } } else if (::memcmp(command + 1U, CONN_REQ, 3U) == 0) { *Cmd = WIRESX_CMD_CONN_REQ; } else if (::memcmp(command + 1U, DISC_REQ, 3U) == 0) { *Cmd = WIRESX_CMD_DISC_REQ; *Arg = 0; } else { std::cout << "Wires-X unknown command" << std::endl; *Cmd = WIRESX_CMD_UNKNOWN; *Arg = 0; valid = false; } } } } } return valid; } // server status packet decoding helpers bool CYsfProtocol::IsValidServerStatusPacket(const CBuffer &Buffer) const { uint8 tag[] = { 'Y','S','F','S' }; return ( (Buffer.size() >= 4) && (Buffer.Compare(tag, sizeof(tag)) == 0) ); } // server status packet encoding helpers bool CYsfProtocol::EncodeServerStatusPacket(CBuffer *Buffer) const { uint8 tag[] = { 'Y','S','F','S' }; uint8 description[] = { 'X','L','X',' ','r','e','f','l','e','c','t','o','r',' ' }; uint8 callsign[16]; // tag Buffer->Set(tag, sizeof(tag)); // hash ::memset(callsign, ' ', sizeof(callsign)); g_Reflector.GetCallsign().GetCallsign(callsign); char sz[16]; ::sprintf(sz, "%05u", CalcHash(callsign, 16) % 100000U); Buffer->Append((uint8 *)sz, 5); // name Buffer->Append(callsign, 16); // desscription Buffer->Append(description, 14); // connected clients CClients *clients = g_Reflector.GetClients(); int count = MIN(999, clients->GetSize()); g_Reflector.ReleaseClients(); ::sprintf(sz, "%03u", count); Buffer->Append((uint8 *)sz, 3); // done return true; } uint32 CYsfProtocol::CalcHash(const uint8 *buffer, int len) const { uint32 hash = 0U; for ( int i = 0; i < len; i++) { hash += buffer[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } //////////////////////////////////////////////////////////////////////////////////////// // uiStreamId helpers // uiStreamId helpers uint32 CYsfProtocol::IpToStreamId(const CIp &ip) const { return ip.GetAddr() ^ (uint32)(MAKEDWORD(ip.GetPort(), ip.GetPort())); } //////////////////////////////////////////////////////////////////////////////////////// // debug #ifdef DEBUG_DUMPFILE bool CYsfProtocol::DebugTestDecodePacket(const CBuffer &Buffer) { uint8 tag[] = { 'Y','S','F','D' }; static uint8 command[4098]; static int len; CYSFFICH Fich; CYSFPayload payload; CBuffer dump; bool valid = false; if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) { // decode YSH fich if ( Fich.decode(&(Buffer.data()[40])) ) { std::cout << (int)Fich.getDT() << "," << (int)Fich.getFI() << "," << (int)Fich.getBN() << "," << (int)Fich.getBT() << "," << (int)Fich.getFN() << "," << (int)Fich.getFT() << " : "; switch ( Fich.getFI() ) { case YSF_FI_HEADER: len = 0; ::memset(command, 0x00, sizeof(command)); std::cout << "Header" << std::endl; break; case YSF_FI_TERMINATOR: std::cout << "Trailer" << std::endl; std::cout << "length of payload : " << len << std::endl; dump.Set(command, len); dump.DebugDump(g_Reflector.m_DebugFile); dump.DebugDumpAscii(g_Reflector.m_DebugFile); break; case YSF_FI_COMMUNICATIONS: if ( Fich.getDT() == YSF_DT_DATA_FR_MODE ) { valid = payload.readDataFRModeData1(&(Buffer.data()[35]), command + len); len += 20; valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + len); len += 20; std::cout << "decoded ok" << std::endl; } break; } } else { std::cout << "invalid fich in packet" << std::endl; } } else { std::cout << "invalid size packet" << std::endl; } return valid; } #endif bool CYsfProtocol::DebugDumpHeaderPacket(const CBuffer &Buffer) { bool ok; CYSFFICH fich; CYSFPayload payload; uint8 data[200]; :: memset(data, 0, sizeof(data)); ok = IsValidDvPacket(Buffer, &fich); if ( ok && (fich.getFI() == YSF_FI_HEADER) ) { ok &= payload.processHeaderData((unsigned char *)&(Buffer.data()[35])); } std::cout << "HD-" <<(ok ? "ok " : "xx ") << "src: " << payload.getSource() << "dest: " << payload.getDest() << std::endl; return ok; } bool CYsfProtocol::DebugDumpDvPacket(const CBuffer &Buffer) { bool ok; CYSFFICH fich; CYSFPayload payload; uint8 data[200]; :: memset(data, 0, sizeof(data)); ok = IsValidDvPacket(Buffer, &fich); if ( ok && (fich.getFI() == YSF_FI_COMMUNICATIONS) ) { ok &= payload.readVDMode2Data(&(Buffer.data()[35]), data); } std::cout << "DV-" <<(ok ? "ok " : "xx ") << "FN:" << (int)fich.getFN() << " payload: " << (char *)data << std::endl; return ok; } bool CYsfProtocol::DebugDumpLastDvPacket(const CBuffer &Buffer) { bool ok; CYSFFICH fich; CYSFPayload payload; uint8 data[200]; :: memset(data, 0, sizeof(data)); ok = IsValidDvPacket(Buffer, &fich); if ( ok && (fich.getFI() == YSF_FI_TERMINATOR) ) { ok &= payload.processHeaderData((unsigned char *)&(Buffer.data()[35])); } std::cout << "TC-" <<(ok ? "ok " : "xx ") << "src: " << payload.getSource() << "dest: " << payload.getDest() << std::endl; return ok; }