bool CNetwork::RoutePacket(QUuid& pTargetGUID, G2Packet* pPacket)
{
	CG2Node* pNode = 0;
	CEndPoint pAddr;

	if(m_oRoutingTable.Find(pTargetGUID, &pNode, &pAddr))
	{
		if(pNode)
		{
			pNode->SendPacket(pPacket, true, false);
			systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 routed to neighbour: %3").arg(pTargetGUID.toString()).arg(pPacket->GetType()).arg(pNode->m_oAddress.toString().toAscii().constData()));
			//qDebug() << "CNetwork::RoutePacket " << pTargetGUID.toString() << " Packet: " << pPacket->GetType() << " routed to neighbour: " << pNode->m_oAddress.toString().toAscii().constData();
			return true;
		}
		else if(!pAddr.isNull())
		{
			Datagrams.SendPacket(pAddr, pPacket, true);
			systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 routed to remote node: %3").arg(pTargetGUID.toString()).arg(pPacket->GetType()).arg(pNode->m_oAddress.toString().toAscii().constData()));
			//qDebug() << "CNetwork::RoutePacket " << pTargetGUID.toString() << " Packet: " << pPacket->GetType() << " routed to remote node: " << pNode->m_oAddress.toString().toAscii().constData();
			return true;
		}
		systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket - No node and no address!"));
		//qDebug() << "CNetwork::RoutePacket - weird thing, should not happen...";
	}

	systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 DROPED!").arg(pTargetGUID.toString()).arg(pPacket->GetType()));
	//qDebug() << "CNetwork::RoutePacket " << pTargetGUID.toString() << " Packet: " << pPacket->GetType() << " DROPPED!";
	return false;
}
bool CNetwork::RoutePacket(G2Packet* pPacket, CG2Node* pNbr)
{
	QUuid pGUID;

	if(pPacket->GetTo(pGUID) && pGUID != quazaaSettings.Profile.GUID)   // no i adres != moj adres
	{
		CG2Node* pNode = 0;
		CEndPoint pAddr;

		if(m_oRoutingTable.Find(pGUID, &pNode, &pAddr))
		{
			bool bForwardTCP = false;
			bool bForwardUDP = false;

			if(pNbr)
			{
				if(pNbr->m_nType == G2_LEAF)    // if received from leaf - can forward anywhere
				{
					bForwardTCP = bForwardUDP = true;
				}
				else    // if received from a hub - can be forwarded to leaf
				{
					if(pNode && pNode->m_nType == G2_LEAF)
					{
						bForwardTCP = true;
					}
				}
			}
			else    // received from udp - do not forward via udp
			{
				bForwardTCP = true;
			}

			if(pNode && bForwardTCP)
			{
				pNode->SendPacket(pPacket, true, false);
				return true;
			}
			else if(!pAddr.isNull() && bForwardUDP)
			{
				Datagrams.SendPacket(pAddr, pPacket, true);
				return true;
			}
			// drop
		}
		return true;
	}
	return false;
}
void CDatagrams::listen()
{
	QMutexLocker l(&m_pSection);

	if(m_bActive)
	{
		systemLog.postLog(LogSeverity::Debug, QString("CDatagrams::Listen - already listening"));
		return;
	}

	Q_ASSERT(m_pSocket == 0);

	m_pSocket = new QUdpSocket(this);

	CEndPoint addr = Network.getLocalAddress();
	if(m_pSocket->bind(addr.port()))
	{
		systemLog.postLog(LogSeverity::Debug, QString("Datagrams listening on %1").arg(m_pSocket->localPort()));
		m_nDiscarded = 0;

		for(int i = 0; i < quazaaSettings.Gnutella2.UdpBuffers; i++)
		{
			m_FreeBuffer.append(new CBuffer(1024));
		}

		for(int i = 0; i < quazaaSettings.Gnutella2.UdpInFrames; i++)
		{
			m_FreeDatagramIn.append(new DatagramIn);
		}

		for(int i = 0; i < quazaaSettings.Gnutella2.UdpOutFrames; i++)
		{
			m_FreeDatagramOut.append(new DatagramOut);
		}

		connect(this, SIGNAL(sendQueueUpdated()), this, SLOT(flushSendCache()), Qt::QueuedConnection);
		connect(m_pSocket, SIGNAL(readyRead()), this, SLOT(onDatagram()), Qt::QueuedConnection);

		m_bActive = true;
	}
	else
	{
		systemLog.postLog(LogSeverity::Debug, QString("Can't bind UDP socket! UDP communication disabled!"));
		disconnectNode();
	}

	m_bFirewalled = true;
}
bool CNetwork::routePacket(QUuid& pTargetGUID, G2Packet* pPacket, bool bLockNeighbours, bool bBuffered)
{
	CG2Node* pNode = 0;
	CEndPoint pAddr;

	if(m_oRoutingTable.find(pTargetGUID, &pNode, &pAddr))
	{
		if(pNode)
		{
			if( bLockNeighbours )
			{
				Neighbours.m_pSection.lock();
			}

			if( Neighbours.neighbourExists(pNode) )
			{
				pNode->sendPacket(pPacket, bBuffered, false);
				systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 routed to neighbour: %3").arg(pTargetGUID.toString()).arg(pPacket->getType()).arg(pNode->m_oAddress.toString().toLocal8Bit().constData()));
			}

			if( bLockNeighbours )
			{
				Neighbours.m_pSection.unlock();
			}
			return true;
		}
		else if(pAddr.isValid())
		{
			Datagrams.sendPacket(pAddr, pPacket, true);
			systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 routed to remote node: %3").arg(pTargetGUID.toString()).arg(pPacket->getType()).arg(pAddr.toString().toLocal8Bit().constData()));
			return true;
		}
		systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket - No node and no address!"));
	}

	systemLog.postLog(LogSeverity::Debug, QString("CNetwork::RoutePacket %1 Packet: %2 DROPPED!").arg(pTargetGUID.toString()).arg(pPacket->getType()));
	return false;
}
CHostCache::CHostCache()
{
	QFile f("hostcache.dat");

	if(f.exists() && f.open(QFile::ReadOnly))
	{
		QDataStream s(&f);
		quint16 nVersion;
		s >> nVersion;

		CEndPoint addr;
		quint32 ts;
		quint16 nPort;

		while(!f.atEnd())
		{
			s >> *(QHostAddress*)&addr >> nPort >> ts;
			addr.setPort(nPort);
			Add(addr, ts);
		}
		f.close();

	}
void CDatagrams::sendPacket(CEndPoint& oAddr, G2Packet* pPacket, bool bAck, DatagramWatcher* pWatcher, void* pParam)
{
	if(!m_bActive)
	{
		return;
	}

	QMutexLocker l(&m_pSection);

	Q_UNUSED(pWatcher);
	Q_UNUSED(pParam);

	if(m_FreeDatagramOut.isEmpty())
	{
		systemLog.postLog(LogSeverity::Debug, QString("UDP out frames exhausted"));

		if( !bAck ) // if caller does not want ACK, drop the packet here
			return; // TODO: needs more testing

		remove(m_SendCache.last());

	}

	if(m_FreeBuffer.isEmpty())
	{
		removeOldIn(false);

		if(m_FreeBuffer.isEmpty())
		{
			systemLog.postLog(LogSeverity::Debug, QString("UDP out discarded, out of buffers"));
			return;
		}
	}

	DatagramOut* pDatagramOut = m_FreeDatagramOut.takeFirst();
	pDatagramOut->create(oAddr, pPacket, m_nSequence++, m_FreeBuffer.takeFirst(), (bAck && (m_nInFrags > 0))); // to prevent net spam when unable to receive datagrams

	m_SendCache.prepend(pDatagramOut);
	m_SendCacheMap[pDatagramOut->m_nSequence] = pDatagramOut;

	// TODO: Notify the listener if we have one.

#ifdef DEBUG_UDP
	systemLog.postLog(LogSeverity::Debug, "UDP queued for %s seq %u parts %u", oAddr.toString().toLocal8Bit().constData(), pDatagramOut->m_nSequence, pDatagramOut->m_nCount);
#endif

	//emit SendQueueUpdated();
	__FlushSendCache();
}
void CNetworkConnection::ConnectTo(CEndPoint oAddress)
{
	m_oAddress = oAddress;

	m_bInitiated = true;
	m_bConnected = false;
	m_tConnected = time(0);

	Q_ASSERT(m_pInput == 0);
	Q_ASSERT(m_pOutput == 0);
	m_pInput = new CBuffer(8192);
	m_pOutput = new CBuffer(8192);
	Q_ASSERT(m_pSocket == 0);

	m_pSocket = new QTcpSocket();
	initializeSocket();
	m_pSocket->connectToHost(oAddress, oAddress.port());

}
void CDatagrams::onQKA(CEndPoint& addr, G2Packet* pPacket)
{
	if ( !pPacket->m_bCompound )
	{
		return;
	}

	quint32 nKey = 0;
	QHostAddress nKeyHost;

	char szType[9];
	quint32 nLength = 0, nNext = 0;

	while(pPacket->readPacket(&szType[0], nLength))
	{
		nNext = pPacket->m_nPosition + nLength;

		if(strcmp("QK", szType) == 0 && nLength >= 4)
		{
			nKey = pPacket->readIntLE<quint32>();
		}
		else if(strcmp("SNA", szType) == 0 && nLength >= 4)
		{
			if(nLength >= 16)
			{
				Q_IPV6ADDR ip;
				pPacket->read(&ip, 16);
				nKeyHost.setAddress(ip);
			}
			else
			{
				quint32 nIp = pPacket->readIntBE<quint32>();
				nKeyHost.setAddress(nIp);
			}
		}
		pPacket->m_nPosition = nNext;
	}

	hostCache.m_pSection.lock();
	CHostCacheHost* pCache = hostCache.add( addr, common::getTNowUTC() );
	if ( pCache )
	{
		if( !nKey ) // null QK means a hub does not want to be queried or just downgraded
		{
			hostCache.remove( pCache );
		}
		else
		{
			pCache->setKey( nKey );
		}
	}
	hostCache.m_pSection.unlock();

#if LOG_QUERY_HANDLING
	systemLog.postLog(LogSeverity::Debug, QString("Got a query key for %1 = 0x%2").arg(addr.toString().toLocal8Bit().constData()).arg(nKey));
	//qDebug("Got a query key for %s = 0x%x", addr.toString().toLocal8Bit().constData(), nKey);
#endif // LOG_QUERY_HANDLING

	if(Neighbours.isG2Hub() && !nKeyHost.isNull() && nKeyHost != ((QHostAddress)Network.m_oAddress))
	{
		G2Packet* pQNA = G2Packet::newPacket("QNA");
		pQNA->writeHostAddress(&addr);
		pPacket->prependPacket(pQNA);

		Neighbours.m_pSection.lock();
		CNeighbour* pNode = Neighbours.find(nKeyHost, dpG2);
		if( pNode )
		{
			((CG2Node*)pNode)->sendPacket(pPacket, true, false);
		}
		Neighbours.m_pSection.unlock();
	}
}
void CDatagrams::onQKR(CEndPoint& addr, G2Packet* pPacket)
{
	if(!Neighbours.isG2Hub())
	{
		return;
	}

	CEndPoint oRequestedAddress = addr;
	CEndPoint oSendingAddress = addr;

	if(pPacket->m_bCompound)
	{
		char szType[9];
		quint32 nLength = 0, nNext = 0;

		while(pPacket->readPacket(&szType[0], nLength))
		{
			nNext = pPacket->m_nPosition + nLength;

			if(strcmp("SNA", szType) == 0 && nLength >= 4)
			{
				if(nLength >= 16)
				{
					Q_IPV6ADDR ip;
					pPacket->read(&ip, 16);
					oSendingAddress.setAddress(ip);
				}
				else
				{
					quint32	nIp = pPacket->readIntBE<quint32>();
					oSendingAddress.setAddress(nIp);
				}
			}
			else if(strcmp("RNA", szType) == 0 && nLength >= 6)
			{
				if(nLength >= 18)
				{
					pPacket->readHostAddress(&oRequestedAddress, false);
				}
				else
				{
					pPacket->readHostAddress(&oRequestedAddress);
				}
			}
			pPacket->m_nPosition = nNext;
		}
	}

	if(!oRequestedAddress.port() || oRequestedAddress.isFirewalled())
	{
		return;
	}

	G2Packet* pAns = G2Packet::newPacket("QKA", true);
	quint32 nKey = QueryKeys.create(oRequestedAddress);
	pAns->writePacket("QK", 4);
	pAns->writeIntLE<quint32>(nKey);
	G2Packet* pSNA = G2Packet::newPacket("SNA");
	pSNA->writeHostAddress(&oSendingAddress);
	pAns->writePacket(pSNA);
	pSNA->release();

	sendPacket(oRequestedAddress, pAns, false);
	pAns->release();

#if LOG_QUERY_HANDLING
	systemLog.postLog(LogSeverity::Debug, "Node %s asked for a query key (0x%08x) for node %s", qPrintable(addr.toStringWithPort()), nKey, qPrintable(oRequestedAddress.toStringWithPort()));
#endif // LOG_QUERY_HANDLING
}
void CDatagrams::onQuery(CEndPoint &addr, G2Packet *pPacket)
{
	CQueryPtr pQuery = CQuery::fromPacket(pPacket, &addr);

	if(pQuery.isNull())
	{
#if LOG_QUERY_HANDLING
		qDebug() << "Received malformed query from" << qPrintable(addr.toStringWithPort());
#endif // LOG_QUERY_HANDLING
		return;
	}

	if( !Neighbours.isG2Hub() )
	{
		// Stop receiving queries from others
		// We are here because we just downgraded to leaf mode
		// Shareaza should not retry with QK == 0
		// TODO: test this
#if LOG_QUERY_HANDLING
		systemLog.postLog(LogSeverity::Debug, "Sending null query key to %s because we're not a hub.", qPrintable(addr.toStringWithPort()));
#endif // LOG_QUERY_HANDLING

		G2Packet* pQKA = G2Packet::newPacket("QKA", true);
		pQKA->writePacket("QK", 4)->writeIntLE<quint32>(0);

		if( addr != pQuery->m_oEndpoint )
		{
			pQKA->writePacket("SNA", (pQuery->m_oEndpoint.protocol() == QAbstractSocket::IPv6Protocol ? 18 : 6))->writeHostAddress(&pQuery->m_oEndpoint);
		}

		sendPacket(pQuery->m_oEndpoint, pQKA);
		pQKA->release();

		return;
	}

	if(!QueryKeys.check(pQuery->m_oEndpoint, pQuery->m_nQueryKey))
	{
#if LOG_QUERY_HANDLING
		systemLog.postLog(LogSeverity::Debug, "Issuing query key correction for %s.", qPrintable(addr.toStringWithPort()));
#endif // LOG_QUERY_HANDLING

		G2Packet* pQKA = G2Packet::newPacket("QKA", true);
		pQKA->writePacket("QK", 4)->writeIntLE<quint32>(QueryKeys.create(pQuery->m_oEndpoint));

		if( addr != pQuery->m_oEndpoint )
		{
			pQKA->writePacket("SNA", (pQuery->m_oEndpoint.protocol() == QAbstractSocket::IPv6Protocol ? 18 : 6))->writeHostAddress(&pQuery->m_oEndpoint);
		}
		sendPacket(addr, pPacket);
		pQKA->release();

		return;
	}

	if( !Network.m_oRoutingTable.add(pQuery->m_oGUID, pQuery->m_oEndpoint) )
	{
#if LOG_QUERY_HANDLING
		qDebug() << "Query already processed, ignoring";
#endif // LOG_QUERY_HANDLING
		G2Packet* pQA = Neighbours.createQueryAck(pQuery->m_oGUID, false, 0, false);
		sendPacket(pQuery->m_oEndpoint, pQA, true);
		pQA->release();
		return;
	}

#if LOG_QUERY_HANDLING
	qDebug() << "Processing query from: " << qPrintable(addr.toStringWithPort());
#endif // LOG_QUERY_HANDLING

	// just in case
	if( pQuery->m_oEndpoint == Network.m_oAddress )
	{
		systemLog.postLog( LogSeverity::Error, Components::Network,
						   "Q2 received via UDP and return address points to us, changing return address to source %s",
						   qPrintable( addr.toStringWithPort() ) );
		G2Packet* pUDP = G2Packet::newPacket("UDP");
		pUDP->writeHostAddress(&addr);
		pUDP->writeIntLE<quint32>(0);
		pPacket->addOrReplaceChild("UDP", pUDP);
	}

	Neighbours.m_pSection.lock();
	G2Packet* pQA = Neighbours.createQueryAck(pQuery->m_oGUID);
	sendPacket(pQuery->m_oEndpoint, pQA, true);
	pQA->release();

	Neighbours.routeQuery(pQuery, pPacket);
	Neighbours.m_pSection.unlock();

	// local search
}
bool CSearchManager::OnQueryAcknowledge(G2Packet* pPacket, CEndPoint& addr, QUuid& oGUID)
{
	if(!pPacket->m_bCompound)
	{
		return false;
	}

	pPacket->SkipCompound();			// skip children to get search GUID
	if(pPacket->GetRemaining() < 16)	// must be at least 16 bytes for GUID
	{
		return false;
	}

	oGUID = pPacket->ReadGUID();		// Read search GUID

	QMutexLocker l(&m_pSection);

	if(CManagedSearch* pSearch = Find(oGUID))	// is it our Query Ack?
	{
		// YES, this is ours, let's parse the packet and process it

		CEndPoint oFromIp = addr;
		QList<QHostAddress>  lDone;
		quint32 nRetryAfter = 0;
		qint64 tAdjust = 0;
		quint32 tNow = time(0);

		quint32 nHubs = 0, nLeaves = 0, nSuggestedHubs = 0;

		pPacket->m_nPosition = 0;	// reset position

		char szType[9];
		quint32 nLength = 0, nNext = 0;

		while(pPacket->ReadPacket(&szType[0], nLength))
		{
			nNext = pPacket->m_nPosition + nLength;

			if(strcmp("D", szType) == 0 && nLength >= 4)
			{
				QHostAddress ha;
				if( nLength >= 16 )
				{
					// IPv6
					Q_IPV6ADDR nIP;
					pPacket->Read(&nIP, 16);
					ha.setAddress(nIP);

					if( nLength >= 18 )
					{
						quint16 nPort = pPacket->ReadIntLE<quint16>();
						CEndPoint a(nIP, nPort);
						HostCache.Add(a, tNow);
					}

					if(nLength >= 20)
					{
						nLeaves += pPacket->ReadIntLE<quint16>();
					}
				}
				else
				{
					// IPv4
					quint32 nIP = pPacket->ReadIntBE<quint32>();
					ha.setAddress(nIP);

					if(nLength >= 6)
					{
						quint16 nPort = pPacket->ReadIntLE<quint16>();
						CEndPoint a(nIP, nPort);
						HostCache.Add(a, tNow);
					}

					if(nLength >= 8)
					{
						nLeaves += pPacket->ReadIntLE<quint16>();
					}
				}
				lDone.append(ha);

				nHubs++;
			}
			else if(strcmp("S", szType) == 0 && nLength >= 6)
			{
				CEndPoint a;
				pPacket->ReadHostAddress(&a, !(nLength >= 18));
				quint32 tSeen = (nLength >= (a.protocol() == 0 ? 10 : 22)) ? pPacket->ReadIntLE<quint32>() + tAdjust : tNow;

				HostCache.Add(a, tSeen);
				nSuggestedHubs++;
			}
			else if(strcmp("TS", szType) == 0 && nLength >= 4)
			{
				tAdjust = tNow - pPacket->ReadIntLE<quint32>();
			}
			else if(strcmp("RA", szType) == 0 && nLength >= 2)
			{
				if(nLength >= 4)
				{
					nRetryAfter = pPacket->ReadIntLE<quint32>();
				}
				else if(nLength >= 2)
				{
					nRetryAfter = pPacket->ReadIntLE<quint16>();
				}

				CHostCacheHost* pHost = HostCache.Find(oFromIp);
				if(pHost)
				{
					pHost->m_tRetryAfter = tNow + nRetryAfter;
				}
			}
			else if(strcmp("FR", szType) == 0 && nLength >= 4)
			{
				if( nLength >= 16 )
				{
					Q_IPV6ADDR ip;
					pPacket->Read(&ip, 16);
					oFromIp.setAddress(ip);
				}
				else
				{
					quint32 nFromIp = pPacket->ReadIntBE<quint32>();
					oFromIp.setAddress(nFromIp);
				}
			}

			pPacket->m_nPosition = nNext;
		}

		// we already know QA GUID

		systemLog.postLog(LogSeverity::Debug, "Processing query acknowledge from %s (time adjust %+d seconds): %d hubs, %d leaves, %d suggested hubs, retry after %d seconds.",
			   addr.toString().toAscii().constData(), int(tAdjust), nHubs, nLeaves, nSuggestedHubs, nRetryAfter);
		//qDebug("Processing query acknowledge from %s (time adjust %+d seconds): %d hubs, %d leaves, %d suggested hubs, retry after %d seconds.",
		//	   addr.toString().toAscii().constData(), int(tAdjust), nHubs, nLeaves, nSuggestedHubs, nRetryAfter);

		pSearch->m_nHubs += nHubs;
		pSearch->m_nLeaves += nLeaves;

		pSearch->OnHostAcknowledge(oFromIp, tNow);

		for(int i = 0; i < lDone.size(); i++)
		{
			pSearch->OnHostAcknowledge(lDone[i], tNow);
		}

		emit pSearch->StatsUpdated();

		return false;
	}

	// not our ack - tell caller to route it
	return true;

}