static void on_content_changed( struct evbuffer * buf, const struct evbuffer_cb_info * info, void * vtask ) { uint32_t len; const size_t n_added = info->n_added; struct tr_webseed_task * task = vtask; struct tr_webseed * w = task->webseed; if( n_added <= 0 ) return; if( !w->is_stopping ) { tr_bandwidthUsed( &w->bandwidth, TR_DOWN, n_added, true, tr_time_msec( ) ); fire_client_got_data( w, n_added ); } len = evbuffer_get_length( buf ); if( !task->response_code ) { tr_webGetTaskInfo( task->web_task, TR_WEB_GET_CODE, &task->response_code ); if( task->response_code == 206 ) { const char * url; struct connection_succeeded_data * data; url = NULL; tr_webGetTaskInfo( task->web_task, TR_WEB_GET_REAL_URL, &url ); data = tr_new( struct connection_succeeded_data, 1 ); data->webseed = w; data->real_url = tr_strdup( url ); data->piece_index = task->piece_index; data->piece_offset = task->piece_offset + (task->blocks_done * task->block_size) + (len - 1); /* processing this uses a tr_torrent pointer, so push the work to the libevent thread... */ tr_runInEventThread( w->session, connection_succeeded, data ); } }
int tr_dhtStatus( tr_session * session, int af, int * nodes_return ) { struct getstatus_closure closure = { af, -1, -1 }; if( !tr_dhtEnabled( session ) || (af == AF_INET && dht_socket < 0) || (af == AF_INET6 && dht6_socket < 0) ) { if( nodes_return ) *nodes_return = 0; return TR_DHT_STOPPED; } tr_runInEventThread( session, getstatus, &closure ); while( closure.status < 0 ) tr_wait_msec( 50 /*msec*/ ); if( nodes_return ) *nodes_return = closure.count; return closure.status; }
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_variant top; int64_t intVal; tr_variant * files; tr_variant * flags; size_t len; const char * str; const bool variant_loaded = !tr_variantFromBenc (&top, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Scrape response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&top, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Scrape response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded) { if (tr_variantDictFindStr (&top, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindDict (&top, TR_KEY_flags, &flags)) if (tr_variantDictFindInt (flags, TR_KEY_min_request_interval, &intVal)) response->min_request_interval = intVal; if (tr_variantDictFindDict (&top, TR_KEY_files, &files)) { int i = 0; for (;;) { int j; tr_quark key; tr_variant * val; /* get the next "file" */ if (!tr_variantDictChild (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 (tr_quark_get_string(key,NULL), row->info_hash, SHA_DIGEST_LENGTH)) { if (tr_variantDictFindInt (val, TR_KEY_complete, &intVal)) row->seeders = intVal; if (tr_variantDictFindInt (val, TR_KEY_incomplete, &intVal)) row->leechers = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaded, &intVal)) row->downloads = intVal; if (tr_variantDictFindInt (val, TR_KEY_downloaders, &intVal)) row->downloaders = intVal; break; } } } } tr_variantFree (&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_variant benc; const bool variant_loaded = !tr_variantFromBenc (&benc, msg, msglen); if (getenv ("TR_CURL_VERBOSE") != NULL) { if (!variant_loaded) fprintf (stderr, "%s", "Announce response was not in benc format\n"); else { int i, len; char * str = tr_variantToStr (&benc, TR_VARIANT_FMT_JSON, &len); fprintf (stderr, "%s", "Announce response:\n< "); for (i=0; i<len; ++i) fputc (str[i], stderr); fputc ('\n', stderr); tr_free (str); } } if (variant_loaded && tr_variantIsDict (&benc)) { int64_t i; size_t len; tr_variant * tmp; const char * str; const uint8_t * raw; if (tr_variantDictFindStr (&benc, TR_KEY_failure_reason, &str, &len)) response->errmsg = tr_strndup (str, len); if (tr_variantDictFindStr (&benc, TR_KEY_warning_message, &str, &len)) response->warning = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_interval, &i)) response->interval = i; if (tr_variantDictFindInt (&benc, TR_KEY_min_interval, &i)) response->min_interval = i; if (tr_variantDictFindStr (&benc, TR_KEY_tracker_id, &str, &len)) response->tracker_id_str = tr_strndup (str, len); if (tr_variantDictFindInt (&benc, TR_KEY_complete, &i)) response->seeders = i; if (tr_variantDictFindInt (&benc, TR_KEY_incomplete, &i)) response->leechers = i; if (tr_variantDictFindInt (&benc, TR_KEY_downloaded, &i)) response->downloads = i; if (tr_variantDictFindRaw (&benc, TR_KEY_peers6, &raw, &len)) { dbgmsg (data->log_name, "got a peers6 length of %zu", len); response->pex6 = tr_peerMgrCompact6ToPex (raw, len, NULL, 0, &response->pex6_count); } if (tr_variantDictFindRaw (&benc, TR_KEY_peers, &raw, &len)) { dbgmsg (data->log_name, "got a compact peers length of %zu", len); response->pex = tr_peerMgrCompactToPex (raw, len, NULL, 0, &response->pex_count); } else if (tr_variantDictFindList (&benc, TR_KEY_peers, &tmp)) { response->pex = listToPex (tmp, &response->pex_count); dbgmsg (data->log_name, "got a peers list with %zu entries", response->pex_count); } } if (variant_loaded) tr_variantFree (&benc); } tr_runInEventThread (session, on_announce_done_eventthread, data); }
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 ); }
static void tr_webThreadFunc( void * vsession ) { int unused; CURLM * multi; struct tr_web * web; int taskCount = 0; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if( curl_global_init( CURL_GLOBAL_SSL ) ) curl_global_init( 0 ); web = tr_new0( struct tr_web, 1 ); web->close_mode = ~0; web->taskLock = tr_lockNew( ); web->tasks = NULL; multi = curl_multi_init( ); session->web = web; for( ;; ) { long msec; CURLMsg * msg; CURLMcode mcode; struct tr_web_task * task; if( web->close_mode == TR_WEB_CLOSE_NOW ) break; if( ( web->close_mode == TR_WEB_CLOSE_WHEN_IDLE ) && !taskCount ) break; /* add tasks from the queue */ tr_lockLock( web->taskLock ); while(( task = tr_list_pop_front( &web->tasks ))) { dbgmsg( "adding task to curl: [%s]", task->url ); curl_multi_add_handle( multi, createEasy( session, task )); /*fprintf( stderr, "adding a task.. taskCount is now %d\n", taskCount );*/ ++taskCount; } tr_lockUnlock( web->taskLock ); /* maybe wait a little while before calling curl_multi_perform() */ msec = 0; curl_multi_timeout( multi, &msec ); if( msec < 0 ) msec = THREADFUNC_MAX_SLEEP_MSEC; if( msec > 0 ) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO( &r_fd_set ); FD_ZERO( &w_fd_set ); FD_ZERO( &c_fd_set ); curl_multi_fdset( multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd ); if( msec > THREADFUNC_MAX_SLEEP_MSEC ) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select( max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t ); } /* call curl_multi_perform() */ do { mcode = curl_multi_perform( multi, &unused ); } while( mcode == CURLM_CALL_MULTI_PERFORM ); /* pump completed tasks from the multi */ while(( msg = curl_multi_info_read( multi, &unused ))) { if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL )) { struct tr_web_task * task; CURL * e = msg->easy_handle; curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task ); curl_easy_getinfo( e, CURLINFO_RESPONSE_CODE, &task->code ); curl_multi_remove_handle( multi, e ); curl_easy_cleanup( e ); /*fprintf( stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, (int)evbuffer_get_length(task->response) );*/ tr_runInEventThread( task->session, task_finish_func, task ); --taskCount; } } } /* cleanup */ curl_multi_cleanup( multi ); tr_lockFree( web->taskLock ); tr_free( web ); session->web = NULL; }
static void tr_webThreadFunc( void * vsession ) { CURLM * multi; struct tr_web * web; int taskCount = 0; struct tr_web_task * task; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if( curl_global_init( CURL_GLOBAL_SSL ) ) curl_global_init( 0 ); web = tr_new0( struct tr_web, 1 ); web->close_mode = ~0; web->taskLock = tr_lockNew( ); web->tasks = NULL; web->curl_verbose = getenv( "TR_CURL_VERBOSE" ) != NULL; web->cookie_filename = tr_buildPath( session->configDir, "cookies.txt", NULL ); multi = curl_multi_init( ); session->web = web; for( ;; ) { long msec; int unused; CURLMsg * msg; CURLMcode mcode; if( web->close_mode == TR_WEB_CLOSE_NOW ) break; if( ( web->close_mode == TR_WEB_CLOSE_WHEN_IDLE ) && ( web->tasks == NULL ) ) break; /* add tasks from the queue */ tr_lockLock( web->taskLock ); while( web->tasks != NULL ) { /* pop the task */ task = web->tasks; web->tasks = task->next; task->next = NULL; dbgmsg( "adding task to curl: [%s]", task->url ); curl_multi_add_handle( multi, createEasy( session, web, task )); /*fprintf( stderr, "adding a task.. taskCount is now %d\n", taskCount );*/ ++taskCount; } tr_lockUnlock( web->taskLock ); /* maybe wait a little while before calling curl_multi_perform() */ msec = 0; curl_multi_timeout( multi, &msec ); if( msec < 0 ) msec = THREADFUNC_MAX_SLEEP_MSEC; if( session->isClosed ) msec = 100; /* on shutdown, call perform() more frequently */ if( msec > 0 ) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO( &r_fd_set ); FD_ZERO( &w_fd_set ); FD_ZERO( &c_fd_set ); curl_multi_fdset( multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd ); if( msec > THREADFUNC_MAX_SLEEP_MSEC ) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select( max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t ); } /* call curl_multi_perform() */ do { mcode = curl_multi_perform( multi, &unused ); } while( mcode == CURLM_CALL_MULTI_PERFORM ); /* pump completed tasks from the multi */ while(( msg = curl_multi_info_read( multi, &unused ))) { if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL )) { double total_time; struct tr_web_task * task; long req_bytes_sent; CURL * e = msg->easy_handle; curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task ); curl_easy_getinfo( e, CURLINFO_RESPONSE_CODE, &task->code ); curl_easy_getinfo( e, CURLINFO_REQUEST_SIZE, &req_bytes_sent ); curl_easy_getinfo( e, CURLINFO_TOTAL_TIME, &total_time ); task->did_connect = task->code>0 || req_bytes_sent>0; task->did_timeout = !task->code && ( total_time >= task->timeout_secs ); curl_multi_remove_handle( multi, e ); curl_easy_cleanup( e ); /*fprintf( stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, (int)evbuffer_get_length(task->response) );*/ tr_runInEventThread( task->session, task_finish_func, task ); --taskCount; } } } /* Discard any remaining tasks. * This is rare, but can happen on shutdown with unresponsive trackers. */ while( web->tasks != NULL ) { task = web->tasks; web->tasks = task->next; dbgmsg( "Discarding task \"%s\"", task->url ); task_free( task ); } /* cleanup */ curl_multi_cleanup( multi ); tr_lockFree( web->taskLock ); tr_free( web->cookie_filename ); tr_free( web ); session->web = NULL; }
static void tr_webThreadFunc (void * vsession) { char * str; CURLM * multi; struct tr_web * web; int taskCount = 0; struct tr_web_task * task; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if (curl_global_init (CURL_GLOBAL_SSL)) curl_global_init (0); web = tr_new0 (struct tr_web, 1); web->close_mode = ~0; web->taskLock = tr_lockNew (); web->tasks = NULL; web->curl_verbose = getenv ("TR_CURL_VERBOSE") != NULL; web->curl_ssl_verify = getenv ("TR_CURL_SSL_VERIFY") != NULL; web->curl_ca_bundle = getenv ("CURL_CA_BUNDLE"); if (web->curl_ssl_verify) { tr_logAddNamedInfo ("web", "will verify tracker certs using envvar CURL_CA_BUNDLE: %s", web->curl_ca_bundle == NULL ? "none" : web->curl_ca_bundle); tr_logAddNamedInfo ("web", "NB: this only works if you built against libcurl with openssl or gnutls, NOT nss"); tr_logAddNamedInfo ("web", "NB: invalid certs will show up as 'Could not connect to tracker' like many other errors"); } str = tr_buildPath (session->configDir, "cookies.txt", NULL); if (tr_fileExists (str, NULL)) web->cookie_filename = tr_strdup (str); tr_free (str); multi = curl_multi_init (); session->web = web; for (;;) { long msec; int unused; CURLMsg * msg; CURLMcode mcode; if (web->close_mode == TR_WEB_CLOSE_NOW) break; if ((web->close_mode == TR_WEB_CLOSE_WHEN_IDLE) && (web->tasks == NULL)) break; /* add tasks from the queue */ tr_lockLock (web->taskLock); while (web->tasks != NULL) { /* pop the task */ task = web->tasks; web->tasks = task->next; task->next = NULL; dbgmsg ("adding task to curl: [%s]", task->url); curl_multi_add_handle (multi, createEasy (session, web, task)); /*fprintf (stderr, "adding a task.. taskCount is now %d\n", taskCount);*/ ++taskCount; } tr_lockUnlock (web->taskLock); /* unpause any paused curl handles */ if (paused_easy_handles != NULL) { CURL * handle; tr_list * tmp; /* swap paused_easy_handles to prevent oscillation between writeFunc this while loop */ tmp = paused_easy_handles; paused_easy_handles = NULL; while ((handle = tr_list_pop_front (&tmp))) curl_easy_pause (handle, CURLPAUSE_CONT); } /* maybe wait a little while before calling curl_multi_perform () */ msec = 0; curl_multi_timeout (multi, &msec); if (msec < 0) msec = THREADFUNC_MAX_SLEEP_MSEC; if (session->isClosed) msec = 100; /* on shutdown, call perform () more frequently */ if (msec > 0) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO (&r_fd_set); FD_ZERO (&w_fd_set); FD_ZERO (&c_fd_set); curl_multi_fdset (multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd); if (msec > THREADFUNC_MAX_SLEEP_MSEC) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select (max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t); } /* call curl_multi_perform () */ do mcode = curl_multi_perform (multi, &unused); while (mcode == CURLM_CALL_MULTI_PERFORM); /* pump completed tasks from the multi */ while ((msg = curl_multi_info_read (multi, &unused))) { if ((msg->msg == CURLMSG_DONE) && (msg->easy_handle != NULL)) { double total_time; struct tr_web_task * task; long req_bytes_sent; CURL * e = msg->easy_handle; curl_easy_getinfo (e, CURLINFO_PRIVATE, (void*)&task); assert (e == task->curl_easy); curl_easy_getinfo (e, CURLINFO_RESPONSE_CODE, &task->code); curl_easy_getinfo (e, CURLINFO_REQUEST_SIZE, &req_bytes_sent); curl_easy_getinfo (e, CURLINFO_TOTAL_TIME, &total_time); task->did_connect = task->code>0 || req_bytes_sent>0; task->did_timeout = !task->code && (total_time >= task->timeout_secs); curl_multi_remove_handle (multi, e); tr_list_remove_data (&paused_easy_handles, e); curl_easy_cleanup (e); tr_runInEventThread (task->session, task_finish_func, task); --taskCount; } } } /* Discard any remaining tasks. * This is rare, but can happen on shutdown with unresponsive trackers. */ while (web->tasks != NULL) { task = web->tasks; web->tasks = task->next; dbgmsg ("Discarding task \"%s\"", task->url); task_free (task); } /* cleanup */ tr_list_free (&paused_easy_handles, NULL); curl_multi_cleanup (multi); tr_lockFree (web->taskLock); tr_free (web->cookie_filename); tr_free (web); session->web = NULL; }