int tr_ctorSetMetainfo( tr_ctor * ctor, const uint8_t * metainfo, size_t len ) { int err; clearMetainfo( ctor ); err = tr_bencLoad( metainfo, len, &ctor->metainfo, NULL ); ctor->isSet_metainfo = !err; return err; }
static int testJSONSnippet( const char * benc_str, const char * expected ) { tr_benc top; char * serialized; tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); serialized = tr_bencSaveAsJSON( &top, NULL ); stripWhitespace( serialized ); #if 0 fprintf( stderr, "benc: %s\n", benc_str ); fprintf( stderr, "json: %s\n", serialized ); fprintf( stderr, "want: %s\n", expected ); #endif check( !strcmp( serialized, expected ) ); tr_free( serialized ); tr_bencFree( &top ); return 0; }
static int testJSONSnippet( const char * benc_str, const char * expected ) { tr_benc top; char * serialized; struct evbuffer * buf; tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); buf = tr_bencToBuf( &top, TR_FMT_JSON ); serialized = (char*) evbuffer_pullup( buf, -1 ); stripWhitespace( serialized ); #if 0 fprintf( stderr, "benc: %s\n", benc_str ); fprintf( stderr, "json: %s\n", serialized ); fprintf( stderr, "want: %s\n", expected ); #endif check_streq (expected, serialized); tr_bencFree( &top ); evbuffer_free( buf ); return 0; }
static void on_scrape_done( tr_session * session, bool did_connect, bool did_timeout, long response_code, const void * msg, size_t msglen, void * vdata ) { tr_scrape_response * response; struct scrape_data * data = vdata; response = &data->response; response->did_connect = did_connect; response->did_timeout = did_timeout; dbgmsg( data->log_name, "Got scrape response for \"%s\"", response->url ); 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 top; int64_t intVal; tr_benc * files; const char * str; const int benc_loaded = !tr_bencLoad( msg, msglen, &top, NULL ); if( benc_loaded ) { if( tr_bencDictFindStr( &top, "failure reason", &str ) ) response->errmsg = tr_strdup( str ); if( tr_bencDictFindInt( &top, "min_request_interval", &intVal ) ) response->min_request_interval = intVal; if( tr_bencDictFindDict( &top, "files", &files ) ) { int i = 0; for( ;; ) { int j; tr_benc * val; const char * key; /* get the next "file" */ if( !tr_bencDictChild( files, i++, &key, &val ) ) break; /* populate the corresponding row in our response array */ for( j=0; j<response->row_count; ++j ) { struct tr_scrape_response_row * row = &response->rows[j]; if( !memcmp( key, row->info_hash, SHA_DIGEST_LENGTH ) ) { if( tr_bencDictFindInt( val, "complete", &intVal ) ) row->seeders = intVal; if( tr_bencDictFindInt( val, "incomplete", &intVal ) ) row->leechers = intVal; if( tr_bencDictFindInt( val, "downloaded", &intVal ) ) row->downloads = intVal; if( tr_bencDictFindInt( val, "downloaders", &intVal ) ) row->downloaders = intVal; break; } } } } tr_bencFree( &top ); } } tr_runInEventThread( session, on_scrape_done_eventthread, data ); }
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_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers ) { tr_info_t * inf = &tor->info; int s, i, ret; uint8_t buf[1024]; benc_val_t scrape, * val1, * val2; struct in_addr addr; uint64_t date; int pos, len; tr_resolve_t * resolve; if( !tor->scrape[0] ) { /* scrape not supported */ return 1; } resolve = tr_netResolveInit( inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netResolvePulse( resolve, &addr ); if( ret == TR_RESOLVE_OK ) { tr_netResolveClose( resolve ); break; } if( ret == TR_RESOLVE_ERROR || ( ret == TR_RESOLVE_WAIT && tr_date() > date + 10000 ) ) { fprintf( stderr, "Could not resolve %s\n", inf->trackerAddress ); tr_netResolveClose( resolve ); return 1; } tr_wait( 10 ); } s = tr_netOpen( addr, htons( inf->trackerPort ) ); if( s < 0 ) { return 1; } len = snprintf( (char *) buf, sizeof( buf ), "GET %s?info_hash=%s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n", tor->scrape, tor->hashString, inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netSend( s, buf, len ); if( ret & TR_NET_CLOSE ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } } else { break; } tr_wait( 10 ); } pos = 0; for( date = tr_date();; ) { ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos ); if( ret & TR_NET_CLOSE ) { break; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } } else { pos += ret; } tr_wait( 10 ); } if( pos < 1 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } for( i = 0; i < pos - 8; i++ ) { if( !memcmp( &buf[i], "d5:files", 8 ) ) { break; } } if( i >= pos - 8 ) { return 1; } if( tr_bencLoad( &buf[i], pos - i, &scrape, NULL ) ) { return 1; } val1 = tr_bencDictFind( &scrape, "files" ); if( !val1 ) { return 1; } val1 = &val1->val.l.vals[1]; if( !val1 ) { return 1; } val2 = tr_bencDictFind( val1, "complete" ); if( !val2 ) { return 1; } *seeders = val2->val.i; val2 = tr_bencDictFind( val1, "incomplete" ); if( !val2 ) { return 1; } *leechers = val2->val.i; tr_bencFree( &scrape ); return 0; }
static void recvAnswer( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; int ret; int i; benc_val_t beAll; benc_val_t * bePeers, * beFoo; uint8_t * body; int bodylen; if( tc->pos == tc->size ) { tc->size *= 2; tc->buf = realloc( tc->buf, tc->size ); } ret = tr_netRecv( tc->socket, &tc->buf[tc->pos], tc->size - tc->pos ); if( ret & TR_NET_BLOCK ) { return; } if( !( ret & TR_NET_CLOSE ) ) { // printf( "got %d bytes\n", ret ); tc->pos += ret; return; } tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); // printf( "connection closed, got total %d bytes\n", tc->pos ); tc->status = TC_STATUS_IDLE; tc->dateTry = tr_date(); if( tc->pos < 12 || ( 0 != memcmp( tc->buf, "HTTP/1.0 ", 9 ) && 0 != memcmp( tc->buf, "HTTP/1.1 ", 9 ) ) ) { /* We don't have a complete HTTP status line */ tr_inf( "Tracker: incomplete HTTP status line" ); tc->lastAttempt = TC_ATTEMPT_NOREACH; return; } if( '2' != tc->buf[9] ) { /* we didn't get a 2xx status code */ tr_err( "Tracker: invalid HTTP status code: %c%c%c", tc->buf[9], tc->buf[10], tc->buf[11] ); tc->lastAttempt = TC_ATTEMPT_ERROR; return; } /* find the end of the http headers */ body = tr_memmem( tc->buf, tc->pos, "\015\012\015\012", 4 ); if( NULL != body ) { body += 4; } /* hooray for trackers that violate the HTTP spec */ else if( NULL != ( body = tr_memmem( tc->buf, tc->pos, "\015\015", 2 ) ) || NULL != ( body = tr_memmem( tc->buf, tc->pos, "\012\012", 2 ) ) ) { body += 2; } else { tr_err( "Tracker: could not find end of HTTP headers" ); tc->lastAttempt = TC_ATTEMPT_NOREACH; return; } bodylen = tc->pos - (body - tc->buf); /* Find and load the dictionary */ for( i = 0; i < bodylen; i++ ) { if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) ) { break; } } if( i >= bodylen ) { if( tc->stopped || 0 < tc->newPort ) { tc->lastAttempt = TC_ATTEMPT_OK; goto nodict; } tr_err( "Tracker: no valid dictionary found in answer" ); tc->lastAttempt = TC_ATTEMPT_ERROR; return; } // tr_bencPrint( &beAll ); if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) ) { tr_err( "Tracker: %s", bePeers->val.s.s ); tor->error |= TR_ETRACKER; snprintf( tor->trackerError, sizeof( tor->trackerError ), "%s", bePeers->val.s.s ); tc->lastAttempt = TC_ATTEMPT_ERROR; goto cleanup; } tor->error &= ~TR_ETRACKER; tc->lastAttempt = TC_ATTEMPT_OK; if( !tc->interval ) { /* Get the tracker interval, ignore it if it is not between 10 sec and 5 mins */ if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) || !( beFoo->type & TYPE_INT ) ) { tr_err( "Tracker: no 'interval' field" ); goto cleanup; } tc->interval = beFoo->val.i; tc->interval = MIN( tc->interval, 300 ); tc->interval = MAX( 10, tc->interval ); tr_inf( "Tracker: interval = %d seconds", tc->interval ); } if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) && ( beFoo->type & TYPE_INT ) ) { tc->seeders = beFoo->val.i; } if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) && ( beFoo->type & TYPE_INT ) ) { tc->leechers = beFoo->val.i; } if( tc->seeders + tc->leechers >= 50 ) { tc->hasManyPeers = 1; } if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) ) { if( tc->stopped || 0 < tc->newPort ) { goto nodict; } tr_err( "Tracker: no \"peers\" field" ); goto cleanup; } if( bePeers->type & TYPE_LIST ) { char * ip; int port; /* Original protocol */ tr_inf( "Tracker: got %d peers", bePeers->val.l.count ); for( i = 0; i < bePeers->val.l.count; i++ ) { beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" ); if( !beFoo ) continue; ip = beFoo->val.s.s; beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" ); if( !beFoo ) continue; port = beFoo->val.i; tr_peerAddOld( tor, ip, port ); } if( bePeers->val.l.count >= 50 ) { tc->hasManyPeers = 1; } } else if( bePeers->type & TYPE_STR ) { struct in_addr addr; in_port_t port; /* "Compact" extension */ if( bePeers->val.s.i % 6 ) { tr_err( "Tracker: \"peers\" of size %d", bePeers->val.s.i ); tr_lockUnlock( &tor->lock ); goto cleanup; } tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 ); for( i = 0; i < bePeers->val.s.i / 6; i++ ) { memcpy( &addr, &bePeers->val.s.s[6*i], 4 ); memcpy( &port, &bePeers->val.s.s[6*i+4], 2 ); tr_peerAddCompact( tor, addr, port ); } if( bePeers->val.s.i / 6 >= 50 ) { tc->hasManyPeers = 1; } } nodict: /* Success */ tc->started = 0; tc->completed = 0; tc->dateOk = tr_date(); if( tc->stopped ) { tor->status = TR_STATUS_STOPPED; tc->stopped = 0; } else if( 0 < tc->newPort ) { tc->started = 1; tc->download = tor->downloaded; tc->upload = tor->uploaded; } cleanup: tr_bencFree( &beAll ); }
int _tr_bencLoad( char * buf, size_t len, benc_val_t * val, char ** end ) { char * p, * e, * foo; if( 1 >= len ) { return 1; } if( !end ) { /* So we only have to check once */ end = &foo; } val->begin = buf; if( buf[0] == 'i' ) { e = memchr( &buf[1], 'e', len - 1 ); if( NULL == e ) { return 1; } /* Integer: i1242e */ val->type = TYPE_INT; *e = '\0'; val->val.i = strtoll( &buf[1], &p, 10 ); *e = 'e'; if( p != e ) { return 1; } val->end = p + 1; } else if( buf[0] == 'l' || buf[0] == 'd' ) { /* List: l<item1><item2>e Dict: d<string1><item1><string2><item2>e A dictionary is just a special kind of list with an even count of items, and where even items are strings. */ char * cur; char is_dict; char str_expected; is_dict = ( buf[0] == 'd' ); val->type = is_dict ? TYPE_DICT : TYPE_LIST; val->val.l.alloc = LIST_SIZE; val->val.l.count = 0; val->val.l.vals = malloc( LIST_SIZE * sizeof( benc_val_t ) ); cur = &buf[1]; str_expected = 1; while( (size_t)(cur - buf) < len && cur[0] != 'e' ) { if( val->val.l.count == val->val.l.alloc ) { /* We need a bigger boat */ val->val.l.alloc += LIST_SIZE; val->val.l.vals = realloc( val->val.l.vals, val->val.l.alloc * sizeof( benc_val_t ) ); } if( tr_bencLoad( cur, len - (cur - buf), &val->val.l.vals[val->val.l.count], &p ) ) { return 1; } if( is_dict && str_expected && val->val.l.vals[val->val.l.count].type != TYPE_STR ) { return 1; } str_expected = !str_expected; val->val.l.count++; cur = p; } if( is_dict && ( val->val.l.count & 1 ) ) { return 1; } val->end = cur + 1; } else { e = memchr( buf, ':', len ); if( NULL == e ) { return 1; } /* String: 12:whateverword */ val->type = TYPE_STR; e[0] = '\0'; val->val.s.i = strtol( buf, &p, 10 ); e[0] = ':'; if( p != e || 0 > val->val.s.i || (size_t)(val->val.s.i) > len - ((p + 1) - buf) ) { return 1; } val->val.s.s = malloc( val->val.s.i + 1 ); val->val.s.s[val->val.s.i] = 0; memcpy( val->val.s.s, p + 1, val->val.s.i ); val->end = p + 1 + val->val.s.i; } *end = val->end; return 0; }