From e9645fad8272cc991b51cbc353ca14f069664c2b Mon Sep 17 00:00:00 2001 From: Tom Early Date: Sat, 13 Oct 2018 17:29:34 -0700 Subject: [PATCH] DPlus authorization and "empty txt message" bug --- DPlusAuthenticator.cpp | 177 +++++++++++++++++++++++++++ DPlusAuthenticator.h | 40 +++++++ ITAP.README | 3 + MMDVM.README | 3 + Makefile | 4 +- QnetGateway.cpp | 60 +++++++--- QnetGateway.h | 4 +- QnetLink.cpp | 27 +++-- QnetLink.h | 7 +- TCPReaderWriterClient.cpp | 243 ++++++++++++++++++++++++++++++++++++++ TCPReaderWriterClient.h | 56 +++++++++ qn.dvap.cfg | 12 ++ qn.everything.cfg | 12 ++ qn.icom.cfg | 12 ++ qn.itap.cfg | 12 ++ qn.mmdvm.cfg | 12 ++ versions.h | 4 +- 17 files changed, 658 insertions(+), 30 deletions(-) create mode 100644 DPlusAuthenticator.cpp create mode 100644 DPlusAuthenticator.h create mode 100644 TCPReaderWriterClient.cpp create mode 100644 TCPReaderWriterClient.h diff --git a/DPlusAuthenticator.cpp b/DPlusAuthenticator.cpp new file mode 100644 index 0000000..8dea5d2 --- /dev/null +++ b/DPlusAuthenticator.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010-2015 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Thomas A. Early N7TAE + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DPlusAuthenticator.h" +//#include "DStarDefines.h" +//#include "Utils.h" +//#include "Defs.h" + +CDPlusAuthenticator::CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address) : +m_loginCallsign(loginCallsign), +m_address(address) +{ + assert(loginCallsign.size()); + + Trim(m_loginCallsign); +} + +CDPlusAuthenticator::~CDPlusAuthenticator() +{ +} + +bool CDPlusAuthenticator::Process(std::map &gwy_map, const bool reflectors, const bool repeaters) +// return true if everything went okay +{ + struct addrinfo hints, *infoptr; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; // AF_INET means IPv4 only addresses + hints.ai_socktype = SOCK_STREAM; + + int result = getaddrinfo(m_address.c_str(), NULL, &hints, &infoptr); + if (result) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result)); + return false; + } + + struct addrinfo *p; + char host[256]; + + bool success = false; + + for (p = infoptr; p != NULL && !success; p = p->ai_next) { + getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); + printf("Trying %s from %s\n", host, m_address.c_str()); + success = authenticate(m_loginCallsign, std::string(host), gwy_map, reflectors, repeaters); + } + + freeaddrinfo(infoptr); + return success; +} + +bool CDPlusAuthenticator::authenticate(const std::string &callsign, const std::string &hostname, std::map &gwy_map, const bool reflectors, const bool repeaters) +{ + CTCPReaderWriterClient socket(hostname, 20001U); + + bool ret = socket.open(); + if (!ret) + return false; + + unsigned char* buffer = new unsigned char[4096U]; + ::memset(buffer, ' ', 56U); + + buffer[0U] = 0x38U; + buffer[1U] = 0xC0U; + buffer[2U] = 0x01U; + buffer[3U] = 0x00U; + + ::memcpy(buffer+4, callsign.c_str(), callsign.size()); + ::memcpy(buffer+12, "DV019999", 8); + ::memcpy(buffer+28, "W7IB2", 5); + ::memcpy(buffer+40, "DHS0257", 7); + + ret = socket.write(buffer, 56U); + if (!ret) { + socket.close(); + delete[] buffer; + return false; + } + + ret = read(socket, buffer, 2U); + + while (ret) { + unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U]; + + // Ensure that we get exactly len - 2U bytes from the TCP stream + ret = read(socket, buffer + 2U, len - 2U); + if (!ret) { + fprintf(stderr, "Short read from %s:20001", hostname.c_str()); + return false; + } + + if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U) { + fprintf(stderr, "Invalid packet received from %s:20001", hostname.c_str()); + return false; + } + + for (unsigned int i = 8U; (i + 25U) < len; i += 26U) { + std::string address((char *)(buffer + i)); + std::string name((char *)(buffer + i + 16U)); + + Trim(address); + Trim(name); + + // Get the active flag + bool active = (buffer[i + 25U] & 0x80U) == 0x80U; + + // An empty name or IP address or an inactive gateway/reflector is not added + if (address.size()>0U && name.size()>0U && active) { + if (reflectors && 0==name.compare(0, 3, "REF")) + gwy_map[name] = address.append(" 20001"); + else if (repeaters && name.compare(0, 3, "REF")) + gwy_map[name] = address.append(" 20001"); + } + } + + ret = read(socket, buffer, 2U); + } + + printf("Authorized DPlus with %s using callsign %s\n", hostname.c_str(), callsign.c_str()); + printf("Added %ld DPlus gateways\n", gwy_map.size()); + socket.close(); + + delete[] buffer; + + return true; +} + +void CDPlusAuthenticator::Trim(std::string &s) +{ + auto it = s.begin(); + while (it!=s.end() && isspace(*it)) + s.erase(it); + auto rit = s.rbegin(); + while (rit!=s.rend() && isspace(*rit)) { + s.resize(s.size() - 1); + rit = s.rbegin(); + } +} + +bool CDPlusAuthenticator::read(CTCPReaderWriterClient &socket, unsigned char *buffer, unsigned int len) const +{ + unsigned int offset = 0U; + + do { + int n = socket.read(buffer + offset, len - offset, 10U); + if (n < 0) + return false; + + offset += n; + } while ((len - offset) > 0U); + + return true; +} diff --git a/DPlusAuthenticator.h b/DPlusAuthenticator.h new file mode 100644 index 0000000..c9e712a --- /dev/null +++ b/DPlusAuthenticator.h @@ -0,0 +1,40 @@ +#pragma once +/* + * Copyright (C) 2010-2013 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Thomas A. Early N7TAE + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +**/ + +#include +#include +#include +#include "TCPReaderWriterClient.h" + +class CDPlusAuthenticator { +public: + CDPlusAuthenticator(const std::string &loginCallsign, const std::string &address); + ~CDPlusAuthenticator(); + + bool Process(std::map &gwy_map, const bool reflectors, const bool repeaters); + +private: + std::string m_loginCallsign; + std::string m_address; + + void Trim(std::string &s); + bool authenticate(const std::string &callsign, const std::string &hostname, std::map &gwy_map, const bool reflectors, const bool repeaters); + bool read(CTCPReaderWriterClient &socket, unsigned char *buffer, unsigned int len) const; +}; diff --git a/ITAP.README b/ITAP.README index df0147e..3165fbb 100644 --- a/ITAP.README +++ b/ITAP.README @@ -30,6 +30,9 @@ your start. On a Raspberry Pi, you can do all of this with the configuration men USB devices on your system the device might end up somewhere else. Do "ls /dev" before and after plugging in your cable to figure out where it is. If it's not on /dev/ttyUSB0, uncomment the device line and put in the correct device. + If you are planning on linking to a Trust DPlus (REF) reflector or repeater, + please look over the "dplus" section to enable this. You need to already be + registered (www.dstargateway.org) to do this. 6) You need a gwys.txt file for all the systems to which you may wish to link. If you want to be able to link to repeaters: ./get_gwy_list.sh diff --git a/MMDVM.README b/MMDVM.README index 7fb6a8a..30f01a3 100644 --- a/MMDVM.README +++ b/MMDVM.README @@ -59,6 +59,9 @@ disable the serial0 console in the /boot/cmdline.txt file: Remove the reference 12) You need a configuration file called qn.cfg for QnetGateway. A good, nearly working config file is qn.mmdvm.cfg. Copy it to qn.cfg and edit it. + If you are planning on linking to a Trust DPlus (REF) reflector or repeater, + please look over the "dplus" section to enable this. You need to already be + registered (www.dstargateway.org) to do this. 13) You need a gwys.txt file for all the systems to which you may wish to link. If you want to be able to link to repeaters: ./get_gwy_list.sh diff --git a/Makefile b/Makefile index 14893f7..ec079af 100644 --- a/Makefile +++ b/Makefile @@ -54,8 +54,8 @@ itap : $(TAP_PROGRAMS) qngateway : $(IRCOBJS) QnetGateway.o aprs.o g++ $(CPPFLAGS) -o qngateway QnetGateway.o aprs.o $(IRCOBJS) $(LDFLAGS) -pthread -qnlink : QnetLink.o Random.o - g++ $(CPPFLAGS) -o qnlink QnetLink.o Random.o $(LDFLAGS) -pthread +qnlink : QnetLink.o DPlusAuthenticator.o TCPReaderWriterClient.o Random.o + g++ $(CPPFLAGS) -o qnlink QnetLink.o DPlusAuthenticator.o TCPReaderWriterClient.o Random.o $(LDFLAGS) -pthread qnrelay : QnetRelay.o g++ $(CPPFLAGS) -o qnrelay QnetRelay.o $(LDFLAGS) diff --git a/QnetGateway.cpp b/QnetGateway.cpp index 4e97e6b..d8e90f6 100644 --- a/QnetGateway.cpp +++ b/QnetGateway.cpp @@ -814,6 +814,11 @@ void CQnetGateway::ProcessTimeouts() } } +// new_group is true if we are processing the first voice packet of a 2-voice packet pair. The high order nibble of the first byte of +// this first packet specifed the type of slow data that is being sent. +// the to_print is an integer that counts down how many 2-voice-frame pairs remain to be processed. +// ABC_grp means that we are processing a 20-character message. +// C_seen means that we are processing the last 2-voice-frame packet on a 20 character message. void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsigned char header_type, bool *new_group, short *to_print, bool *ABC_grp, bool *C_seen) { /* extract 20-byte RADIO ID */ @@ -829,7 +834,7 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi if (new_group[i]) { header_type = c1 & 0xf0; - // header squelch + // header squelch if ((header_type == 0x50) || (header_type == 0xc0)) { new_group[i] = false; to_print[i] = 0; @@ -933,7 +938,7 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi /* We should NOT see any more text, if we already processed text, so blank out the codes. */ - if (band_txt[i].txt_stats_sent) { + if (band_txt[i].sent_key_on_msg) { data[0] = 0x70; data[1] = 0x4f; data[2] = 0x93; @@ -944,12 +949,26 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi band_txt[i].txt_cnt = 0; } } - else { + else { // header type is not header, squelch, gps or message new_group[i] = false; to_print[i] = 0; ABC_grp[i] = false; } - } else { + } + else { // not a new_group, this is the second of a two-voice-frame pair + if (! band_txt[i].sent_key_on_msg && vPacketCount > 100) { + // 100 voice packets received and still no 20-char message! + /*** if YRCALL is CQCQCQ, set dest_rptr ***/ + band_txt[i].txt[0] = '\0'; + if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0) { + set_dest_rptr(i, band_txt[i].dest_rptr); + if (memcmp(band_txt[i].dest_rptr, "REF", 3) == 0) + band_txt[i].dest_rptr[0] = '\0'; + } + // we have the 20-character message, send it to the server... + ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, band_txt[i].lh_sfx, (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].dest_rptr, band_txt[i].txt); + band_txt[i].sent_key_on_msg = true; + } if (to_print[i] == 3) { if (ABC_grp[i]) { band_txt[i].txt[band_txt[i].txt_cnt] = c1; @@ -964,7 +983,7 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi /* We should NOT see any more text, if we already processed text, so blank out the codes. */ - if (band_txt[i].txt_stats_sent) { + if (band_txt[i].sent_key_on_msg) { data[0] = 0x70; data[1] = 0x4f; data[2] = 0x93; @@ -972,16 +991,16 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi if ((band_txt[i].txt_cnt >= 20) || C_seen[i]) { band_txt[i].txt[band_txt[i].txt_cnt] = '\0'; - if (!band_txt[i].txt_stats_sent) { + if ( ! band_txt[i].sent_key_on_msg) { /*** if YRCALL is CQCQCQ, set dest_rptr ***/ if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0) { set_dest_rptr(i, band_txt[i].dest_rptr); if (memcmp(band_txt[i].dest_rptr, "REF", 3) == 0) band_txt[i].dest_rptr[0] = '\0'; } - + // we have the 20-character message, send it to the server... ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, band_txt[i].lh_sfx, (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].dest_rptr, band_txt[i].txt); - band_txt[i].txt_stats_sent = true; + band_txt[i].sent_key_on_msg = true; } band_txt[i].txt_cnt = 0; } @@ -995,7 +1014,7 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid, unsi band_txt[i].temp_line_cnt = 0; } - /* do not copy CR, NL */ + /* do not copy carrige return or newline */ if ((c1 != '\r') && (c1 != '\n')) { band_txt[i].temp_line[band_txt[i].temp_line_cnt] = c1; band_txt[i].temp_line_cnt++; @@ -1394,7 +1413,7 @@ void CQnetGateway::Process() } if (recvlen == 58) { - + vPacketCount = 0U; if (bool_qso_details) printf("id=%04x cntr=%04x start RPTR ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s ip=%s\n", ntohs(rptrbuf.vpkt.streamid), ntohs(rptrbuf.counter), rptrbuf.vpkt.hdr.ur, rptrbuf.vpkt.hdr.r1, rptrbuf.vpkt.hdr.r2, rptrbuf.vpkt.hdr.my, rptrbuf.vpkt.hdr.nm, inet_ntoa(fromRptr.sin_addr)); @@ -1435,7 +1454,7 @@ void CQnetGateway::Process() band_txt[i].txt[0] = '\0'; band_txt[i].txt_cnt = 0; - band_txt[i].txt_stats_sent = false; + band_txt[i].sent_key_on_msg = false; band_txt[i].dest_rptr[0] = '\0'; @@ -1896,7 +1915,18 @@ void CQnetGateway::Process() dtmf_counter[i] = 0; dtmf_last_frame[i] = 0; } - + if (! band_txt[i].sent_key_on_msg) { + band_txt[i].txt[0] = '\0'; + if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0) { + set_dest_rptr(i, band_txt[i].dest_rptr); + if (memcmp(band_txt[i].dest_rptr, "REF", 3) == 0) + band_txt[i].dest_rptr[0] = '\0'; + } + // we have the 20-character message, send it to the server... + ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, band_txt[i].lh_sfx, (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].dest_rptr, band_txt[i].txt); + band_txt[i].sent_key_on_msg = true; + } + // send the "key off" message, this will end up in the openquad.net Last Heard webpage. ii->sendHeardWithTXStats(band_txt[i].lh_mycall, band_txt[i].lh_sfx, band_txt[i].lh_yrcall, band_txt[i].lh_rpt1, band_txt[i].lh_rpt2, band_txt[i].flags[0], band_txt[i].flags[1], band_txt[i].flags[2], band_txt[i].num_dv_frames, band_txt[i].num_dv_silent_frames, band_txt[i].num_bit_errors); band_txt[i].streamID = 0; @@ -1955,8 +1985,8 @@ void CQnetGateway::Process() break; } } - - if (recvlen == 29) + vPacketCount++; + if (recvlen == 29) // process the slow data from every voice packet ProcessSlowData(rptrbuf.vpkt.vasd.text, rptrbuf.vpkt.streamid, header_type, new_group, to_print, ABC_grp, C_seen); else ProcessSlowData(rptrbuf.vpkt.vasd1.text, rptrbuf.vpkt.streamid, header_type, new_group, to_print, ABC_grp, C_seen); @@ -2558,7 +2588,7 @@ int CQnetGateway::Init(char *cfgfile) band_txt[i].txt[0] = '\0'; band_txt[i].txt_cnt = 0; - band_txt[i].txt_stats_sent = false; + band_txt[i].sent_key_on_msg = false; band_txt[i].dest_rptr[0] = '\0'; diff --git a/QnetGateway.h b/QnetGateway.h index 5dd476e..15e6dd6 100644 --- a/QnetGateway.h +++ b/QnetGateway.h @@ -58,7 +58,7 @@ typedef struct band_txt_tag { time_t last_time; char txt[64]; // Only 20 are used unsigned short txt_cnt; - bool txt_stats_sent; + bool sent_key_on_msg; char dest_rptr[CALL_SIZE + 1]; @@ -93,6 +93,8 @@ private: int play_wait, play_delay, echotest_rec_timeout, voicemail_rec_timeout, from_remote_g2_timeout, from_local_rptr_timeout, dtmf_digit; + unsigned int vPacketCount; + std::map portmap; // data needed for aprs login and aprs beacon diff --git a/QnetLink.cpp b/QnetLink.cpp index 6cd1d04..da8cd5e 100644 --- a/QnetLink.cpp +++ b/QnetLink.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -45,16 +44,13 @@ #include #include #include -#include -#include -#include -#include #include #include #include #include #include "versions.h" +#include "DPlusAuthenticator.h" #include "QnetLink.h" using namespace libconfig; @@ -315,6 +311,15 @@ void CQnetLink::print_status_file() /* Open text file of repeaters, reflectors */ bool CQnetLink::load_gwys(const std::string &filename) { + // DPlus Authenticate + if (dplus_authorize) { + CDPlusAuthenticator auth(owner, std::string("auth.dstargateway.org")); + if (auth.Process(gwy_list, dplus_reflectors, dplus_repeaters)) + fprintf(stdout, "DPlus Authorization complete.\n"); + else + fprintf(stderr, "DPlus Authorization failed!\n"); + } + char inbuf[1024]; const char *delim = " "; @@ -397,11 +402,11 @@ bool CQnetLink::load_gwys(const std::string &filename) sprintf(payload, "%s %s", host, port); auto gwy_pos = gwy_list.find(call); - if (gwy_pos == gwy_list.end()) { - gwy_list[call] = payload; + if (gwy_pos == gwy_list.end()) printf("Added Call=[%s], payload=[%s]\n",call, payload); - } else - printf("Call [%s] is duplicate\n", call); + else + printf("%s %s has been redefined!\n", call, payload); + gwy_list[call] = payload; } fclose(fp); @@ -714,6 +719,10 @@ bool CQnetLink::read_config(const char *cfgFile) rf_inactivity_timer[i] = timer; } + get_value(cfg, "dplus.authorize", dplus_authorize, false); + get_value(cfg, "dplus.use_reflectors", dplus_reflectors, true); + get_value(cfg, "dplus.use_repeaters", dplus_repeaters, true); + return false; } diff --git a/QnetLink.h b/QnetLink.h index 3d81e1c..2b4cb08 100644 --- a/QnetLink.h +++ b/QnetLink.h @@ -19,7 +19,11 @@ */ #include - +#include +#include +#include +#include +#include #include #include "versions.h" #include "QnetTypeDefs.h" @@ -83,6 +87,7 @@ private: /* configuration data */ std::string login_call, owner, to_g2_external_ip, my_g2_link_ip, gwys, status_file, qnvoice_file, announce_dir; bool only_admin_login, only_link_unlink, qso_details, bool_rptr_ack, announce; + bool dplus_authorize, dplus_reflectors, dplus_repeaters; int rmt_xrf_port, rmt_ref_port, rmt_dcs_port, my_g2_link_port, to_g2_external_port, delay_between, delay_before; char link_at_startup[CALL_SIZE+1]; unsigned int max_dongles, saved_max_dongles; diff --git a/TCPReaderWriterClient.cpp b/TCPReaderWriterClient.cpp new file mode 100644 index 0000000..c26c365 --- /dev/null +++ b/TCPReaderWriterClient.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2013 by Jonathan Naylor G4KLX + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "TCPReaderWriterClient.h" +//#include "UDPReaderWriter.h" +#include +#include +#include +#include + + +CTCPReaderWriterClient::CTCPReaderWriterClient(const std::string &address, unsigned int port, const std::string &localAddress) : +m_address(address), +m_port(port), +m_localAddress(localAddress), +m_fd(-1) +{ + assert(address.size()); + assert(port > 0U); +} + +CTCPReaderWriterClient::CTCPReaderWriterClient(int fd) : +m_address(), +m_port(0U), +m_localAddress(), +m_fd(fd) +{ + assert(fd >= 0); +} + +CTCPReaderWriterClient::CTCPReaderWriterClient() : +m_address(), +m_port(0U), +m_localAddress(), +m_fd(-1) +{ +} + +CTCPReaderWriterClient::~CTCPReaderWriterClient() +{ +} + +bool CTCPReaderWriterClient::open(const std::string& address, unsigned int port, const std::string& localAddress) +{ + m_address = address; + m_port = port; + m_localAddress = localAddress; + + return open(); +} + +bool CTCPReaderWriterClient::open() +{ + if (m_fd != -1) + return true; + + if (0 == m_address.size() || m_port == 0U) + return false; + + m_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd < 0) { + fprintf(stderr, "Cannot create the TCP client socket, err=%d\n", errno); + return false; + } + + if (m_localAddress.size()) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0U; + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.c_str()); + if (addr.sin_addr.s_addr == INADDR_NONE) { + fprintf(stderr, "The address is invalid - %s\n", m_localAddress.c_str()); + close(); + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { + fprintf(stderr, "Cannot bind the TCP client address, err=%d\n", errno); + close(); + return false; + } + } + + struct sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr = lookup(m_address); + + if (addr.sin_addr.s_addr == INADDR_NONE) { + close(); + return false; + } + + if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { + fprintf(stderr, "Cannot connect the TCP client socket, err=%d\n", errno); + close(); + return false; + } + + int noDelay = 1; + if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) { + fprintf(stderr, "Cannot set the TCP client socket option, err=%d\n", errno); + close(); + return false; + } + + return true; +} + +int CTCPReaderWriterClient::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs) +{ + assert(buffer != NULL); + assert(length > 0U); + assert(m_fd != -1); + + // Check that the recv() won't block + fd_set readFds; + FD_ZERO(&readFds); + FD_SET(m_fd, &readFds); + + // Return after timeout + timeval tv; + tv.tv_sec = secs; + tv.tv_usec = msecs * 1000; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { + fprintf(stderr, "Error returned from TCP client select, err=%d\n", errno); + return -1; + } + + if (!FD_ISSET(m_fd, &readFds)) + return 0; + + ssize_t len = ::recv(m_fd, (char*)buffer, length, 0); + if (len == 0) { + return -2; + } else if (len < 0) { + fprintf(stderr, "Error returned from recv, err=%d\n", errno); + return -1; + } + + return len; +} + +int CTCPReaderWriterClient::readLine(std::string& line, unsigned int secs) +{ + //maybe there is a better way to do this like reading blocks, pushing them for later calls + //Nevermind, we'll read one char at a time for the time being. + unsigned char c; + int resultCode; + int len = 0; + line = ""; + + do + { + resultCode = read(&c, 1, secs); + if(resultCode == 1){ + line += c; + len++; + } + }while(c != '\n' && resultCode == 1); + + return resultCode <= 0 ? resultCode : len; +} + +bool CTCPReaderWriterClient::write(const unsigned char* buffer, unsigned int length) +{ + assert(buffer != NULL); + assert(length > 0U); + assert(m_fd != -1); + + ssize_t ret = ::send(m_fd, (char *)buffer, length, 0); + if (ret != ssize_t(length)) { + fprintf(stderr, "Error returned from send, err=%d\n", errno); + return false; + } + + return true; +} + +bool CTCPReaderWriterClient::writeLine(const std::string& line) +{ + std::string lineCopy(line); + if(lineCopy.size() > 0 && lineCopy.at(lineCopy.size() - 1) != '\n') + lineCopy.append("\n"); + + //stupidly write one char after the other + size_t len = lineCopy.size(); + bool result = true; + for(size_t i = 0; i < len && result; i++){ + unsigned char c = lineCopy.at(i); + result = write(&c , 1); + } + + return result; +} + +void CTCPReaderWriterClient::close() +{ + if (m_fd != -1) { + ::close(m_fd); + m_fd = -1; + } +} + +in_addr CTCPReaderWriterClient::lookup(const std::string &hostname) +{ + in_addr addr; + in_addr_t address = ::inet_addr(hostname.c_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + fprintf(stderr, "Cannot find address for host %s", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +} diff --git a/TCPReaderWriterClient.h b/TCPReaderWriterClient.h new file mode 100644 index 0000000..3470010 --- /dev/null +++ b/TCPReaderWriterClient.h @@ -0,0 +1,56 @@ +#pragma once + +/* end of inma once */ +/* + * Copyright (C) 2010,2011,2012,2013 by Jonathan Naylor G4KLX + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CTCPReaderWriterClient { +public: + CTCPReaderWriterClient(const std::string &address, unsigned int port, const std::string &localAddress = std::string("")); + CTCPReaderWriterClient(int fd); + CTCPReaderWriterClient(); + ~CTCPReaderWriterClient(); + + bool open(const std::string &address, unsigned int port, const std::string &localAddress = std::string("")); + bool open(); + + int read(unsigned char *buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U); + int readLine(std::string &line, unsigned int secs); + bool write(const unsigned char* buffer, unsigned int length); + bool writeLine(const std::string &line); + in_addr lookup(const std::string &hostname); + + void close(); + +private: + std::string m_address; + unsigned short m_port; + std::string m_localAddress; + int m_fd; +}; diff --git a/qn.dvap.cfg b/qn.dvap.cfg index 15514eb..92b466a 100644 --- a/qn.dvap.cfg +++ b/qn.dvap.cfg @@ -30,3 +30,15 @@ link = { # link to the reflector of your choice. the first character is the module you are linking. # link_at_start = "CREF001C" } + +dplus = { +# The following settings do not affect your ability to use dplus linking to XRF or XLX reflectors! +# You must be registered on the DPlus system, see www.dstargateway.org, otherwise authorization will fail, +# even if QnetLink reports a successful authorization. + +# authorize = false # uncomment and set to true if you want to use the closed-source DPlus reflectors and/or repeaters +# use_reflectors = true # uncomment and set to false if you are not going to link to DPlus reflectors +# use_repeaters = true # uncomment and set to false if you are not going to link to DPlus repeaters + +# any values specified in you gwys.txt file will override any reflectors or repeaters that DPlus authorization returns. +} diff --git a/qn.everything.cfg b/qn.everything.cfg index 280e2f8..5b2f8c6 100644 --- a/qn.everything.cfg +++ b/qn.everything.cfg @@ -281,6 +281,18 @@ link = { # max_dongles = 5 # maximum number of linked hot-spots } +dplus = { +# The following settings do not affect your ability to use dplus linking to XRF or XLX reflectors! +# You must be registered on the DPlus system, see www.dstargateway.org, otherwise authorization will fail, +# even if QnetLink reports a successful authorization. + +# authorize = false # uncomment and set to true if you want to use the closed-source DPlus reflectors and/or repeaters +# use_reflectors = true # uncomment and set to false if you are not going to link to DPlus reflectors +# use_repeaters = true # uncomment and set to false if you are not going to link to DPlus repeaters + +# any values specified in you gwys.txt file will override any reflectors or repeaters that DPlus authorization returns. +} + file = { # status = "/usr/local/etc/rptr_status" # where repeater status info is passed between services # DTMF = "/tmp" # diff --git a/qn.icom.cfg b/qn.icom.cfg index 7e32f61..2a64798 100644 --- a/qn.icom.cfg +++ b/qn.icom.cfg @@ -110,6 +110,18 @@ link = { # max_dongles = 5 # maximum number of linked hotspots } +dplus = { +# The following settings do not affect your ability to use dplus linking to XRF or XLX reflectors! +# You must be registered on the DPlus system, see www.dstargateway.org, otherwise authorization will fail, +# even if QnetLink reports a successful authorization. + +# authorize = false # uncomment and set to true if you want to use the closed-source DPlus reflectors and/or repeaters +# use_reflectors = true # uncomment and set to false if you are not going to link to DPlus reflectors +# use_repeaters = true # uncomment and set to false if you are not going to link to DPlus repeaters + +# any values specified in you gwys.txt file will override any reflectors or repeaters that DPlus authorization returns. +} + file = { # status = "/usr/local/etc/rptr_status" # where repeater status info is passed between services # DTMF = "/tmp" # diff --git a/qn.itap.cfg b/qn.itap.cfg index e991b63..584770e 100644 --- a/qn.itap.cfg +++ b/qn.itap.cfg @@ -30,3 +30,15 @@ link = { # link to the reflector of your choice. the first character is the module you are linking. # link_at_start = "CREF001C" } + +dplus = { +# The following settings do not affect your ability to use dplus linking to XRF or XLX reflectors! +# You must be registered on the DPlus system, see www.dstargateway.org, otherwise authorization will fail, +# even if QnetLink reports a successful authorization. + +# authorize = false # uncomment and set to true if you want to use the closed-source DPlus reflectors and/or repeaters +# use_reflectors = true # uncomment and set to false if you are not going to link to DPlus reflectors +# use_repeaters = true # uncomment and set to false if you are not going to link to DPlus repeaters + +# any values specified in you gwys.txt file will override any reflectors or repeaters that DPlus authorization returns. +} diff --git a/qn.mmdvm.cfg b/qn.mmdvm.cfg index 36bf9cc..97270cc 100644 --- a/qn.mmdvm.cfg +++ b/qn.mmdvm.cfg @@ -41,3 +41,15 @@ link = { admin = [ "AA0AAA" , "BB1BBB" , "CC3CCC" ] } + +dplus = { +# The following settings do not affect your ability to use dplus linking to XRF or XLX reflectors! +# You must be registered on the DPlus system, see www.dstargateway.org, otherwise authorization will fail, +# even if QnetLink reports a successful authorization. + +# authorize = false # uncomment and set to true if you want to use the closed-source DPlus reflectors and/or repeaters +# use_reflectors = true # uncomment and set to false if you are not going to link to DPlus reflectors +# use_repeaters = true # uncomment and set to false if you are not going to link to DPlus repeaters + +# any values specified in you gwys.txt file will override any reflectors or repeaters that DPlus authorization returns. +} diff --git a/versions.h b/versions.h index bad3b36..502fd8f 100644 --- a/versions.h +++ b/versions.h @@ -1,6 +1,6 @@ // version strings must be 55 characters or less! -#define IRCDDB_VERSION "QnetGateway-7.3.0" -#define LINK_VERSION "QnetLink-6.1.1" +#define IRCDDB_VERSION "QnetGateway-7.4.0" +#define LINK_VERSION "QnetLink-6.2.0" #define DVAP_VERSION "QnetDVAP-5.1.2" #define RELAY_VERSION "QnetRelay-0.2.3" #define ITAP_VERSION "QnetITAP-0.2.1"