From be7b5a0ea735c3827cd3f7b561676498e8af0098 Mon Sep 17 00:00:00 2001 From: Tom Early Date: Wed, 24 Apr 2019 08:58:17 -0700 Subject: [PATCH] dual IRC clients --- QnetGateway.cpp | 425 +++++++++++++++++++++++++++--------------------- QnetGateway.h | 24 +-- SockAddress.h | 2 + defaults | 11 +- qnconfig | 46 ++++-- 5 files changed, 293 insertions(+), 215 deletions(-) diff --git a/QnetGateway.cpp b/QnetGateway.cpp index 036412e..949828f 100644 --- a/QnetGateway.cpp +++ b/QnetGateway.cpp @@ -216,14 +216,18 @@ bool CQnetGateway::ReadConfig(char *cfgFile) printf("OWNER='%s'\n", OWNER.c_str()); OWNER.resize(CALL_SIZE, ' '); - if (cfg.GetValue(path+"host", estr, ircddb.ip, 3, MAXHOSTNAMELEN)) - return true; - - if (cfg.GetValue(path+"port", estr, ircddb.port, 1000, 65535)) - return true; - - if (cfg.GetValue(path+"password", estr, IRCDDB_PASSWORD, 0, 512)) + for (int i=0; i<2; i++) { + std::string p(path + std::to_string(i) + "_"); + if (cfg.KeyExists(p+"host")) { + cfg.GetValue(path+"host", estr, ircddb[i].ip, 3, MAXHOSTNAMELEN); + cfg.GetValue(path+"port", estr, ircddb[i].port, 1000, 65535); + cfg.GetValue(path+"password", estr, IRCDDB_PASSWORD[i], 0, 512); + } + } + if (0 == ircddb[0].ip.compare(ircddb[1].ip)) { + fprintf(stderr, "IRC networks must be different\n"); return true; + } // module for (int m=0; m<3; m++) { @@ -336,11 +340,11 @@ bool CQnetGateway::ReadConfig(char *cfgFile) } // Create ports -int CQnetGateway::open_port(const SPORTIP &pip) +int CQnetGateway::open_port(const SPORTIP &pip, int family) { - CSockAddress sin(af_family, pip.port, pip.ip.c_str()); + CSockAddress sin(family, pip.port, pip.ip.c_str()); - int sock = socket(af_family, SOCK_DGRAM, 0); + int sock = socket(family, SOCK_DGRAM, 0); if (0 > sock) { printf("Failed to create socket on %s:%d, errno=%d, %s\n", pip.ip.c_str(), pip.port, errno, strerror(errno)); return -1; @@ -363,7 +367,7 @@ int CQnetGateway::open_port(const SPORTIP &pip) } /* receive data from the irc server and save it */ -void CQnetGateway::GetIRCDataThread() +void CQnetGateway::GetIRCDataThread(int i) { std::string user, rptr, gateway, ipaddr; DSTAR_PROTOCOL proto; @@ -394,10 +398,10 @@ void CQnetGateway::GetIRCDataThread() bool not_announced[3]; for (int i=0; i<3; i++) not_announced[i] = this->rptr.mod[i].defined; // announce to all modules that are defined! - bool is_quadnet = (std::string::npos != ircddb.ip.find(".openquad.net")); + bool is_quadnet = (std::string::npos != ircddb[i].ip.find(".openquad.net")); bool doFind = true; while (keep_running) { - int rc = ii->getConnectionState(); + int rc = ii[i]->getConnectionState(); if (rc > 5 && rc < 8 && is_quadnet) { char ch = '\0'; if (not_announced[0]) @@ -425,7 +429,7 @@ void CQnetGateway::GetIRCDataThread() for (auto it=findRoute.begin(); it!=findRoute.end(); it++) { std::this_thread::sleep_for(std::chrono::milliseconds(800)); printf("\t'%s'\n", it->c_str()); - ii->findUser(*it); + ii[i]->findUser(*it); } doFind = false; } @@ -451,22 +455,22 @@ void CQnetGateway::GetIRCDataThread() threshold = 0; } - while (((type = ii->getMessageType()) != IDRT_NONE) && keep_running) { + while (((type = ii[i]->getMessageType()) != IDRT_NONE) && keep_running) { switch (type) { case IDRT_USER: - ii->receiveUser(user, rptr, gateway, ipaddr); + ii[i]->receiveUser(user, rptr, gateway, ipaddr); if (!user.empty()) { if (!rptr.empty() && !gateway.empty() && !ipaddr.empty()) { if (LOG_IRC) printf("C-u:%s,%s,%s,%s\n", user.c_str(), rptr.c_str(), gateway.c_str(), ipaddr.c_str()); - pthread_mutex_lock(&irc_data_mutex); + pthread_mutex_lock(&irc_data_mutex[i]); - user2rptr_map[user] = rptr; - rptr2gwy_map[rptr] = gateway; - gwy2ip_map[gateway] = ipaddr; + user2rptr_map[i][user] = rptr; + rptr2gwy_map[i][rptr] = gateway; + gwy2ip_map[i][gateway] = ipaddr; - pthread_mutex_unlock(&irc_data_mutex); + pthread_mutex_unlock(&irc_data_mutex[i]); // printf("%d users, %d repeaters, %d gateways\n", user2rptr_map.size(), rptr2gwy_map.size(), gwy2ip_map.size()); @@ -474,18 +478,18 @@ void CQnetGateway::GetIRCDataThread() } break; case IDRT_REPEATER: - ii->receiveRepeater(rptr, gateway, ipaddr, proto); + ii[i]->receiveRepeater(rptr, gateway, ipaddr, proto); if (!rptr.empty()) { if (!gateway.empty() && !ipaddr.empty()) { if (LOG_IRC) printf("C-r:%s,%s,%s\n", rptr.c_str(), gateway.c_str(), ipaddr.c_str()); - pthread_mutex_lock(&irc_data_mutex); + pthread_mutex_lock(&irc_data_mutex[i]); - rptr2gwy_map[rptr] = gateway; - gwy2ip_map[gateway] = ipaddr; + rptr2gwy_map[i][rptr] = gateway; + gwy2ip_map[i][gateway] = ipaddr; - pthread_mutex_unlock(&irc_data_mutex); + pthread_mutex_unlock(&irc_data_mutex[i]); // printf("%d repeaters, %d gateways\n", rptr2gwy_map.size(), gwy2ip_map.size()); @@ -493,34 +497,34 @@ void CQnetGateway::GetIRCDataThread() } break; case IDRT_GATEWAY: - ii->receiveGateway(gateway, ipaddr, proto); + ii[i]->receiveGateway(gateway, ipaddr, proto); if (!gateway.empty() && !ipaddr.empty()) { if (LOG_IRC) printf("C-g:%s,%s\n", gateway.c_str(),ipaddr.c_str()); - pthread_mutex_lock(&irc_data_mutex); + pthread_mutex_lock(&irc_data_mutex[i]); - gwy2ip_map[gateway] = ipaddr; + gwy2ip_map[i][gateway] = ipaddr; - pthread_mutex_unlock(&irc_data_mutex); + pthread_mutex_unlock(&irc_data_mutex[i]); // printf("%d gateways\n", gwy2ip_map.size()); } break; case IDRT_PING: - ii->receivePing(rptr); + ii[i]->receivePing(rptr); ReplaceChar(rptr, '_', ' '); if (! rptr.empty()) { - pthread_mutex_lock(&irc_data_mutex); - auto git = rptr2gwy_map.find(rptr); - if (rptr2gwy_map.end() != git) { + pthread_mutex_lock(&irc_data_mutex[i]); + auto git = rptr2gwy_map[i].find(rptr); + if (rptr2gwy_map[i].end() != git) { gateway = git->second; - auto ait = gwy2ip_map.find(gateway); - if (gwy2ip_map.end() != ait) { + auto ait = gwy2ip_map[i].find(gateway); + if (gwy2ip_map[i].end() != ait) { ipaddr = ait->second; - CSockAddress to(af_family, (unsigned short)g2_external.port, ipaddr.c_str()); - sendto(g2_sock, "PONG", 4, 0, to.GetPointer(), to.GetSize()); + CSockAddress to(af_family[i], (unsigned short)g2_external.port, ipaddr.c_str()); + sendto(g2_sock[i], "PONG", 4, 0, to.GetPointer(), to.GetSize()); if (LOG_QSO) printf("Sent 'PONG' to %s\n", ipaddr.c_str()); } else { @@ -529,7 +533,7 @@ void CQnetGateway::GetIRCDataThread() } else { printf("Can't respond to PING, repeater %s not in rptr2gwy_map\n", rptr.c_str()); } - pthread_mutex_unlock(&irc_data_mutex); + pthread_mutex_unlock(&irc_data_mutex[i]); } break; default: @@ -543,7 +547,7 @@ void CQnetGateway::GetIRCDataThread() } /* return codes: 0=OK(found it), 1=TRY AGAIN, 2=FAILED(bad data) */ -int CQnetGateway::get_yrcall_rptr_from_cache(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU) +int CQnetGateway::get_yrcall_rptr_from_cache(const int i, const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU) { std::string temp; @@ -553,8 +557,8 @@ int CQnetGateway::get_yrcall_rptr_from_cache(const std::string &call, std::strin /* find the user in the CACHE */ if (RoU == 'U') { - auto user_pos = user2rptr_map.find(call); - if (user_pos != user2rptr_map.end()) { + auto user_pos = user2rptr_map[i].find(call); + if (user_pos != user2rptr_map[i].end()) { arearp_cs = user_pos->second.substr(0, 7); *mod = user_pos->second.at(7); } else { @@ -578,12 +582,12 @@ int CQnetGateway::get_yrcall_rptr_from_cache(const std::string &call, std::strin temp.append(1, *mod); arearp_cs.resize(8, ' '); - auto rptr_pos = rptr2gwy_map.find(temp); - if (rptr_pos != rptr2gwy_map.end()) { + auto rptr_pos = rptr2gwy_map[i].find(temp); + if (rptr_pos != rptr2gwy_map[i].end()) { zonerp_cs.assign(rptr_pos->second); - auto gwy_pos = gwy2ip_map.find(zonerp_cs); - if (gwy_pos != gwy2ip_map.end()) { + auto gwy_pos = gwy2ip_map[i].find(zonerp_cs); + if (gwy_pos != gwy2ip_map[i].end()) { ip.assign(gwy_pos->second); return 0; } else { @@ -596,46 +600,55 @@ int CQnetGateway::get_yrcall_rptr_from_cache(const std::string &call, std::strin } } -bool CQnetGateway::get_yrcall_rptr(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU) +int CQnetGateway::get_yrcall_rptr(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU) +// returns zero if unsuccessful, otherwise returns ii index plus one { - pthread_mutex_lock(&irc_data_mutex); - int rc = get_yrcall_rptr_from_cache(call, arearp_cs, zonerp_cs, mod, ip, RoU); - pthread_mutex_unlock(&irc_data_mutex); - if (rc == 0) { - //printf("get_yrcall_rptr_from_cache: call='%s' arearp_cs='%s' zonerp_cs='%s', mod=%c ip='%s' RoU=%c\n", call.c_str(), arearp_cs.c_str(), zonerp_cs.c_str(), *mod, ip.c_str(), RoU); - return true; - } else if (rc == 2) - return false; - - /* at this point, the data is not in cache */ - /* report the irc status */ - int status = ii->getConnectionState(); - // printf("irc status=%d\n", status); - if (status != 7) { - printf("Remote irc database not ready, irc status is not 7, try again\n"); - return false; - } - - /* request data from irc server */ - if (RoU == 'U') { - printf("User [%s] not in local cache, try again\n", call.c_str()); - /*** YRCALL=KJ4NHFBL ***/ - if (((call.at(6) == 'A') || (call.at(6) == 'B') || (call.at(6) == 'C')) && (call.at(7) == 'L')) - printf("If this was a gateway link request, that is ok\n"); - - if (!ii->findUser(call)) { - printf("findUser(%s): Network error\n", call.c_str()); - return false; + for (int i=0; i<2; i++) { + if (NULL == ii[i]) + continue; + int rc; + if (ii[i]) { + pthread_mutex_lock(&irc_data_mutex[i]); + rc = get_yrcall_rptr_from_cache(i, call, arearp_cs, zonerp_cs, mod, ip, RoU); + pthread_mutex_unlock(&irc_data_mutex[i]); } - } else if (RoU == 'R') { - printf("Repeater [%s] not in local cache, try again\n", call.c_str()); - if (!ii->findRepeater(call)) { - printf("findRepeater(%s): Network error\n", call.c_str()); - return false; + if (rc == 0) { + //printf("get_yrcall_rptr_from_cache: call='%s' arearp_cs='%s' zonerp_cs='%s', mod=%c ip='%s' RoU=%c\n", call.c_str(), arearp_cs.c_str(), zonerp_cs.c_str(), *mod, ip.c_str(), RoU); + return i+1; + } else if (rc == 2) + return 0; + } + + for (int i=0; i<2; i++) { + if (NULL == ii[i]) + continue; + /* at this point, the data is not in cache */ + /* report the irc status */ + int status = ii[i]->getConnectionState(); + // printf("irc status=%d\n", status); + if (7 == status) { + /* request data from irc server */ + if (RoU == 'U') { + printf("User [%s] not in local cache, try again\n", call.c_str()); + /*** YRCALL=KJ4NHFBL ***/ + if (((call.at(6) == 'A') || (call.at(6) == 'B') || (call.at(6) == 'C')) && (call.at(7) == 'L')) + printf("If this was a gateway link request, that is ok\n"); + + if (!ii[i]->findUser(call)) { + printf("findUser(%s): Network error\n", call.c_str()); + return 0; + } + } else if (RoU == 'R') { + printf("Repeater [%s] not in local cache, try again\n", call.c_str()); + if (!ii[i]->findRepeater(call)) { + printf("findRepeater(%s): Network error\n", call.c_str()); + return 0; + } + } } } - return false; + return 0; } bool CQnetGateway::Flag_is_ok(unsigned char flag) @@ -746,7 +759,7 @@ void CQnetGateway::ProcessTimeouts() if ((t_now - to_remote_g2[i].last_time) > TIMING_TIMEOUT_LOCAL_RPTR) { printf("Inactivity from local rptr mod %c, removing stream id %04x\n", 'A'+i, ntohs(to_remote_g2[i].streamid)); - to_remote_g2[i].toDstar.Initialize(af_family); + to_remote_g2[i].toDstar.Initialize(AF_UNSPEC); to_remote_g2[i].streamid = 0; to_remote_g2[i].last_time = 0; } @@ -906,7 +919,10 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid) 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); + for (int x=0; x<2; x++) { + if (ii[x]) + ii[x]->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) { @@ -939,7 +955,10 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid) 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); + for (int x=0; x<2; x++) { + if (ii[x]) + ii[x]->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; } band_txt[i].txt_cnt = 0; @@ -1061,9 +1080,8 @@ void CQnetGateway::ProcessSlowData(unsigned char *data, unsigned short sid) } } -void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const bool is_from_g2) -// is_from_g2==true means it's coming from external port 40000 -// is_from_g2==false means it's coming from the link2gate Unix socket +void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const int source_sock) +// source_sock is the socket number of the incoming data, or -1 if it's a unix socket { static unsigned char lastctrl = 20U; static std::string superframe[3]; @@ -1079,7 +1097,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const b superframe[i].clear(); if (LOG_QSO) { printf("id=%04x flags=%02x:%02x:%02x ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s ", ntohs(g2buf.streamid), g2buf.hdr.flag[0], g2buf.hdr.flag[1], g2buf.hdr.flag[2], g2buf.hdr.urcall, g2buf.hdr.rpt1, g2buf.hdr.rpt2, g2buf.hdr.mycall, g2buf.hdr.sfx); - if (is_from_g2) + if (source_sock >= 0) printf("IP=[%s]:%u\n", fromDstar.GetAddress(), fromDstar.GetPort()); else printf("UnixSock=%s\n", link2gate.c_str()); @@ -1089,16 +1107,16 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const b lastctrl = 20U; /* save the header */ - if (! is_from_g2) { - if (AF_INET == af_family) { + if (source_sock < 0) { + //if (AF_INET == af_family) { char address[16]; snprintf(address, 16, "%d.0.0.0", i); fromDstar.Initialize(AF_INET, 40000U, address); - } else { - char address[8]; - snprintf(address, 8, "%d::", i); - fromDstar.Initialize(AF_INET6, 40000U, address); - } + //} else { + // char address[8]; + // snprintf(address, 8, "%d::", i); + // fromDstar.Initialize(AF_INET6, 40000U, address); + //} } memcpy(toRptr[i].saved_hdr.title, g2buf.title, 56); toRptr[i].saved_addr = fromDstar; @@ -1120,7 +1138,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const b if (rptr.mod[i].defined) { /* streamid match ? */ bool match = (toRptr[i].streamid == g2buf.streamid); - if (is_from_g2) + if (source_sock >= 0) match = match && (toRptr[i].addr == fromDstar); if (match) { if (LOG_DEBUG) { @@ -1208,7 +1226,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const b continue; /* match saved stream ? */ bool match = (toRptr[i].saved_hdr.streamid == g2buf.streamid); - if (is_from_g2) + if (source_sock >= 0) match = match && (toRptr[i].saved_addr == fromDstar); if (match) { /* repeater module is inactive ? */ @@ -1242,6 +1260,7 @@ void CQnetGateway::ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const b void CQnetGateway::ProcessModem() { + static int result = -1; char temp_mod; char temp_radio_user[9]; char tempfile[FILENAME_MAX]; @@ -1253,7 +1272,6 @@ void CQnetGateway::ProcessModem() if (0 == memcmp(dsvt.title, "DSVT", 4)) { if ( (recvlen==56 || recvlen==27) && dsvt.id==0x20U && (dsvt.config==0x10U || dsvt.config==0x20U) ) { - if (recvlen == 56) { vPacketCount = 0U; if (LOG_QSO) @@ -1364,13 +1382,13 @@ void CQnetGateway::ProcessModem() temp_radio_user[7] = 'A'; temp_radio_user[CALL_SIZE] = '\0'; - bool result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'R'); - if (result) { /* it is a repeater */ + result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'R'); + if (result--) { /* it is a repeater */ std::string from = OWNER.substr(0, 7); from.append(1, i+'A'); - ii->sendPing(temp_radio_user, from); + ii[result]->sendPing(temp_radio_user, from); to_remote_g2[i].streamid = dsvt.streamid; - to_remote_g2[i].toDstar.Initialize(af_family, (uint16_t)g2_external.port, ip.c_str()); + to_remote_g2[i].toDstar.Initialize(af_family[result], (uint16_t)g2_external.port, ip.c_str()); /* set rpt1 */ memset(dsvt.hdr.rpt1, ' ', 8); @@ -1393,7 +1411,7 @@ void CQnetGateway::ProcessModem() // send to remote gateway for (int j=0; j<5; j++) - sendto(g2_sock, dsvt.title, 56, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); + sendto(g2_sock[result], dsvt.title, 56, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); printf("id=%04x zone route to [%s]:%u ur=%.8s r1=%.8s r2=%.8s my=%.8s/%.4s\n", ntohs(dsvt.streamid), to_remote_g2[i].toDstar.GetAddress(), to_remote_g2[i].toDstar.GetPort(), @@ -1416,8 +1434,8 @@ void CQnetGateway::ProcessModem() memset(temp_radio_user, ' ', 8); memcpy(temp_radio_user, dsvt.hdr.urcall, 8); temp_radio_user[8] = '\0'; - bool result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'U'); - if (result) { + result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'U'); + if (result--) { /* destination is a remote system */ if (0 != zonerp_cs.compare(0, 7, OWNER, 0, 7)) { int i = dsvt.hdr.rpt1[7] - 'A'; @@ -1428,9 +1446,9 @@ void CQnetGateway::ProcessModem() /* set the destination */ std::string from = OWNER.substr(0, 7); from.append(1, i+'A'); - ii->sendPing(temp_radio_user, from); + ii[result]->sendPing(temp_radio_user, from); to_remote_g2[i].streamid = dsvt.streamid; - to_remote_g2[i].toDstar.Initialize(af_family, (uint16_t)g2_external.port, ip.c_str()); + to_remote_g2[i].toDstar.Initialize(af_family[result], (uint16_t)g2_external.port, ip.c_str()); /* set rpt1 */ memset(dsvt.hdr.rpt1, ' ', 8); @@ -1450,7 +1468,7 @@ void CQnetGateway::ProcessModem() /* send to remote gateway */ for (int j=0; j<5; j++) - sendto(g2_sock, dsvt.title, 56, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); + sendto(g2_sock[result], dsvt.title, 56, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); printf("Callsign route to [%s]:%u id=%04x my=%.8s/%.4s ur=%.8s rpt1=%.8s rpt2=%.8s\n", to_remote_g2[i].toDstar.GetAddress(), to_remote_g2[i].toDstar.GetPort(), ntohs(dsvt.streamid), dsvt.hdr.mycall, dsvt.hdr.sfx, dsvt.hdr.urcall, dsvt.hdr.rpt1, dsvt.hdr.rpt2); @@ -1690,11 +1708,13 @@ void CQnetGateway::ProcessModem() 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); + if (result >= 0) + ii[result]->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); + if (result >= 0) + ii[result]->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); if (playNotInCache) { // Not in cache, please try again! @@ -1770,14 +1790,14 @@ void CQnetGateway::ProcessModem() for (int i=0; i<3; i++) { /* find out if data must go to the remote G2 */ - if (to_remote_g2[i].streamid == dsvt.streamid) { - sendto(g2_sock, dsvt.title, 27, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); + if (to_remote_g2[i].streamid==dsvt.streamid && result>=0) { + sendto(g2_sock[result], dsvt.title, 27, 0, to_remote_g2[i].toDstar.GetPointer(), to_remote_g2[i].toDstar.GetSize()); time(&(to_remote_g2[i].last_time)); /* Is this the end-of-stream */ if (dsvt.ctrl & 0x40) { - to_remote_g2[i].toDstar.Initialize(af_family); + to_remote_g2[i].toDstar.Initialize(AF_UNSPEC); to_remote_g2[i].streamid = 0; to_remote_g2[i].last_time = 0; } @@ -1833,7 +1853,7 @@ void CQnetGateway::ProcessModem() if (dsvt.ctrl & 0x40) { toRptr[i].last_time = 0; toRptr[i].streamid = 0; - toRptr[i].addr.Initialize(af_family); + toRptr[i].addr.Initialize(AF_UNSPEC); } break; } @@ -1866,7 +1886,7 @@ void CQnetGateway::Process() dstar_dv_init(); - std::future aprs_future, irc_data_future; + std::future aprs_future, irc_data_future[2]; if (APRS_ENABLE) { // start the beacon thread try { aprs_future = std::async(std::launch::async, &CQnetGateway::APRSBeaconThread, this); @@ -1877,16 +1897,20 @@ void CQnetGateway::Process() printf("APRS beacon thread started\n"); } - try { // start the IRC read thread - irc_data_future = std::async(std::launch::async, &CQnetGateway::GetIRCDataThread, this); - } catch (const std::exception &e) { - printf("Failed to start GetIRCDataThread. Exception: %s\n", e.what()); - keep_running = false; - } - if (keep_running) - printf("get_irc_data thread started\n"); + for (int i=0; i<2; i++) { + if (ii[i]) { + try { // start the IRC read thread + irc_data_future[i] = std::async(std::launch::async, &CQnetGateway::GetIRCDataThread, this, i); + } catch (const std::exception &e) { + printf("Failed to start GetIRCDataThread[%d]. Exception: %s\n", i, e.what()); + keep_running = false; + } + if (keep_running) + printf("get_irc_data thread[%d] started\n", i); - ii->kickWatchdog(IRCDDB_VERSION); + ii[i]->kickWatchdog(IRCDDB_VERSION); + } + } while (keep_running) { ProcessTimeouts(); @@ -1895,7 +1919,10 @@ void CQnetGateway::Process() int max_nfds = 0; fd_set fdset; FD_ZERO(&fdset); - AddFDSet(max_nfds, g2_sock, &fdset); + if (g2_sock[0] >= 0) + AddFDSet(max_nfds, g2_sock[0], &fdset); + if (g2_sock[1] >= 0) + AddFDSet(max_nfds, g2_sock[1], &fdset); AddFDSet(max_nfds, Link2Gate.GetFD(), &fdset); AddFDSet(max_nfds, Modem2Gate.GetFD(), &fdset); struct timeval tv; @@ -1904,15 +1931,19 @@ void CQnetGateway::Process() (void)select(max_nfds + 1, &fdset, 0, 0, &tv); // process packets coming from remote G2 - if (keep_running && FD_ISSET(g2_sock, &fdset)) { - SDSVT dsvt; - socklen_t fromlen = sizeof(struct sockaddr_storage); - ssize_t g2buflen = recvfrom(g2_sock, dsvt.title, 56, 0, fromDstar.GetPointer(), &fromlen); - if (LOG_QSO && 4==g2buflen && 0==memcmp(dsvt.title, "PONG", 4)) - printf("Got a pong from [%s]:%u\n", fromDstar.GetAddress(), fromDstar.GetPort()); - else - ProcessG2(g2buflen, dsvt, true); - FD_CLR(g2_sock, &fdset); + for (int i=0; i<2; i++) { + if (g2_sock[i] < 0) + continue; + if (keep_running && FD_ISSET(g2_sock[i], &fdset)) { + SDSVT dsvt; + socklen_t fromlen = sizeof(struct sockaddr_storage); + ssize_t g2buflen = recvfrom(g2_sock[i], dsvt.title, 56, 0, fromDstar.GetPointer(), &fromlen); + if (LOG_QSO && 4==g2buflen && 0==memcmp(dsvt.title, "PONG", 4)) + printf("Got a pong from [%s]:%u\n", fromDstar.GetAddress(), fromDstar.GetPort()); + else + ProcessG2(g2buflen, dsvt, true); + FD_CLR(g2_sock[i], &fdset); + } } // process packets from qnlink @@ -1935,7 +1966,10 @@ void CQnetGateway::Process() if (aprs_future.valid()) aprs_future.get(); } - irc_data_future.get(); + for (int i=0; i<2; i++) { + if (ii[i]) + irc_data_future[i].get(); + } return; } @@ -2285,10 +2319,14 @@ void CQnetGateway::qrgs_and_maps() std::string rptrcall = OWNER; rptrcall.resize(CALL_SIZE-1); rptrcall += i + 'A'; - if (rptr.mod[i].latitude || rptr.mod[i].longitude || rptr.mod[i].desc1.length() || rptr.mod[i].url.length()) - ii->rptrQTH(rptrcall, rptr.mod[i].latitude, rptr.mod[i].longitude, rptr.mod[i].desc1, rptr.mod[i].desc2, rptr.mod[i].url, rptr.mod[i].package_version); - if (rptr.mod[i].frequency) - ii->rptrQRG(rptrcall, rptr.mod[i].frequency, rptr.mod[i].offset, rptr.mod[i].range, rptr.mod[i].agl); + for (int j=0; j<2; j++) { + if (ii[j]) { + if (rptr.mod[i].latitude || rptr.mod[i].longitude || rptr.mod[i].desc1.length() || rptr.mod[i].url.length()) + ii[j]->rptrQTH(rptrcall, rptr.mod[i].latitude, rptr.mod[i].longitude, rptr.mod[i].desc1, rptr.mod[i].desc2, rptr.mod[i].url, rptr.mod[i].package_version); + if (rptr.mod[i].frequency) + ii[j]->rptrQRG(rptrcall, rptr.mod[i].frequency, rptr.mod[i].offset, rptr.mod[i].range, rptr.mod[i].agl); + } + } } return; @@ -2396,52 +2434,70 @@ bool CQnetGateway::Init(char *cfgfile) } compute_aprs_hash(); - ii = new CIRCDDB(ircddb.ip, ircddb.port, owner, IRCDDB_PASSWORD, IRCDDB_VERSION.c_str()); - bool ok = ii->open(); - if (!ok) { - printf("irc open failed\n"); - return true; - } + for (int j=0; j<2; j++) { + if (ircddb[j].ip.empty()) + continue; + ii[j] = new CIRCDDB(ircddb[j].ip, ircddb[j].port, owner, IRCDDB_PASSWORD[j], IRCDDB_VERSION.c_str()); + bool ok = ii[j]->open(); + if (!ok) { + printf("%s open failed\n", ircddb[j].ip.c_str()); + return true; + } - int rc = ii->getConnectionState(); - printf("Waiting for irc connection status of 2\n"); - i = 0; - while (rc < 2) { - printf("irc status=%d\n", rc); - if (rc < 2) { - i++; - sleep(5); - } else - break; + int rc = ii[j]->getConnectionState(); + printf("Waiting for %s connection status of 2\n", ircddb[j].ip.c_str()); + i = 0; + while (rc < 2) { + printf("%s status=%d\n", ircddb[j].ip.c_str(), rc); + if (rc < 2) { + i++; + sleep(5); + } else + break; - if (!keep_running) - break; + if (!keep_running) + break; - if (i > 5) { - printf("We can not wait any longer...\n"); - break; + if (i > 5) { + printf("We can not wait any longer for %s...\n", ircddb[j].ip.c_str()); + break; + } + rc = ii[j]->getConnectionState(); } - rc = ii->getConnectionState(); - } - switch (ii->GetFamily()) { + switch (ii[j]->GetFamily()) { case AF_INET: printf("IRC server is using IPV4\n"); - af_family = AF_INET; + af_family[j] = AF_INET; break; case AF_INET6: printf("IRC server is using IPV6\n"); - af_family = AF_INET6; + af_family[j] = AF_INET6; break; default: - printf("IRC server is using unknown protocol! Shutting down...\n"); + printf("%s server is using unknown protocol! Shutting down...\n", ircddb[j].ip.c_str()); return true; + } } - - /* udp port 40000 must open first */ - g2_sock = open_port(g2_external); - if (0 > g2_sock) { - printf("Can't open %s:%d\n", g2_external.ip.c_str(), g2_external.port); - return true; + /* udp port 40000 must open first */ + if (ii[0]) { + g2_sock[0] = open_port(g2_external, af_family[0]); + if (0 > g2_sock[0]) { + printf("Can't open %s:%d for %s\n", g2_external.ip.c_str(), g2_external.port, ircddb[i].ip.c_str()); + return true; + } + if (ii[1] && (af_family[0] != af_family[1])) { // we only need to open a second port if the family for the irc server is different! + g2_sock[1] = open_port(g2_external, af_family[1]); + if (0 > g2_sock[1]) { + printf("Can't open %s:%d for %s\n", g2_external.ip.c_str(), g2_external.port, ircddb[1].ip.c_str()); + return true; + } + } + } else if (ii[1]) { + g2_sock[1] = open_port(g2_external, af_family[1]); + if (0 > g2_sock[1]) { + printf("Can't open %s:%d for %s\n", g2_external.ip.c_str(), g2_external.port, ircddb[1].ip.c_str()); + return true; + } } // Open unix sockets between qngateway and qnlink @@ -2476,10 +2532,10 @@ bool CQnetGateway::Init(char *cfgfile) // the repeater modules run on these ports memset(toRptr[i].saved_hdr.title, 0, 56); - toRptr[i].saved_addr.Initialize(af_family); + toRptr[i].saved_addr.Initialize(AF_UNSPEC); toRptr[i].streamid = 0; - toRptr[i].addr.Initialize(af_family); + toRptr[i].addr.Initialize(AF_UNSPEC); toRptr[i].last_time = 0; @@ -2496,7 +2552,7 @@ bool CQnetGateway::Init(char *cfgfile) /* to remote systems */ for (i = 0; i < 3; i++) { - to_remote_g2[i].toDstar.Initialize(af_family); + to_remote_g2[i].toDstar.Initialize(AF_UNSPEC); to_remote_g2[i].streamid = 0; to_remote_g2[i].last_time = 0; } @@ -2510,7 +2566,7 @@ bool CQnetGateway::Init(char *cfgfile) CQnetGateway::CQnetGateway() { - ii = NULL; + ii[0] = ii[1] = NULL; } CQnetGateway::~CQnetGateway() @@ -2518,11 +2574,6 @@ CQnetGateway::~CQnetGateway() Link2Gate.Close(); Modem2Gate.Close(); - if (g2_sock != -1) { - close(g2_sock); - printf("Closed G2_EXTERNAL_PORT\n"); - } - if (APRS_ENABLE) { if (aprs->aprs_sock.GetFD() != -1) { aprs->aprs_sock.Close(); @@ -2540,9 +2591,15 @@ CQnetGateway::~CQnetGateway() } } - if (ii) { - ii->close(); - delete ii; + for (int i=0; i<2; i++) { + if (g2_sock[i] >= 0) { + close(g2_sock[i]); + printf("Closed G2_EXTERNAL_PORT %d\n", i); + } + if (ii[i]) { + ii[i]->close(); + delete ii[i]; + } } printf("QnetGateway exiting\n"); diff --git a/QnetGateway.h b/QnetGateway.h index 2bd531f..7ab8a1b 100644 --- a/QnetGateway.h +++ b/QnetGateway.h @@ -83,7 +83,7 @@ public: private: // network type - int af_family; + int af_family[2] = { AF_UNSPEC, AF_UNSPEC }; // text stuff bool new_group[3] = { true, true, true }; unsigned char header_type = 0; @@ -91,14 +91,14 @@ private: bool ABC_grp[3] = { false, false, false }; bool C_seen[3] = { false, false, false }; - SPORTIP g2_external, ircddb; + SPORTIP g2_external, ircddb[2]; CUnixDgramReader Link2Gate, Modem2Gate; CUnixDgramWriter Gate2Link, Gate2Modem[3]; std::string gate2link, link2gate, gate2modem[3], modem2gate; - std::string OWNER, owner, FILE_STATUS, FILE_DTMF, FILE_ECHOTEST, IRCDDB_PASSWORD, FILE_QNVOICE_FILE; + std::string OWNER, owner, FILE_STATUS, FILE_DTMF, FILE_ECHOTEST, IRCDDB_PASSWORD[2], FILE_QNVOICE_FILE; bool GATEWAY_SEND_QRGS_MAP, GATEWAY_HEADER_REGEN, APRS_ENABLE, playNotInCache; bool LOG_DEBUG, LOG_IRC, LOG_DTMF, LOG_QSO; @@ -122,7 +122,7 @@ private: STOREMOTEG2 to_remote_g2[3]; // 0=A, 1=B, 2=C // input from remote G2 gateway - int g2_sock = -1; + int g2_sock[2] = { -1, -1 }; CSockAddress fromDstar; // Incoming data from remote systems @@ -135,7 +135,7 @@ private: struct sockaddr_in plug; // for talking with the irc server - CIRCDDB *ii; + CIRCDDB *ii[2]; // for handling APRS stuff CAPRS *aprs; @@ -148,9 +148,9 @@ private: // CACHE used to cache users, repeaters, // gateways, IP numbers coming from the irc server - std::map user2rptr_map, rptr2gwy_map, gwy2ip_map; + std::map user2rptr_map[2], rptr2gwy_map[2], gwy2ip_map[2]; - pthread_mutex_t irc_data_mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_t irc_data_mutex[2] = PTHREAD_MUTEX_INITIALIZER; // dtmf stuff int dtmf_buf_count[3]; @@ -160,17 +160,17 @@ private: bool VoicePacketIsSync(const unsigned char *text); void AddFDSet(int &max, int newfd, fd_set *set); - int open_port(const SPORTIP &pip); + int open_port(const SPORTIP &pip, int family); void calcPFCS(unsigned char *packet, int len); - void GetIRCDataThread(); - int get_yrcall_rptr_from_cache(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU); - bool get_yrcall_rptr(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU); + void GetIRCDataThread(int i); + int get_yrcall_rptr_from_cache(const int i, const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU); + int get_yrcall_rptr(const std::string &call, std::string &arearp_cs, std::string &zonerp_cs, char *mod, std::string &ip, char RoU); void PlayFileThread(SECHO &edata); void compute_aprs_hash(); void APRSBeaconThread(); void ProcessTimeouts(); void ProcessSlowData(unsigned char *data, unsigned short sid); - void ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const bool is_from_g2); + void ProcessG2(const ssize_t g2buflen, const SDSVT &g2buf, const int sock_source); void ProcessModem(); bool Flag_is_ok(unsigned char flag); void UnpackCallsigns(const std::string &str, std::set &set, const std::string &delimiters = ","); diff --git a/SockAddress.h b/SockAddress.h index 59d2aad..d1b9ff7 100644 --- a/SockAddress.h +++ b/SockAddress.h @@ -63,6 +63,8 @@ public: inet_pton(AF_INET6, "::", &(addr6->sin6_addr)); else inet_pton(AF_INET6, address, &(addr6->sin6_addr)); + } else if (AF_UNSPEC == family) { + memset(&addr, 0, sizeof(struct sockaddr_storage)); } } diff --git a/defaults b/defaults index cc78ece..4ad1a4f 100644 --- a/defaults +++ b/defaults @@ -33,10 +33,13 @@ # # IRCDDB - You MUST use a legal callsign for logging into any IRC network # -#ircddb_login_d='' # login callsign for the ircDDB network -ircddb_host_d='rr.openquad.net' # other irc networks include group1-irc.ircddb.net -ircddb_port_d=9007 # not a good idea to change! -ircddb_password_d='1111111111111' # not needed for rr.openquad.net +#ircddb_login_d='' # login callsign for the ircDDB network +ircddb0_host_d='rr.openquad.net' # other irc networks include group1-irc.ircddb.net +ircddb0_port_d=9007 # not a good idea to change! +ircddb0_password_d='' # not needed for rr.openquad.net +ircddb1_host_d='' # second irc network +ircddb1_port_d=9007 # not a good idea to change! +ircddb1_password_d='' # not needed for rr.openquad.net ########################################################################################################################## # diff --git a/qnconfig b/qnconfig index 4b49206..f846611 100755 --- a/qnconfig +++ b/qnconfig @@ -210,22 +210,35 @@ IrcddbMenu () { echo echo " IRCDDB Menu" echo + echo "Routes to other clients using IRC Server A will be searched first" + echo echo "l : Login - must be a legal callsign = '${ircddb_login}'" - echo -n "h : Hostname for IRC Server = "; EvaluateVar ircddb_host{,_d} + echo -n "ha : Hostname for IRC Server A = "; EvaluateVar ircddb0_host{,_d} + if [ -n "$em" ]; then + echo -n "oa : IRC TCP port A = "; EvaluateVar ircddb0_port{,_d} + echo -n "aa : IRC Password A = "; EvaluateVar ircddb0_password{,_d} + fi + echo -n "hb : Hostname for IRC Server B = "; EvaluateVar ircddb0_host{,_d} if [ -n "$em" ]; then - echo -n "po : IRC TCP port = "; EvaluateVar ircddb_port{,_d} - echo -n "pa : IRC Password = "; EvaluateVar ircddb_password{,_d} + echo -n "ob : IRC TCP port B = "; EvaluateVar ircddb0_port{,_d} + echo -n "ab : IRC Password B = "; EvaluateVar ircddb0_password{,_d} fi EndMenu if [[ "$key" == l* ]]; then ircddb_login="${value^^}" - elif [[ "$key" == h* ]]; then ircddb_host="$value" - elif [[ "$key" == po* ]]; then ircddb_port="$value" - elif [[ "$key" == pa* ]]; then ircddb_password="$value" + elif [[ "$key" == ha* ]]; then ircddb0_host="$value" + elif [[ "$key" == oa* ]]; then ircddb0_port="$value" + elif [[ "$key" == aa* ]]; then ircddb0_password="$value" + elif [[ "$key" == hb* ]]; then ircddb1_host="$value" + elif [[ "$key" == ob* ]]; then ircddb1_port="$value" + elif [[ "$key" == ab* ]]; then ircddb1_password="$value" elif [[ "$key" == u* ]]; then - if [[ "$value" == h* ]]; then unset ircddb_host - elif [[ "$value" == po* ]]; then unset ircddb_port - elif [[ "$value" == pa* ]]; then unset ircddb_password + if [[ "$value" == ha* ]]; then unset ircddb0_host + elif [[ "$value" == oa* ]]; then unset ircddb0_port + elif [[ "$value" == aa* ]]; then unset ircddb0_password + elif [[ "$value" == hb* ]]; then unset ircddb1_host + elif [[ "$value" == ob* ]]; then unset ircddb1_port + elif [[ "$value" == ab* ]]; then unset ircddb1_password fi fi done @@ -499,11 +512,14 @@ WriteCFGFile () { fi outFile='./qn.cfg' echo "# Created on `date`" > $outFile - # gateway_ section - echo "ircddb_login='$ircddb_login'" >> $outFile - [ -z "${ircddb_host+x}" ] || echo "ircddb_host='${ircddb_host}'" >> $outFile - [ -z "${ircddb_port+x}" ] || echo "ircddb_host=${ircddb_port}" >> $outFile - [ -z "${ircddb_password+x}" ] || echo "ircddb_password='${ircddb_password}'" >> $outFile + # ircddb_ section + echo "ircddb_login='$ircddb_login'" >> $outFile + [ -z "${ircddb0_host+x}" ] || echo "ircddb0_host='${ircddb0_host}'" >> $outFile + [ -z "${ircddb0_port+x}" ] || echo "ircddb0_host=${ircddb0_port}" >> $outFile + [ -z "${ircddb0_password+x}" ] || echo "ircddb0_password='${ircddb0_password}'" >> $outFile + [ -z "${ircddb1_host+x}" ] || echo "ircddb1_host='${ircddb1_host}'" >> $outFile + [ -z "${ircddb1_port+x}" ] || echo "ircddb1_host=${ircddb1_port}" >> $outFile + [ -z "${ircddb1_password+x}" ] || echo "ircddb1_password='${ircddb1_password}'" >> $outFile # module_?_ section for m in a b c @@ -653,7 +669,7 @@ while [[ "$ans" != q* ]] do clear echo - echo " Main Menu" + echo " QnConfig Main Menu V#190424" echo echo -n "a : Module A - "; if [ -z $module_a ]; then echo ""; else echo "${module_a^^}"; fi echo -n "b : Module B - "; if [ -z $module_b ]; then echo ""; else echo "${module_b^^}"; fi