void C4Network2Client::AddAddrFromPuncher(const C4NetIO::addr_t &addr) { AddAddr(C4Network2Address(addr, P_UDP), true); // If the outside port matches the inside port, there is no port translation and the // TCP address will probably work as well. if (addr.GetPort() == Config.Network.PortUDP && Config.Network.PortTCP > 0) { auto tcpAddr = addr; tcpAddr.SetPort(Config.Network.PortTCP); AddAddr(C4Network2Address(tcpAddr, P_TCP), true); } // Save IPv6 address for TCP simultaneous connect. if (addr.GetFamily() == C4NetIO::addr_t::IPv6) IPv6AddrFromPuncher = addr; }
void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) { // set up address struct sockaddr_in addr; ZeroMem(&addr, sizeof addr); addr.sin_family = AF_INET; // get local address(es) in_addr **ppAddr = NULL; #ifdef HAVE_WINSOCK bool fGotWinSock = AcquireWinSock(); if (fGotWinSock) #endif { // get local host name char szLocalHostName[128 + 1]; *szLocalHostName = '\0'; ::gethostname(szLocalHostName, 128); // get hostent-struct hostent *ph = ::gethostbyname(szLocalHostName); // check type, get addr list if (ph) if (ph->h_addrtype != AF_INET) ph = NULL; else ppAddr = reinterpret_cast<in_addr **>(ph->h_addr_list); } // add address(es) for (;;) { if (iPortTCP >= 0) { addr.sin_port = htons(iPortTCP); AddAddr(C4Network2Address(addr, P_TCP), false); } if (iPortUDP >= 0) { addr.sin_port = htons(iPortUDP); AddAddr(C4Network2Address(addr, P_UDP), false); } // get next if (!ppAddr || !*ppAddr) break; addr.sin_addr = **ppAddr++; } #ifdef HAVE_WINSOCK if (fGotWinSock) ReleaseWinSock(); #endif }
void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) { C4NetIO::addr_t addr; for (auto& ha : C4NetIO::GetLocalAddresses()) { addr.SetAddress(ha); if (iPortTCP) { addr.SetPort(iPortTCP); AddAddr(C4Network2Address(addr, P_TCP), false); } if (iPortUDP) { addr.SetPort(iPortUDP); AddAddr(C4Network2Address(addr, P_UDP), false); } if (addr.GetScopeId()) InterfaceIDs.insert(addr.GetScopeId()); } }
bool C4Network2Client::DoConnectAttempt(C4Network2IO *pIO) { // local? if (isLocal()) { iNextConnAttempt = 0; return true; } // msg and data connected? Nothing to do if (getMsgConn() != getDataConn()) { iNextConnAttempt = time(nullptr) + 10; return true; } // too early? if (iNextConnAttempt && iNextConnAttempt > time(nullptr)) return true; // find address to try int32_t iBestAddress = -1; for (int32_t i = 0; i < iAddrCnt; i++) // no connection for this protocol? if ((!pDataConn || Addr[i].getProtocol() != pDataConn->getProtocol()) && (!pMsgConn || Addr[i].getProtocol() != pMsgConn->getProtocol())) // protocol available? if (pIO->getNetIO(Addr[i].getProtocol())) // new best address? if (iBestAddress < 0 || AddrAttempts[i] < AddrAttempts[iBestAddress]) iBestAddress = i; // too many attempts or nothing found? if (iBestAddress < 0 || AddrAttempts[iBestAddress] > C4NetClientConnectAttempts) { iNextConnAttempt = time(nullptr) + 10; return true; } // save attempt AddrAttempts[iBestAddress]++; iNextConnAttempt = time(nullptr) + C4NetClientConnectInterval; auto addr = Addr[iBestAddress].getAddr(); // try TCP simultaneous open if the stars align right if (addr.GetFamily() == C4NetIO::addr_t::IPv6 && // address needs to be IPv6... !addr.IsLocal() && !addr.IsPrivate() && // ...global unicast... Addr[iBestAddress].getProtocol() == P_TCP && // ...TCP, !TcpSimOpenSocket && // there is no previous request, pParent->GetLocal()->getID() < getID()) // and make sure that only one client per pair initiates a request. { DoTCPSimultaneousOpen(pIO, C4Network2Address()); } std::set<int> interfaceIDs; if (addr.IsLocal()) interfaceIDs = Network.Clients.GetLocal()->getInterfaceIDs(); else interfaceIDs = {0}; for (auto id : interfaceIDs) { addr.SetScopeId(id); // log LogSilentF("Network: connecting client %s on %s...", getName(), addr.ToString().getData()); // connect if (pIO->Connect(addr, Addr[iBestAddress].getProtocol(), pClient->getCore())) return true; } return false; }
void C4Network2IO::OnPunch(C4NetIO::addr_t addr) { // Sanity check assert (addr.sin_family == AF_INET); if (addr.sin_family != AF_INET) return; ZeroMem(addr.sin_zero, sizeof(addr.sin_zero)); // Add for local client C4Network2Client *pLocal = ::Network.Clients.GetLocal(); if (pLocal) if (pLocal->AddAddr(C4Network2Address(addr, P_UDP), true)) ::Network.InvalidateReference(); }
void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) { // set up address struct sockaddr_in addr; ZeroMem(&addr, sizeof addr); addr.sin_family = AF_INET; // get local address(es) in_addr **ppAddr = NULL; #ifdef HAVE_WINSOCK bool fGotWinSock = AcquireWinSock(); if (fGotWinSock) { // get local host name char szLocalHostName[128+1]; *szLocalHostName = '\0'; ::gethostname(szLocalHostName, 128); // get hostent-struct hostent *ph = ::gethostbyname(szLocalHostName); // check type, get addr list if (ph) { if (ph->h_addrtype != AF_INET) ph = NULL; else ppAddr = reinterpret_cast<in_addr **>(ph->h_addr_list); } } #else std::vector<in_addr*> addr_vec; struct ifaddrs* addrs; getifaddrs(&addrs); for(struct ifaddrs* addr = addrs; addr != NULL; addr = addr->ifa_next) { struct sockaddr* ad = addr->ifa_addr; if(ad == NULL) continue; if(ad->sa_family == AF_INET && (~addr->ifa_flags & IFF_LOOPBACK)) // Choose only non-loopback IPv4 devices addr_vec.push_back(&reinterpret_cast<sockaddr_in*>(ad)->sin_addr); } addr_vec.push_back(NULL); ppAddr = &addr_vec[0]; #endif // add address(es) for (;;) { if (iPortTCP >= 0) { addr.sin_port = htons(iPortTCP); AddAddr(C4Network2Address(addr, P_TCP), false); } if (iPortUDP >= 0) { addr.sin_port = htons(iPortUDP); AddAddr(C4Network2Address(addr, P_UDP), false); } // get next if (!ppAddr || !*ppAddr) break; addr.sin_addr = **ppAddr++; } #ifdef HAVE_WINSOCK if (fGotWinSock) ReleaseWinSock(); #else if(addrs) freeifaddrs(addrs); #endif }
bool C4Network2Client::DoTCPSimultaneousOpen(class C4Network2IO *pIO, const C4Network2Address &addr) { if (!pIO->getNetIO(P_TCP)) return false; // Did we already bind a socket? if (TcpSimOpenSocket) { LogSilentF("Network: connecting client %s on %s with TCP simultaneous open...", getName(), addr.getAddr().ToString().getData()); return pIO->ConnectWithSocket(addr.getAddr(), addr.getProtocol(), pClient->getCore(), std::move(TcpSimOpenSocket)); } else { // No - bind one, inform peer, and schedule a connection attempt. auto NetIOTCP = dynamic_cast<C4NetIOTCP*>(pIO->getNetIO(P_TCP)); auto bindAddr = pParent->GetLocal()->IPv6AddrFromPuncher; // We need to know an address that works. if (bindAddr.IsNull()) return false; bindAddr.SetPort(0); TcpSimOpenSocket = NetIOTCP->Bind(bindAddr); auto boundAddr = TcpSimOpenSocket->GetAddress(); LogSilentF("Network: %s TCP simultaneous open request for client %s from %s...", addr.isIPNull() ? "initiating" : "responding to", getName(), boundAddr.ToString().getData()); // Send address we bound to to the client. if (!SendMsg(MkC4NetIOPacket(PID_TCPSimOpen, C4PacketTCPSimOpen(pParent->GetLocal()->getID(), C4Network2Address(boundAddr, P_TCP))))) return false; if (!addr.isIPNull()) { // We need to delay the connection attempt a bit. Unfortunately, // waiting for the next tick would usually take way too much time. // Instead, we block the main thread for a very short time and hope // that noone notices... int ping = getMsgConn()->getLag(); std::this_thread::sleep_for(std::chrono::milliseconds(std::min(ping / 2, 10))); DoTCPSimultaneousOpen(pIO, addr); } return true; } }