Changeset 9990


Ignore:
Timestamp:
Jan 22, 2010, 2:40:11 AM (12 years ago)
Author:
charles
Message:

(trunk libT) #2781 "1.80 freezes on startup" -- possible fix. implement nonblocking dns before we hand the URLs over to libcurl

Location:
trunk/libtransmission
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/trevent.c

    r9868 r9990  
    1919#include <signal.h>
    2020
     21#include <event.h>
     22#include <evdns.h>
     23
    2124#include "transmission.h"
    2225#include "net.h"
     
    218221    eh->base = event_init( );
    219222    eh->session->events = eh;
     223    evdns_init( );
     224
    220225
    221226    /* listen to the pipe's read fd */
  • trunk/libtransmission/web.c

    r9911 r9990  
    1515#include <curl/curl.h>
    1616#include <event.h>
     17#include <evdns.h>
    1718
    1819#include "transmission.h"
     20#include "list.h"
    1921#include "net.h"
    2022#include "session.h"
     
    5860    tr_session * session;
    5961    tr_address addr;
     62    tr_list * dns_cache;
    6063    struct event timer_event;
    6164};
    6265
     66struct dns_cache_item;
     67static void dns_cache_item_free( struct dns_cache_item * );
     68
    6369static void
    6470web_free( tr_web * g )
     
    6672    curl_multi_cleanup( g->multi );
    6773    evtimer_del( &g->timer_event );
     74    tr_list_free( &g->dns_cache, (TrListForeachFunc)dns_cache_item_free );
    6875    memset( g, TR_MEMORY_TRASH, sizeof( struct tr_web ) );
    6976    tr_free( g );
     
    7986    struct evbuffer * response;
    8087    char * url;
     88    char * host;
     89    const char * resolved_host;
    8190    char * range;
    8291    tr_session * session;
     
    93102    evtimer_del( &task->timer_event );
    94103    evbuffer_free( task->response );
     104    tr_free( task->host );
    95105    tr_free( task->range );
    96106    tr_free( task->url );
    97107    memset( task, TR_MEMORY_TRASH, sizeof( struct tr_web_task ) );
    98108    tr_free( task );
     109}
     110
     111/***
     112****
     113***/
     114
     115struct dns_cache_item
     116{
     117    char * host;
     118    char * resolved_host;
     119    time_t expiration;
     120};
     121
     122static void
     123dns_cache_item_free( struct dns_cache_item * item )
     124{
     125    tr_free( item->host );
     126    tr_free( item->resolved_host );
     127    tr_free( item );
     128}
     129
     130static const char *
     131dns_get_cached_host( struct tr_web_task * task, const char * host )
     132{
     133    tr_list * l;
     134    tr_web * g = task->session->web;
     135    struct dns_cache_item * item = NULL;
     136
     137    if( g != NULL )
     138    {
     139        /* do we have it cached? */
     140        for( l=g->dns_cache; l!=NULL; l=l->next ) {
     141            struct dns_cache_item * tmp = l->data;
     142            if( !strcmp( host, tmp->host ) ) {
     143                item = tmp;
     144                break;
     145            }
     146        }
     147
     148        /* has the ttl expired? */
     149        if( ( item != NULL ) && ( item->expiration <= tr_time( ) ) ) {
     150            tr_list_remove_data( &g->dns_cache, item );
     151            dns_cache_item_free( item );
     152            item = NULL;
     153        }
     154    }
     155
     156    if( item != NULL )
     157        dbgmsg( "found cached dns entry for \"%s\": %s", host, item->resolved_host );
     158
     159    return item ? item->resolved_host : NULL;
     160}
     161
     162static const char*
     163dns_set_cached_host( struct tr_web_task * task, const char * host, const char * resolved, int ttl )
     164{
     165    char * ret = NULL;
     166    tr_web * g;
     167
     168    assert( task != NULL );
     169    assert( host != NULL );
     170    assert( resolved != NULL );
     171    assert( ttl >= 0 );
     172
     173    g = task->session->web;
     174    if( g != NULL )
     175    {
     176        struct dns_cache_item * item = tr_new( struct dns_cache_item, 1 );
     177        item->host = tr_strdup( host );
     178        item->resolved_host = tr_strdup( resolved );
     179        item->expiration = tr_time( ) + ttl;
     180        tr_list_append( &g->dns_cache, item );
     181        ret = item->resolved_host;
     182        dbgmsg( "adding dns cache entry for \"%s\": %s", host, resolved );
     183    }
     184    return ret;
    99185}
    100186
     
    164250        const long verbose = getenv( "TR_CURL_VERBOSE" ) != NULL;
    165251        const char * user_agent = TR_NAME "/" LONG_VERSION_STRING;
    166 
    167         dbgmsg( "adding task #%lu [%s]", task->tag, task->url );
     252        char * url = NULL;
     253
     254        /* If we've got a resolved host, insert it into the URL: replace
     255         * "http://www.craptrackular.org/announce?key=val&key2=..." with
     256         * "http://127.0.0.1/announce?key=val&key2=..."
     257         * so that curl's DNS won't block */
     258        if( task->resolved_host != NULL )
     259        {
     260            struct evbuffer * buf = evbuffer_new( );
     261            char * pch = strstr( task->url, task->host );
     262            char * tail = pch + strlen( task->host );
     263            evbuffer_add( buf, task->url, pch - task->url );
     264            evbuffer_add_printf( buf, "%s", task->resolved_host );
     265            evbuffer_add_printf( buf, "%s", tail );
     266            url = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
     267            dbgmsg( "old url: \"%s\" -- new url: \"%s\"", task->url, url );
     268            evbuffer_free( buf );
     269        }
     270
     271        dbgmsg( "adding task #%lu [%s]", task->tag, url ? url : task->url );
    168272
    169273        if( !task->range && session->isProxyEnabled ) {
     
    203307        curl_easy_setopt( e, CURLOPT_SSL_VERIFYHOST, 0L );
    204308        curl_easy_setopt( e, CURLOPT_SSL_VERIFYPEER, 0L );
    205         curl_easy_setopt( e, CURLOPT_URL, task->url );
     309        curl_easy_setopt( e, CURLOPT_URL, url ? url : task->url );
    206310        curl_easy_setopt( e, CURLOPT_USERAGENT, user_agent );
    207311        curl_easy_setopt( e, CURLOPT_VERBOSE, verbose );
     
    213317        if( curl_multi_add_handle( web->multi, e ) == CURLM_OK )
    214318            ++web->taskCount;
    215     }
     319
     320        tr_free( url );
     321    }
     322}
     323
     324static void
     325dns_ipv6_done_cb( int err, char type, int count, int ttl, void * addresses, void * vtask )
     326{
     327    struct tr_web_task * task = vtask;
     328
     329    if( !err && ( task->host != NULL ) && ( count > 0 ) && ( ttl >= 0 ) && ( type == DNS_IPv6_AAAA ) )
     330    {
     331        int i;
     332        char buf[INET6_ADDRSTRLEN+1];
     333        struct in6_addr *in6_addrs = addresses;
     334
     335        for( i=0; i<count; ++i ) {
     336            const char * b = inet_ntop(AF_INET6, &in6_addrs[i], buf,sizeof(buf));
     337            if( b != NULL ) {
     338                /* FIXME: is there a better way to tell which one to use if count > 1? */
     339                task->resolved_host = dns_set_cached_host( task, task->host, b, ttl );
     340                break;
     341            }
     342        }
     343    }
     344
     345    addTask( task );
     346}
     347
     348static void
     349dns_ipv4_done_cb( int err, char type, int count, int ttl, void * addresses, void * vtask )
     350{
     351    struct tr_web_task * task = vtask;
     352
     353    if( !err && ( task->host != NULL ) && ( count > 0 ) && ( ttl >= 0 ) && ( type == DNS_IPv4_A ) )
     354    {
     355        struct in_addr * in_addrs = addresses;
     356        const char * resolved = inet_ntoa( in_addrs[0] );
     357        task->resolved_host = dns_set_cached_host( task, task->host, resolved, ttl );
     358        /* FIXME: if count > 1, is there a way to decide which is best to use? */
     359    }
     360
     361    if( task->resolved_host || evdns_resolve_ipv6( task->host, 0, dns_ipv6_done_cb, task ) )
     362        dns_ipv6_done_cb( DNS_ERR_UNKNOWN, DNS_IPv6_AAAA, 0, 0, NULL, task );
     363}
     364
     365static void
     366doDNS( void * vtask )
     367{
     368    char * host = NULL;
     369    struct tr_web_task * task = vtask;
     370
     371    assert( task->resolved_host == NULL );
     372
     373    if( !tr_httpParseURL( task->url, -1, &host, NULL, NULL ) ) {
     374        task->host = host;
     375        task->resolved_host = dns_get_cached_host( task, host );
     376    }
     377
     378    if( task->resolved_host || evdns_resolve_ipv4( host, 0, dns_ipv4_done_cb, task ) )
     379        dns_ipv4_done_cb( DNS_ERR_UNKNOWN, DNS_IPv4_A, 0, 0, NULL, task );
    216380}
    217381
     
    396560        task->tag = ++tag;
    397561        task->response = evbuffer_new( );
    398         tr_runInEventThread( session, addTask, task );
     562        tr_runInEventThread( session, doDNS, task );
    399563    }
    400564}
Note: See TracChangeset for help on using the changeset viewer.