static uint64_t loadPeers( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; const uint8_t * str; size_t len; if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) ) { int i; const int count = len / sizeof( tr_pex ); for( i = 0; i < count; ++i ) { tr_pex pex; memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) ); tr_peerMgrAddPex( tor->session->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex ); } tr_tordbg( tor, "Loaded %d peers from resume file", count ); ret = TR_FR_PEERS; } return ret; }
static uint64_t loadPeers( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; const uint8_t * str; size_t len; if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) ) { const int numAdded = addPeers( tor, str, len ); tr_tordbg( tor, "Loaded %d IPv4 peers from resume file", numAdded ); ret = TR_FR_PEERS; } if( tr_bencDictFindRaw( dict, KEY_PEERS6, &str, &len ) ) { const int numAdded = addPeers( tor, str, len ); tr_tordbg( tor, "Loaded %d IPv6 peers from resume file", numAdded ); ret = TR_FR_PEERS; } return ret; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { size_t i, n; uint64_t ret = 0; tr_benc * prog; const tr_info * inf = tr_torrentInfo( tor ); for( i=0, n=inf->pieceCount; i<n; ++i ) inf->pieces[i].timeChecked = 0; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) ) { const char * err; const char * str; const uint8_t * raw; size_t rawlen; tr_benc * l; tr_benc * b; struct tr_bitset bitset = TR_BITSET_INIT; if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) ) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ tr_file_index_t fi; for( fi=0; fi<inf->fileCount; ++fi ) { tr_benc * b = tr_bencListChild( l, fi ); const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]+1; if( tr_bencIsInt( b ) ) { int64_t t; tr_bencGetInt( b, &t ); for( ; p!=pend; ++p ) p->timeChecked = (time_t)t; } else if( tr_bencIsList( b ) ) { int i = 0; int64_t offset = 0; const int pieces = f->lastPiece + 1 - f->firstPiece; tr_bencGetInt( tr_bencListChild( b, 0 ), &offset ); for( i=0; i<pieces; ++i ) { int64_t t = 0; tr_bencGetInt( tr_bencListChild( b, i+1 ), &t ); inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0); } } } } else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) ) { tr_file_index_t fi; /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for( fi=0; fi<inf->fileCount; ++fi ) { int64_t t; if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) ) { const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]; const time_t mtime = tr_torrentGetFileMTime( tor, fi ); const time_t timeChecked = mtime==t ? mtime : 0; for( ; p!=pend; ++p ) p->timeChecked = timeChecked; } } } err = NULL; if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS ))) { if( !tr_bitsetFromBenc( &bitset, b ) ) err = "Invalid value for PIECES"; } else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) ) { if( !strcmp( str, "all" ) ) tr_bitsetSetHaveAll( &bitset ); else err = "Invalid value for HAVE"; } else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { bitset.bitfield.bits = (void*) raw; bitset.bitfield.byteCount = rawlen; bitset.bitfield.bitCount = rawlen * 8; } else err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; if( !err && !tr_cpBlockBitsetInit( &tor->completion, &bitset ) ) err = "Error loading bitfield"; if( err != NULL ) tr_tordbg( tor, "Torrent needs to be verified - %s", err ); ret = TR_FR_PROGRESS; } return ret; }
static void on_announce_done( tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata ) { tr_announce_response * response; struct announce_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg( data->log_name, "Got announce response" ); if( response_code != HTTP_OK ) { const char * fmt = _( "Tracker gave HTTP response code %1$ld (%2$s)" ); const char * response_str = tr_webGetResponseStr( response_code ); response->errmsg = tr_strdup_printf( fmt, response_code, response_str ); } else { tr_benc benc; const int benc_loaded = !tr_bencLoad( msg, msglen, &benc, NULL ); if( getenv( "TR_CURL_VERBOSE" ) != NULL ) { struct evbuffer * buf = tr_bencToBuf( &benc, TR_FMT_JSON ); fprintf( stderr, "Announce response:\n< %s\n", evbuffer_pullup( buf, -1 ) ); tr_free( buf ); } if( benc_loaded && tr_bencIsDict( &benc ) ) { int64_t i; size_t rawlen; tr_benc * tmp; const char * str; const uint8_t * raw; if( tr_bencDictFindStr( &benc, "failure reason", &str ) ) response->errmsg = tr_strdup( str ); if( tr_bencDictFindStr( &benc, "warning message", &str ) ) response->warning = tr_strdup( str ); if( tr_bencDictFindInt( &benc, "interval", &i ) ) response->interval = i; if( tr_bencDictFindInt( &benc, "min interval", &i ) ) response->min_interval = i; if( tr_bencDictFindStr( &benc, "tracker id", &str ) ) response->tracker_id_str = tr_strdup( str ); if( tr_bencDictFindInt( &benc, "complete", &i ) ) response->seeders = i; if( tr_bencDictFindInt( &benc, "incomplete", &i ) ) response->leechers = i; if( tr_bencDictFindInt( &benc, "downloaded", &i ) ) response->downloads = i; if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) ) { dbgmsg( data->log_name, "got a peers6 length of %zu", rawlen ); response->pex6 = tr_peerMgrCompact6ToPex( raw, rawlen, NULL, 0, &response->pex6_count ); } if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) ) { dbgmsg( data->log_name, "got a compact peers length of %zu", rawlen ); response->pex = tr_peerMgrCompactToPex( raw, rawlen, NULL, 0, &response->pex_count ); } else if( tr_bencDictFindList( &benc, "peers", &tmp ) ) { response->pex = listToPex( tmp, &response->pex_count ); dbgmsg( data->log_name, "got a peers list with %zu entries", response->pex_count ); } } if( benc_loaded ) tr_bencFree( &benc ); } tr_runInEventThread( session, on_announce_done_eventthread, data ); }
int tr_dhtInit(tr_session *ss, const tr_address * tr_addr) { struct sockaddr_in sin; tr_benc benc; int rc; tr_bool have_id = FALSE; char * dat_file; uint8_t * nodes = NULL, * nodes6 = NULL; const uint8_t * raw; size_t len, len6; struct bootstrap_closure * cl; if( session ) /* already initialized */ return -1; dht_port = tr_sessionGetPeerPort(ss); if(dht_port <= 0) return -1; tr_ndbg( "DHT", "Initializing DHT" ); dht_socket = socket(PF_INET, SOCK_DGRAM, 0); if(dht_socket < 0) goto fail; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, &tr_addr->addr.addr4, sizeof (struct in_addr)); sin.sin_port = htons(dht_port); rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin)); if(rc < 0) goto fail; if(tr_globalIPv6()) rebind_ipv6(TRUE); if( getenv( "TR_DHT_VERBOSE" ) != NULL ) dht_debug = stderr; dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL ); rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file ); tr_free( dat_file ); if(rc == 0) { have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len); if( have_id && len==20 ) memcpy( myid, raw, len ); if( dht_socket >= 0 && tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) { nodes = tr_memdup( raw, len ); } if( dht6_socket > 0 && tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) { nodes6 = tr_memdup( raw, len6 ); } tr_bencFree( &benc ); } if(nodes == NULL) len = 0; if(nodes6 == NULL) len6 = 0; if( have_id ) tr_ninf( "DHT", "Reusing old id" ); else { /* Note that DHT ids need to be distributed uniformly, * so it should be something truly random. */ tr_ninf( "DHT", "Generating new id" ); tr_cryptoRandBuf( myid, 20 ); } rc = dht_init( dht_socket, dht6_socket, myid, NULL ); if( rc < 0 ) goto fail; session = ss; cl = tr_new( struct bootstrap_closure, 1 ); cl->session = session; cl->nodes = nodes; cl->nodes6 = nodes6; cl->len = len; cl->len6 = len6; tr_threadNew( dht_bootstrap, cl ); dht_event = event_new( session->event_base, dht_socket, EV_READ, event_callback, NULL ); tr_timerAdd( dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) ); if( dht6_socket >= 0 ) { dht6_event = event_new( session->event_base, dht6_socket, EV_READ, event_callback, NULL ); tr_timerAdd( dht6_event, 0, tr_cryptoWeakRandInt( 1000000 ) ); } tr_ndbg( "DHT", "DHT initialized" ); return 1; fail: { const int save = errno; close(dht_socket); if( dht6_socket >= 0 ) close(dht6_socket); dht_socket = dht6_socket = -1; session = NULL; tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", save ); errno = save; } return -1; }
int tr_dhtInit(tr_session *ss) { tr_benc benc; int rc; bool have_id = false; char * dat_file; uint8_t * nodes = NULL, * nodes6 = NULL; const uint8_t * raw; size_t len, len6; struct bootstrap_closure * cl; if( session ) /* already initialized */ return -1; tr_ndbg( "DHT", "Initializing DHT" ); if( getenv( "TR_DHT_VERBOSE" ) != NULL ) dht_debug = stderr; dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL ); rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file ); tr_free( dat_file ); if(rc == 0) { have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len); if( have_id && len==20 ) memcpy( myid, raw, len ); if( ss->udp_socket >= 0 && tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) { nodes = tr_memdup( raw, len ); } if( ss->udp6_socket > 0 && tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) { nodes6 = tr_memdup( raw, len6 ); } tr_bencFree( &benc ); } if(nodes == NULL) len = 0; if(nodes6 == NULL) len6 = 0; if( have_id ) tr_ninf( "DHT", "Reusing old id" ); else { /* Note that DHT ids need to be distributed uniformly, * so it should be something truly random. */ tr_ninf( "DHT", "Generating new id" ); tr_cryptoRandBuf( myid, 20 ); } rc = dht_init( ss->udp_socket, ss->udp6_socket, myid, NULL ); if( rc < 0 ) goto fail; session = ss; cl = tr_new( struct bootstrap_closure, 1 ); cl->session = session; cl->nodes = nodes; cl->nodes6 = nodes6; cl->len = len; cl->len6 = len6; tr_threadNew( dht_bootstrap, cl ); dht_timer = evtimer_new( session->event_base, timer_callback, session ); tr_timerAdd( dht_timer, 0, tr_cryptoWeakRandInt( 1000000 ) ); tr_ndbg( "DHT", "DHT initialized" ); return 1; fail: tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", errno ); session = NULL; return -1; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_benc * p; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) ) { const uint8_t * raw; size_t rawlen; tr_benc * m; size_t n; time_t * curMTimes = tr_torrentGetMTimes( tor, &n ); if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m ) && ( n == tor->info.fileCount ) && ( n == tr_bencListSize( m ) ) ) { size_t i; for( i = 0; i < n; ++i ) { int64_t tmp; if( !tr_bencGetInt( tr_bencListChild( m, i ), &tmp ) ) { tr_tordbg( tor, "File #%zu needs to be verified - couldn't find benc entry", i ); tr_torrentSetFileChecked( tor, i, FALSE ); } else { const time_t t = (time_t) tmp; if( t == curMTimes[i] ) tr_torrentSetFileChecked( tor, i, TRUE ); else { tr_tordbg( tor, "File #%zu needs to be verified - times %lu and %lu don't match", i, t, curMTimes[i] ); tr_torrentSetFileChecked( tor, i, FALSE ); } } } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" ); } if( tr_bencDictFindRaw( p, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { tr_bitfield tmp; tmp.byteCount = rawlen; tmp.bitCount = tmp.byteCount * 8; tmp.bits = (uint8_t*) raw; if( !tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" ); } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" ); } tr_free( curMTimes ); ret = TR_FR_PROGRESS; } return ret; }