123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- #include "UDPTrackerClient.h"
- #include <cstring>
- #include <cppunit/extensions/HelperMacros.h>
- #include "TestUtil.h"
- #include "UDPTrackerRequest.h"
- #include "bittorrent_helper.h"
- #include "wallclock.h"
- namespace aria2 {
- class UDPTrackerClientTest:public CppUnit::TestFixture {
- CPPUNIT_TEST_SUITE(UDPTrackerClientTest);
- CPPUNIT_TEST(testCreateUDPTrackerConnect);
- CPPUNIT_TEST(testCreateUDPTrackerAnnounce);
- CPPUNIT_TEST(testConnectFollowedByAnnounce);
- CPPUNIT_TEST(testRequestFailure);
- CPPUNIT_TEST(testTimeout);
- CPPUNIT_TEST_SUITE_END();
- public:
- void setUp()
- {
- }
- void testCreateUDPTrackerConnect();
- void testCreateUDPTrackerAnnounce();
- void testConnectFollowedByAnnounce();
- void testRequestFailure();
- void testTimeout();
- };
- CPPUNIT_TEST_SUITE_REGISTRATION( UDPTrackerClientTest );
- namespace {
- std::shared_ptr<UDPTrackerRequest> createAnnounce(const std::string& remoteAddr,
- uint16_t remotePort,
- int32_t transactionId)
- {
- std::shared_ptr<UDPTrackerRequest> req(new UDPTrackerRequest());
- req->connectionId = INT64_MAX;
- req->action = UDPT_ACT_ANNOUNCE;
- req->remoteAddr = remoteAddr;
- req->remotePort = remotePort;
- req->transactionId = transactionId;
- req->infohash = "bittorrent-infohash-";
- req->peerId = "bittorrent-peer-id--";
- req->downloaded = INT64_MAX - 1;
- req->left = INT64_MAX - 2;
- req->uploaded = INT64_MAX - 3;
- req->event = UDPT_EVT_STARTED;
- req->ip = 0;
- req->key = 1000000007;
- req->numWant = 50;
- req->port = 6889;
- req->extensions = 0;
- return req;
- }
- } // namespace
- namespace {
- ssize_t createErrorReply(unsigned char* data, size_t len,
- int32_t transactionId, const std::string& errorString)
- {
- bittorrent::setIntParam(data, UDPT_ACT_ERROR);
- bittorrent::setIntParam(data+4, transactionId);
- memcpy(data+8, errorString.c_str(), errorString.size());
- return 8+errorString.size();
- }
- } // namespace
- namespace {
- ssize_t createConnectReply(unsigned char* data, size_t len,
- uint64_t connectionId, int32_t transactionId)
- {
- bittorrent::setIntParam(data, UDPT_ACT_CONNECT);
- bittorrent::setIntParam(data+4, transactionId);
- bittorrent::setLLIntParam(data+8, connectionId);
- return 16;
- }
- } // namespace
- namespace {
- ssize_t createAnnounceReply(unsigned char*data, size_t len,
- int32_t transactionId, int numPeers = 0)
- {
- bittorrent::setIntParam(data, UDPT_ACT_ANNOUNCE);
- bittorrent::setIntParam(data+4, transactionId);
- bittorrent::setIntParam(data+8, 1800);
- bittorrent::setIntParam(data+12, 100);
- bittorrent::setIntParam(data+16, 256);
- for(int i = 0; i < numPeers; ++i) {
- bittorrent::packcompact(data+20+6*i, "192.168.0."+util::uitos(i+1),
- 6990+i);
- }
- return 20 + 6 * numPeers;
- }
- } // namespace
- void UDPTrackerClientTest::testCreateUDPTrackerConnect()
- {
- unsigned char data[16];
- std::string remoteAddr;
- uint16_t remotePort = 0;
- std::shared_ptr<UDPTrackerRequest> req(new UDPTrackerRequest());
- req->action = UDPT_ACT_CONNECT;
- req->remoteAddr = "192.168.0.1";
- req->remotePort = 6991;
- req->transactionId = 1000000009;
- ssize_t rv = createUDPTrackerConnect(data, sizeof(data), remoteAddr,
- remotePort, req);
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- CPPUNIT_ASSERT_EQUAL(req->remoteAddr, remoteAddr);
- CPPUNIT_ASSERT_EQUAL(req->remotePort, remotePort);
- CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- CPPUNIT_ASSERT_EQUAL((int)req->action, (int)bittorrent::getIntParam(data, 8));
- CPPUNIT_ASSERT_EQUAL(req->transactionId,
- (int32_t)bittorrent::getIntParam(data, 12));
- }
- void UDPTrackerClientTest::testCreateUDPTrackerAnnounce()
- {
- unsigned char data[100];
- std::string remoteAddr;
- uint16_t remotePort = 0;
- std::shared_ptr<UDPTrackerRequest> req(createAnnounce("192.168.0.1", 6991,
- 1000000009));
- ssize_t rv = createUDPTrackerAnnounce(data, sizeof(data), remoteAddr,
- remotePort, req);
- CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
- CPPUNIT_ASSERT_EQUAL(req->connectionId,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- CPPUNIT_ASSERT_EQUAL((int)req->action, (int)bittorrent::getIntParam(data, 8));
- CPPUNIT_ASSERT_EQUAL(req->transactionId,
- (int32_t)bittorrent::getIntParam(data, 12));
- CPPUNIT_ASSERT_EQUAL(req->infohash, std::string(&data[16], &data[36]));
- CPPUNIT_ASSERT_EQUAL(req->peerId, std::string(&data[36], &data[56]));
- CPPUNIT_ASSERT_EQUAL(req->downloaded,
- (int64_t)bittorrent::getLLIntParam(data, 56));
- CPPUNIT_ASSERT_EQUAL(req->left,
- (int64_t)bittorrent::getLLIntParam(data, 64));
- CPPUNIT_ASSERT_EQUAL(req->uploaded,
- (int64_t)bittorrent::getLLIntParam(data, 72));
- CPPUNIT_ASSERT_EQUAL(req->event, (int32_t)bittorrent::getIntParam(data, 80));
- CPPUNIT_ASSERT_EQUAL(req->ip, bittorrent::getIntParam(data, 84));
- CPPUNIT_ASSERT_EQUAL(req->key, bittorrent::getIntParam(data, 88));
- CPPUNIT_ASSERT_EQUAL(req->numWant,
- (int32_t)bittorrent::getIntParam(data, 92));
- CPPUNIT_ASSERT_EQUAL(req->port, bittorrent::getShortIntParam(data, 96));
- CPPUNIT_ASSERT_EQUAL(req->extensions, bittorrent::getShortIntParam(data, 98));
- }
- void UDPTrackerClientTest::testConnectFollowedByAnnounce()
- {
- ssize_t rv;
- UDPTrackerClient tr;
- unsigned char data[100];
- std::string remoteAddr;
- uint16_t remotePort;
- Timer now;
- std::shared_ptr<UDPTrackerRequest> req1(createAnnounce("192.168.0.1", 6991, 0));
- std::shared_ptr<UDPTrackerRequest> req2(createAnnounce("192.168.0.1", 6991, 0));
- req2->infohash = "bittorrent-infohash2";
- tr.addRequest(req1);
- tr.addRequest(req2);
- CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // CONNECT request was inserted
- CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- CPPUNIT_ASSERT_EQUAL(req1->remoteAddr, remoteAddr);
- CPPUNIT_ASSERT_EQUAL(req1->remotePort, remotePort);
- CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- int32_t transactionId = bittorrent::getIntParam(data, 12);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // Duplicate CONNECT request was not inserted
- CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- tr.requestSent(now);
- // CONNECT request was moved to inflight
- CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // Now all pending requests were moved to connect
- CPPUNIT_ASSERT_EQUAL((ssize_t)-1, rv);
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- int64_t connectionId = 12345;
- rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL(0, (int)rv);
- // Now 2 requests get back to pending
- CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // Creates announce for req1
- CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
- CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
- CPPUNIT_ASSERT_EQUAL(connectionId,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
- (int)bittorrent::getIntParam(data, 8));
- CPPUNIT_ASSERT_EQUAL(req1->infohash,
- std::string(&data[16], &data[36]));
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // Don't duplicate same request data
- CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
- CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
- int32_t transactionId1 = bittorrent::getIntParam(data, 12);
- tr.requestSent(now);
- CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- int32_t transactionId2 = bittorrent::getIntParam(data, 12);
- // Creates announce for req2
- CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
- CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
- CPPUNIT_ASSERT_EQUAL(connectionId,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
- (int)bittorrent::getIntParam(data, 8));
- CPPUNIT_ASSERT_EQUAL(req2->infohash,
- std::string(&data[16], &data[36]));
- tr.requestSent(now);
- // Now all requests are inflight
- CPPUNIT_ASSERT_EQUAL((size_t)0, tr.getPendingRequests().size());
- // Reply for req2
- rv = createAnnounceReply(data, sizeof(data), transactionId2);
- rv = tr.receiveReply(data, rv, req2->remoteAddr, req2->remotePort, now);
- CPPUNIT_ASSERT_EQUAL(0, (int)rv);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_SUCCESS, req2->error);
- // Reply for req1
- rv = createAnnounceReply(data, sizeof(data), transactionId1, 2);
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL(0, (int)rv);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_SUCCESS, req1->error);
- CPPUNIT_ASSERT_EQUAL((size_t)2, req1->reply->peers.size());
- for(int i = 0; i < 2; ++i) {
- CPPUNIT_ASSERT_EQUAL("192.168.0."+util::uitos(i+1),
- req1->reply->peers[i].first);
- CPPUNIT_ASSERT_EQUAL((uint16_t)(6990+i), req1->reply->peers[i].second);
- }
- // Since we have connection ID, next announce request can be sent
- // immediately
- std::shared_ptr<UDPTrackerRequest> req3(createAnnounce("192.168.0.1", 6991, 0));
- req3->infohash = "bittorrent-infohash3";
- tr.addRequest(req3);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
- CPPUNIT_ASSERT_EQUAL(req3->infohash,
- std::string(&data[16], &data[36]));
- tr.requestSent(now);
- std::shared_ptr<UDPTrackerRequest> req4(createAnnounce("192.168.0.1", 6991, 0));
- req4->infohash = "bittorrent-infohash4";
- tr.addRequest(req4);
- Timer future = now;
- future.advance(3600);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort,
- future);
- // connection ID is stale because of the timeout
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
- (int64_t)bittorrent::getLLIntParam(data, 0));
- }
- void UDPTrackerClientTest::testRequestFailure()
- {
- ssize_t rv;
- UDPTrackerClient tr;
- unsigned char data[100];
- std::string remoteAddr;
- uint16_t remotePort;
- Timer now;
- {
- std::shared_ptr<UDPTrackerRequest> req1
- (createAnnounce("192.168.0.1", 6991, 0));
- std::shared_ptr<UDPTrackerRequest> req2
- (createAnnounce("192.168.0.1", 6991, 0));
- tr.addRequest(req1);
- tr.addRequest(req2);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- tr.requestFail(UDPT_ERR_NETWORK);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_NETWORK, req1->error);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_NETWORK, req2->error);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- }
- {
- std::shared_ptr<UDPTrackerRequest> req1
- (createAnnounce("192.168.0.1", 6991, 0));
- std::shared_ptr<UDPTrackerRequest> req2
- (createAnnounce("192.168.0.1", 6991, 0));
- tr.addRequest(req1);
- tr.addRequest(req2);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- int32_t transactionId = bittorrent::getIntParam(data, 12);
- tr.requestSent(now);
- rv = createErrorReply(data, sizeof(data), transactionId, "error");
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req1->error);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req2->error);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- }
- {
- std::shared_ptr<UDPTrackerRequest> req1
- (createAnnounce("192.168.0.1", 6991, 0));
- tr.addRequest(req1);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- int32_t transactionId = bittorrent::getIntParam(data, 12);
- tr.requestSent(now);
- int64_t connectionId = 12345;
- rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL(0, (int)rv);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
- (int)bittorrent::getIntParam(data, 8));
- transactionId = bittorrent::getIntParam(data, 12);
- tr.requestSent(now);
- rv = createErrorReply(data, sizeof(data), transactionId, "announce error");
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req1->error);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- }
- }
- void UDPTrackerClientTest::testTimeout()
- {
- ssize_t rv;
- unsigned char data[100];
- std::string remoteAddr;
- uint16_t remotePort;
- Timer now;
- UDPTrackerClient tr;
- {
- std::shared_ptr<UDPTrackerRequest> req1
- (createAnnounce("192.168.0.1", 6991, 0));
- std::shared_ptr<UDPTrackerRequest> req2
- (createAnnounce("192.168.0.1", 6991, 0));
- tr.addRequest(req1);
- tr.addRequest(req2);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- tr.requestSent(now);
- now.advance(20);
- // 15 seconds 1st stage timeout passed
- tr.handleTimeout(now);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- // CONNECT request was inserted
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- tr.requestSent(now);
- now.advance(65);
- // 60 seconds 2nd stage timeout passed
- tr.handleTimeout(now);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req1->error);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req2->error);
- }
- {
- std::shared_ptr<UDPTrackerRequest> req1
- (createAnnounce("192.168.0.1", 6991, 0));
- tr.addRequest(req1);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
- (int)bittorrent::getIntParam(data, 8));
- int32_t transactionId = bittorrent::getIntParam(data, 12);
- tr.requestSent(now);
- int64_t connectionId = 12345;
- rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
- rv = tr.receiveReply(data, rv, req1->remoteAddr, req1->remotePort, now);
- CPPUNIT_ASSERT_EQUAL(0, (int)rv);
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
- (int)bittorrent::getIntParam(data, 8));
- tr.requestSent(now);
- now.advance(20);
- // 15 seconds 1st stage timeout passed
- tr.handleTimeout(now);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
- (int)bittorrent::getIntParam(data, 8));
- tr.requestSent(now);
- now.advance(65);
- // 60 seconds 2nd stage timeout passed
- tr.handleTimeout(now);
- CPPUNIT_ASSERT(tr.getConnectRequests().empty());
- CPPUNIT_ASSERT(tr.getPendingRequests().empty());
- CPPUNIT_ASSERT(tr.getInflightRequests().empty());
- CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
- CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req1->error);
- }
- }
- } // namespace aria2
|