Exemple #1
0
static tr_tristate_t
receiveresponse( tr_http_t * http )
{
    int    ret, before;
    void * newbuf;

    if( 0 == http->date )
    {
        http->date = tr_date();
    }

    before = http->header.used;
    for(;;)
    {
        if( http->header.size - http->header.used < HTTP_BUFSIZE )
        {
            newbuf = realloc( http->header.buf,
                              http->header.size + HTTP_BUFSIZE );
            if( NULL == newbuf )
            {
                return TR_ERROR;
            }
            http->header.buf = newbuf;
            http->header.size += HTTP_BUFSIZE;
        }

        ret = tr_netRecv( http->sock,
                          (uint8_t *) ( http->header.buf + http->header.used ),
                          http->header.size - http->header.used );
        if( ret & TR_NET_CLOSE )
        {
            checklength( http );
            return TR_OK;
        }
        else if( ret & TR_NET_BLOCK )
        {
            break;
        }
        else
        {
            http->header.used += ret;
        }
    }

    if( before < http->header.used && checklength( http ) )
    {
        return TR_OK;
    }

    if( tr_date() > HTTP_TIMEOUT + http->date )
    {
        return TR_ERROR;
    }

    return TR_WAIT;
}
Exemple #2
0
void
tr_upnpPulse( tr_upnp_t * upnp )
{
    tr_upnp_device_t ** ii;

    tr_lockLock( &upnp->lock );

    if( upnp->active )
    {
        /* pulse on all known devices */
        upnp->discovering = 1;
        for( ii = &upnp->devices; NULL != *ii; ii = &(*ii)->next )
        {
            if( devicePulse( *ii, upnp->fdlimit, upnp->port ) )
            {
                upnp->discovering = 0;
            }
        }

        /* send an SSDP discover message */
        if( upnp->discovering &&
            upnp->lastdelay + upnp->lastdiscover < tr_date() )
        {
            upnp->outfd = sendSSDP( upnp->fdlimit, upnp->outfd );
            upnp->lastdiscover = tr_date();
            upnp->lastdelay = MIN( upnp->lastdelay * 2, SSDP_MAX_DELAY );
        }

        /* try to receive SSDP messages */
        watchSSDP( &upnp->devices, upnp->infd );
        if( watchSSDP( &upnp->devices, upnp->outfd ) )
        {
            killSock( upnp->fdlimit, &upnp->outfd );
        }
    }
    else
    {
        /* delete all mappings then delete devices */
        ii = &upnp->devices;
        while( NULL != *ii )
        {
            if( deviceStop( *ii ) )
            {
                deviceRemove( ii, upnp->fdlimit );
            }
            else
            {
                devicePulse( *ii, upnp->fdlimit, 0 );
                ii = &(*ii)->next;
            }
        }
    }

    tr_lockUnlock( &upnp->lock );
}
static int
sendreq( tr_natpmp_req_t * req )
{
    uint8_t buf[12];
    int res;

    memset( buf, 0, sizeof( buf ) );
    buf[0] = PMP_VERSION;
    buf[1] = PMP_OPCODE_ADDTCP;
    PMP_TOBUF16( buf + 4, req->askport );
    if( req->adding )
    {
        PMP_TOBUF16( buf + 6, req->askport );
        PMP_TOBUF32( buf + 8, PMP_LIFETIME );
    }

    res = tr_netSend( req->fd, buf, sizeof( buf ) );
    if( TR_NET_CLOSE & res && EHOSTUNREACH == errno )
    {
        res = TR_NET_BLOCK;
    }
    if( TR_NET_CLOSE & res )
    {
        tr_err( "failed to send nat-pmp request (%s)", strerror( errno ) );
        return 1;
    }
    else if( !( TR_NET_BLOCK & res ) )
    {
        /* XXX is it all right to assume the entire thing is written? */
        req->retry  = tr_date() + req->delay;
        req->delay *= 2;
    }
    return 0;
}
/***********************************************************************
 * rateForInterval
 ***********************************************************************
 * Returns the transfer rate in KB/s on the last 'interval'
 * milliseconds
 **********************************************************************/
static float rateForInterval( tr_ratecontrol_t * r, int interval )
{
    tr_transfer_t * t = NULL;
    uint64_t now, start;
    int i, total;

    now = tr_date();
    start = now - interval;

    /* Browse the history back in time */
    total = 0;
    for( i = r->transferStop; i != r->transferStart; i-- )
    {
        t = &r->transfers[i];
        if( t->date < start )
            break;

        total += t->size;

        if( !i )
            i = HISTORY_SIZE; /* Loop */
    }
    if( ( r->transferStop + 1 ) % HISTORY_SIZE == r->transferStart
        && i == r->transferStart )
    {
        /* High bandwidth -> the history isn't big enough to remember
         * everything transferred since 'interval' ms ago. Correct the
         * interval so that we return the correct rate */
        interval = now - t->date;
    }

    return ( 1000.0f / 1024.0f ) * total / interval;
}
Exemple #5
0
void ui_set_datetime(struct tm *tick_time, TimeUnits units_changed) {
    if (units_changed & MINUTE_UNIT) {
        strftime(ui.texts.time, sizeof(ui.texts.time), clock_is_24h_style() ? "%H:%M" : "%I:%M", tick_time);

        //Remove leading 0
        if (ui.texts.time[0] == '0') {
            memmove(ui.texts.time, &ui.texts.time[1], sizeof(ui.texts.time) - 1);
        }
    }

    if (units_changed & DAY_UNIT) {
        tr_date(tick_time);
    }

    //TODO figure out how to make the translations accept the char* passed in. strftime must not like it
    if (units_changed & MONTH_UNIT) {
        tr_month(tick_time);
    }

    tr_am_pm(tick_time);

    //For pretty screenshots
    //SCREENSHOT_0 strncpy(ui.texts.time, "12:00", sizeof(ui.texts.time));
    //SCREENSHOT_0 strncpy(ui.texts.date, "Thu 28", sizeof(ui.texts.date));
    //SCREENSHOT_0 strncpy(ui.texts.month, "May", sizeof(ui.texts.month));
    //SCREENSHOT_1 strncpy(ui.texts.time, "6:30", sizeof(ui.texts.time));
    //SCREENSHOT_1 strncpy(ui.texts.date, "Thu 28", sizeof(ui.texts.date));
    //SCREENSHOT_1 strncpy(ui.texts.month, "February", sizeof(ui.texts.month));
    //SCREENSHOT_2 strncpy(ui.texts.time, "9:00", sizeof(ui.texts.time));
    //SCREENSHOT_2 strncpy(ui.texts.date, "Fri 27", sizeof(ui.texts.date));
    //SCREENSHOT_2 strncpy(ui.texts.month, "May", sizeof(ui.texts.month));
    //SCREENSHOT_3 strncpy(ui.texts.time, "9:00", sizeof(ui.texts.time));
    //SCREENSHOT_3 strncpy(ui.texts.date, "Fri 27", sizeof(ui.texts.date));
    //SCREENSHOT_3 strncpy(ui.texts.month, "May", sizeof(ui.texts.month));
    //SCREENSHOT_4 strncpy(ui.texts.time, "12:15", sizeof(ui.texts.time));
    //SCREENSHOT_4 strncpy(ui.texts.date, "Mon 5", sizeof(ui.texts.date));
    //SCREENSHOT_4 strncpy(ui.texts.month, "November", sizeof(ui.texts.month));

    text_layer_set_text(ui.layers.time, ui.texts.time);
    text_layer_set_text(ui.layers.date, ui.texts.date);
    text_layer_set_text(ui.layers.month, ui.texts.month);
    ui_set_info(STATUS_ITEMS_AMPM, ui.texts.ampm);

    //Update day/night flag
    if (config.sunrise > 0 && config.sunset > 0) {
        int minutes = tick_time->tm_hour * 60 + tick_time->tm_min;

        if (config.sunrise <= minutes && minutes < config.sunset) {
            is_day = true;
        }
        else {
            is_day = false;
        }
    }
    else {
        is_day = true;
    }
}
Exemple #6
0
void
tr_sessionClose( tr_session * session )
{
    int            i;
    const int      maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
    const uint64_t deadline = tr_date( ) + maxwait_msec;

    assert( tr_isSession( session ) );

    dbgmsg( "shutting down transmission session %p", session );

    /* close the session */
    tr_runInEventThread( session, tr_closeAllConnections, session );
    while( !session->isClosed && !deadlineReached( deadline ) )
    {
        dbgmsg(
            "waiting for the shutdown commands to run in the main thread" );
        tr_wait( 100 );
    }

    /* "shared" and "tracker" have live sockets,
     * so we need to keep the transmission thread alive
     * for a bit while they tell the router & tracker
     * that we're closing now */
    while( ( session->shared
           || session->tracker ) && !deadlineReached( deadline ) )
    {
        dbgmsg( "waiting on port unmap (%p) or tracker (%p)",
                session->shared, session->tracker );
        tr_wait( 100 );
    }

    tr_fdClose( );

    /* close the libtransmission thread */
    tr_eventClose( session );
    while( session->events && !deadlineReached( deadline ) )
    {
        dbgmsg( "waiting for the libevent thread to shutdown cleanly" );
        tr_wait( 100 );
    }

    /* free the session memory */
    tr_bandwidthFree( session->bandwidth );
    tr_lockFree( session->lock );
    for( i = 0; i < session->metainfoLookupCount; ++i )
        tr_free( session->metainfoLookup[i].filename );
    tr_free( session->metainfoLookup );
    tr_free( session->tag );
    tr_free( session->configDir );
    tr_free( session->resumeDir );
    tr_free( session->torrentDir );
    tr_free( session->downloadDir );
    tr_free( session->proxy );
    tr_free( session->proxyUsername );
    tr_free( session->proxyPassword );
    tr_free( session );
}
static void
resetreq( tr_natpmp_req_t * req )
{
    uint64_t now;

    now          = tr_date();
    req->delay   = PMP_INITIAL_DELAY;
    req->retry   = now;
    req->timeout = now + PMP_TOTAL_DELAY;
}
static int shouldConnect( tr_tracker_t * tc )
{
    uint64_t now = tr_date();

    /* Unreachable tracker, try 10 seconds before trying again */
    if( tc->lastAttempt == TC_ATTEMPT_NOREACH &&
            now < tc->dateTry + 10000 )
    {
        return 0;
    }

    /* The tracker rejected us (like 4XX code, unauthorized IP...),
       don't hammer it - we'll probably get the same answer next time
       anyway */
    if( tc->lastAttempt == TC_ATTEMPT_ERROR &&
            now < tc->dateTry + 1000 * tc->interval )
    {
        return 0;
    }

    /* Do we need to send an event? */
    if( tc->started || tc->completed || tc->stopped || 0 < tc->newPort )
    {
        return 1;
    }

    /* Should we try and get more peers? */
    if( now > tc->dateOk + 1000 * tc->interval )
    {
        return 1;
    }

    /* If there is quite a lot of people on this torrent, stress
       the tracker a bit until we get a decent number of peers */
    if( tc->hasManyPeers )
    {
        if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
        {
            return 1;
        }
        if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
        {
            return 1;
        }
        if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
        {
            return 1;
        }
    }

    return 0;
}
Exemple #9
0
static tr_tristate_t
sendrequest( tr_http_t * http )
{
    struct buf * buf;
    int          ret;

    if( 0 == http->date )
    {
        http->date = tr_date();
    }

    if( 0 > http->sock || tr_date() > http->date + HTTP_TIMEOUT )
    {
        return TR_ERROR;
    }

    buf = ( 0 < http->header.used ? &http->header : &http->body );
    while( 0 < buf->used )
    {
      ret = tr_netSend( http->sock, (uint8_t *) buf->buf, buf->used );
        if( ret & TR_NET_CLOSE )
        {
            return TR_ERROR;
        }
        else if( ret & TR_NET_BLOCK )
        {
            return TR_WAIT;
        }
        buf->used = 0;
        buf = &http->body;
    }

    free( http->body.buf );
    http->body.buf = NULL;
    http->body.size = 0;
    http->date = 0;

    return TR_OK;
}
Exemple #10
0
static void
bytesUsed( struct bratecontrol * r, size_t size )
{
    const uint64_t now = tr_date ( );

    if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now )
        r->transfers[r->newest].size += size;
    else
    {
        if( ++r->newest == HISTORY_SIZE ) r->newest = 0;
        r->transfers[r->newest].date = now;
        r->transfers[r->newest].size = size;
    }
}
Exemple #11
0
static float
getSpeed( const struct bratecontrol * r, int interval_msec )
{
    uint64_t       bytes = 0;
    const uint64_t cutoff = tr_date ( ) - interval_msec;
    int            i = r->newest;

    for( ;; )
    {
        if( r->transfers[i].date <= cutoff )
            break;

        bytes += r->transfers[i].size;

        if( --i == -1 ) i = HISTORY_SIZE - 1; /* circular history */
        if( i == r->newest ) break; /* we've come all the way around */
    }

    return ( bytes / 1024.0 ) * ( 1000.0 / interval_msec );
}
void tr_rcTransferred( tr_ratecontrol_t * r, int size )
{
    tr_transfer_t * t;

    if( size < 100 )
    {
        /* Don't count small messages */
        return;
    }
    
    tr_lockLock( &r->lock );

    r->transferStop = ( r->transferStop + 1 ) % HISTORY_SIZE;
    if( r->transferStop == r->transferStart )
        /* History is full, forget about the first (oldest) item */
        r->transferStart = ( r->transferStart + 1 ) % HISTORY_SIZE;

    t = &r->transfers[r->transferStop];
    t->date = tr_date();
    t->size = size;

    tr_lockUnlock( &r->lock );
}
Exemple #13
0
/* returns an fd on success, or a -1 on failure and sets errno */
int
tr_fdFileCheckout( const char * folder,
                   const char * torrentFile,
                   int          doWrite,
                   int          doPreallocate,
                   uint64_t     desiredFileSize )
{
    int                  i, winner = -1;
    struct tr_openfile * o;
    char               * filename;

    assert( folder && *folder );
    assert( torrentFile && *torrentFile );
    assert( doWrite == 0 || doWrite == 1 );

    filename = tr_buildPath( folder, torrentFile, NULL );
    dbgmsg( "looking for file '%s', writable %c", filename,
            doWrite ? 'y' : 'n' );

    tr_lockLock( gFd->lock );

    /* Is it already open? */
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
    {
        o = &gFd->open[i];

        if( !fileIsOpen( o ) )
            continue;

        if( strcmp( filename, o->filename ) )
            continue;

        if( fileIsCheckedOut( o ) )
        {
            dbgmsg( "found it!  it's open, but checked out.  waiting..." );
            tr_lockUnlock( gFd->lock );
            tr_wait( 200 );
            tr_lockLock( gFd->lock );
            i = -1; /* reloop */
            continue;
        }

        if( doWrite && !o->isWritable )
        {
            dbgmsg(
                "found it!  it's open and available, but isn't writable. closing..." );
            TrCloseFile( i );
            break;
        }

        dbgmsg( "found it!  it's ready for use!" );
        winner = i;
        break;
    }

    dbgmsg(
        "it's not already open.  looking for an open slot or an old file." );
    while( winner < 0 )
    {
        uint64_t date = tr_date( ) + 1;

        /* look for the file that's been open longest */
        for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
        {
            o = &gFd->open[i];

            if( !fileIsOpen( o ) )
            {
                winner = i;
                dbgmsg( "found an empty slot in %d", winner );
                break;
            }

            if( date > o->date )
            {
                date = o->date;
                winner = i;
            }
        }

        if( winner >= 0 )
        {
            if( fileIsOpen( &gFd->open[winner] ) )
            {
                dbgmsg( "closing file '%s', slot #%d",
                        gFd->open[winner].filename,
                        winner );
                TrCloseFile( winner );
            }
        }
        else
        {
            dbgmsg(
                "everything's full!  waiting for someone else to finish something" );
            tr_lockUnlock( gFd->lock );
            tr_wait( 200 );
            tr_lockLock( gFd->lock );
        }
    }

    assert( winner >= 0 );
    o = &gFd->open[winner];
    if( !fileIsOpen( o ) )
    {
        const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize );
        if( err ) {
            tr_lockUnlock( gFd->lock );
            tr_free( filename );
            errno = err;
            return -1;
        }

        dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
                doWrite ? 'y' : 'n' );
        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
        o->isWritable = doWrite;
    }

    dbgmsg( "checking out '%s' in slot %d", filename, winner );
    o->isCheckedOut = 1;
    o->closeWhenDone = 0;
    o->date = tr_date( );
    tr_free( filename );
    tr_lockUnlock( gFd->lock );
    return o->fd;
}
Exemple #14
0
static int
deadlineReached( const uint64_t deadline )
{
    return tr_date( ) >= deadline;
}
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 );
}
static void sendQuery( tr_tracker_t * tc )
{
    tr_torrent_t * tor = tc->tor;
    tr_info_t    * inf = &tor->info;

    char     * event;
    uint64_t   left;
    int        ret;
    uint64_t   down;
    uint64_t   up;

    down = tor->downloaded - tc->download;
    up = tor->uploaded - tc->upload;
    if( tc->started )
    {
        event = "&event=started";
        down = up = 0;

        if( 0 < tc->newPort )
        {
            tc->bindPort = tc->newPort;
            tc->newPort = -1;
        }
    }
    else if( tc->completed )
    {
        event = "&event=completed";
    }
    else if( tc->stopped || 0 < tc->newPort )
    {
        event = "&event=stopped";
    }
    else
    {
        event = "";
    }

    left = tr_cpLeftBytes( tor->completion );

    ret = snprintf( (char *) tc->buf, tc->size,
                    "GET %s?"
                    "info_hash=%s&"
                    "peer_id=%s&"
                    "port=%d&"
                    "uploaded=%"PRIu64"&"
                    "downloaded=%"PRIu64"&"
                    "left=%"PRIu64"&"
                    "compact=1&"
                    "numwant=50&"
                    "key=%s"
                    "%s "
                    "HTTP/1.1\r\n"
                    "Host: %s\r\n"
                    "User-Agent: Transmission/%d.%d\r\n"
                    "Connection: close\r\n\r\n",
                    inf->trackerAnnounce, tor->hashString, tc->id,
                    tc->bindPort, up, down,
                    left, tor->key, event, inf->trackerAddress,
                    VERSION_MAJOR, VERSION_MINOR );

    ret = tr_netSend( tc->socket, tc->buf, ret );
    if( ret & TR_NET_CLOSE )
    {
        tr_inf( "Tracker: connection failed" );
        tr_netClose( tc->socket );
        tr_fdSocketClosed( tor->fdlimit, 1 );
        tc->status  = TC_STATUS_IDLE;
        tc->dateTry = tr_date();
    }
    else if( !( ret & TR_NET_BLOCK ) )
    {
        // printf( "Tracker: sent %s", tc->buf );
        tc->status = TC_STATUS_RECV;
        tc->pos    = 0;
    }
}
Exemple #18
0
static int
devicePulseHttp( tr_upnp_device_t * dev, tr_fd_t * fdlimit,
                 const char ** body, int * len )
{
    const char * headers;
    int          hlen, code;

    if( NULL == dev->http )
    {
        if( tr_date() < dev->lastrequest + HTTP_REQUEST_INTERVAL )
        {
            return -1;
        }
        dev->lastrequest = tr_date();
        dev->http = devicePulseGetHttp( dev, fdlimit );
        if( NULL == dev->http )
        {
            tr_dbg( "upnp device %s: http init failed, state %hhu -> error",
                    dev->host, dev->state );
            dev->state = UPNPDEV_STATE_ERROR;
            dev->soapretry = 0;
            return -1;
        }
    }

    if( NULL == dev->myaddr )
    {
        dev->myaddr = tr_httpWhatsMyAddress( dev->http );
    }

    switch( tr_httpPulse( dev->http, &headers, &hlen ) )
    {
        case TR_OK:
            code = tr_httpResponseCode( headers, hlen );
            if( SOAP_METHOD_NOT_ALLOWED == code && !dev->soapretry )
            {
                dev->soapretry = 1;
                killHttp( fdlimit, &dev->http );
                break;
            }
            dev->soapretry = 0;
            *body = tr_httpParse( headers, hlen, NULL );
            *len = ( NULL == body ? 0 : hlen - ( *body - headers ) );
            return code;
        case TR_ERROR:
            killHttp( fdlimit, &dev->http );
            if( dev->soapretry )
            {
                tr_dbg( "upnp device %s: http pulse failed, state %hhu -> error",
                        dev->host, dev->state );
                dev->state = UPNPDEV_STATE_ERROR;
                dev->soapretry = 0;
            }
            else
            {
                dev->soapretry = 1;
            }
            break;
        case TR_WAIT:
            break;
    }

    return -1;
}
Exemple #19
0
static tr_tristate_t
pulsereq( tr_natpmp_t * pmp )
{
    tr_natpmp_req_t  * req = pmp->req;
    struct sockaddr_in sin;
    uint8_t            buf[16];
    int                res;
    uint64_t           now;
    tr_tristate_t      ret;
    tr_natpmp_parse_t  parse;

    now = tr_date();
    /* check for timeout */
    if( now >= req->timeout )
    {
        tr_dbg( "nat-pmp request timed out" );
        req->nobodyhome = 1;
        return TR_NET_ERROR;
    }
    /* send another request  if it's been long enough */
    if( now >= req->retry && sendreq( req ) )
    {
        return TR_NET_ERROR;
    }

    /* check for incoming packets */
    res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
    if( TR_NET_BLOCK & res )
    {
        return TR_NET_WAIT;
    }
    else if( TR_NET_CLOSE & res )
    {
        if( ECONNRESET == errno || ECONNREFUSED == errno )
        {
            tr_dbg( "nat-pmp not supported by device" );
            req->nobodyhome = 1;
        }
        else
        {
            tr_inf( "error reading nat-pmp response (%s)", strerror( errno ) );
        }
        return TR_NET_ERROR;
    }

    /* parse the packet */
    tr_dbg( "nat-pmp read %i byte response", res );
    ret = parseresponse( buf, res, req->askport, &parse );
    req->tmpfail = parse.tmpfail;
    /* check for device reset */
    if( checktime( &pmp->uptime, parse.seconds ) )
    {
        pmp->renew = 0;
        tr_inf( "detected nat-pmp device reset" );
        resetreq( req );
        ret = TR_NET_WAIT;
    }
    if( TR_NET_OK == ret && req->adding )
    {
        if( req->askport != parse.port )
        {
            tr_dbg( "nat-pmp received %i for public port instead of %i",
                    parse.port, req->askport );
            req->gotport = parse.port;
        }
        tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime );
        pmp->renew = now + ( parse.lifetime / 2 * 1000 );
    }

    return ret;
}
Exemple #20
0
void
tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort )
{
    if( 0 <= pmp->mcastfd )
    {
        mcastpulse( pmp );
    }

    if( NULL != publicPort )
    {
        *publicPort = -1;
    }

    if( pmp->active || PMP_STATE_DELETING == pmp->state )
    {
        switch( pmp->state )
        {
            case PMP_STATE_IDLE:
            case PMP_STATE_TMPFAIL:
                if( 0 < pmp->newport )
                {
                    tr_dbg( "nat-pmp state %s -> add with port %i",
                            ( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ),
                            pmp->newport );
                    pmp->state = PMP_STATE_ADDING;
                }
                break;

            case PMP_STATE_ADDING:
                if( NULL == pmp->req )
                {
                    if( 0 >= pmp->newport )
                    {
                        tr_dbg( "nat-pmp state add -> idle, no port" );
                        pmp->state = PMP_STATE_IDLE;
                    }
                    else if( INADDR_NONE == pmp->dest.s_addr )
                    {
                        tr_dbg( "nat-pmp state add -> fail, no default route" );
                        pmp->state = PMP_STATE_FAILED;
                    }
                    else
                    {
                        pmp->req = newreq( 1, pmp->dest, pmp->newport );
                        if( NULL == pmp->req )
                        {
                            pmp->state = PMP_STATE_FAILED;
                            tr_dbg( "nat-pmp state add -> fail on req init" );
                        }
                    }
                }
                if( PMP_STATE_ADDING == pmp->state )
                {
                    switch( pulsereq( pmp ) )
                    {
                        case TR_NET_ERROR:
                            if( pmp->req->nobodyhome )
                            {
                                pmp->state = PMP_STATE_NOBODYHOME;
                                tr_dbg( "nat-pmp state add -> nobodyhome on pulse" );
                            }
                            else if( pmp->req->tmpfail )
                            {
                                pmp->state = PMP_STATE_TMPFAIL;
                                tr_dbg( "nat-pmp state add -> err on pulse" );
                                if( pmp->req->askport == pmp->newport )
                                {
                                    pmp->newport = 0;
                                }
                            }
                            else
                            {
                                pmp->state = PMP_STATE_FAILED;
                                tr_dbg( "nat-pmp state add -> fail on pulse" );
                            }
                            killreq( &pmp->req );
                            break;
                        case TR_NET_OK:
                            pmp->mappedport = pmp->req->gotport;
                            if( pmp->mappedport != pmp->newport &&
                                pmp->newport == pmp->req->askport )
                            {
                                pmp->newport = pmp->req->gotport;
                            }
                            killreq( &pmp->req );
                            pmp->state = PMP_STATE_MAPPED;
                            pmp->mapped = 1;
                            tr_dbg( "nat-pmp state add -> mapped with port %i",
                                    pmp->mappedport);
                            tr_inf( "nat-pmp mapped port %i", pmp->mappedport );
                            if( NULL != publicPort )
                            {
                                *publicPort = pmp->mappedport;
                            }
                            break;
                        case TR_NET_WAIT:
                            break;
                    }
                }
                break;

            case PMP_STATE_DELETING:
                if( NULL == pmp->req )
                {
                    assert( 0 < pmp->mappedport );
                    pmp->req = newreq( 0, pmp->dest, pmp->mappedport );
                    if( NULL == pmp->req )
                    {
                        pmp->state = PMP_STATE_FAILED;
                        tr_dbg( "nat-pmp state del -> fail on req init" );
                    }
                }
                if( PMP_STATE_DELETING == pmp->state )
                {
                    switch( pulsereq( pmp ) )
                    {
                        case TR_NET_ERROR:
                            if( pmp->req->nobodyhome )
                            {
                                pmp->mapped = 0;
                                pmp->state = PMP_STATE_NOBODYHOME;
                                tr_dbg( "nat-pmp state del -> nobodyhome on pulse" );
                            }
                            else if( pmp->req->tmpfail )
                            {
                                pmp->mapped = 0;
                                pmp->state = PMP_STATE_TMPFAIL;
                                tr_dbg( "nat-pmp state del -> err on pulse" );
                                pmp->mappedport = -1;
                            }
                            else
                            {
                                pmp->state = PMP_STATE_FAILED;
                                tr_dbg( "nat-pmp state del -> fail on pulse" );
                            }
                            killreq( &pmp->req );
                            break;
                        case TR_NET_OK:
                            tr_dbg( "nat-pmp state del -> idle with port %i",
                                    pmp->req->askport);
                            tr_inf( "nat-pmp unmapped port %i",
                                    pmp->req->askport );
                            pmp->mapped = 0;
                            pmp->mappedport = -1;
                            killreq( &pmp->req );
                            pmp->state = PMP_STATE_IDLE;
                            break;
                        case TR_NET_WAIT:
                            break;
                    }
                }
                break;

            case PMP_STATE_MAPPED:
                if( pmp->newport != pmp->mappedport )
                {
                    tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
                            pmp->mappedport, pmp->newport );
                    pmp->state = PMP_STATE_DELETING;
                }
                else if( tr_date() > pmp->renew )
                {
                    pmp->state = PMP_STATE_ADDING;
                    tr_dbg( "nat-pmp state mapped -> add for renewal" );
                }
                break;

            case PMP_STATE_FAILED:
            case PMP_STATE_NOBODYHOME:
                break;

            default:
                assert( 0 );
                break;
        }
    }
}
Exemple #21
0
static int
devicePulse( tr_upnp_device_t * dev, tr_fd_t * fdlimit, int port )
{
    const char * body;
    int          len, code;
    uint8_t      laststate;

    switch( dev->state )
    {
        case UPNPDEV_STATE_READY:
            if( 0 < port )
            {
                tr_dbg( "upnp device %s: want mapping, state ready -> get",
                        dev->host );
                dev->mappedport = port;
                dev->state = UPNPDEV_STATE_GET;
                break;
            }
            return 1;
        case UPNPDEV_STATE_MAPPED:
            if( port != dev->mappedport )
            {
                tr_dbg( "upnp device %s: change mapping, "
                        "state mapped -> delete", dev->host );
                dev->state = UPNPDEV_STATE_DEL;
                break;
            }
            if( tr_date() > dev->lastcheck + MAPPING_CHECK_INTERVAL )
            {
                tr_dbg( "upnp device %s: check mapping, "
                        "state mapped -> get", dev->host );
                dev->state = UPNPDEV_STATE_GET;
            }
            return 1;
        case UPNPDEV_STATE_ERROR:
            return 0;
    }

    code = devicePulseHttp( dev, fdlimit, &body, &len );
    if( 0 > code )
    {
        return 1;
    }

    if( LOOP_DETECT_THRESHOLD <= dev->looping )
    {
        tr_dbg( "upnp device %s: loop detected, state %hhu -> error",
                dev->host, dev->state );
        dev->state = UPNPDEV_STATE_ERROR;
        dev->looping = 0;
        killHttp( fdlimit, &dev->http );
        return 1;
    }

    laststate = dev->state;
    dev->state = UPNPDEV_STATE_ERROR;
    switch( laststate ) 
    {
        case UPNPDEV_STATE_ROOT:
            if( !TR_HTTP_STATUS_OK( code ) )
            {
                tr_dbg( "upnp device %s: fetch root failed with http code %i",
                        dev->host, code );
            }
            else if( parseRoot( body, len, &dev->soap, &dev->scpd ) )
            {
                tr_dbg( "upnp device %s: parse root failed", dev->host );
            }
            else
            {
                tr_dbg( "upnp device %s: found scpd \"%s\" and soap \"%s\"",
                        dev->root, dev->scpd, dev->soap );
                tr_dbg( "upnp device %s: parsed root, state root -> scpd",
                        dev->host );
                dev->state = UPNPDEV_STATE_SCPD;
            }
            break;

        case UPNPDEV_STATE_SCPD:
            if( !TR_HTTP_STATUS_OK( code ) )
            {
                tr_dbg( "upnp device %s: fetch scpd failed with http code %i",
                        dev->host, code );
            }
            else if( parseScpd( body, len, &dev->getcmd,
                                &dev->addcmd, &dev->delcmd ) )
            {
                tr_dbg( "upnp device %s: parse scpd failed", dev->host );
            }
            else
            {
                tr_dbg( "upnp device %s: parsed scpd, state scpd -> ready",
                        dev->host );
                dev->state = UPNPDEV_STATE_READY;
                dev->looping = 0;
            }
            break;

        case UPNPDEV_STATE_ADD:
            dev->looping++;
            if( IGD_ADD_CONFLICT == code )
            {
                tr_dbg( "upnp device %s: add conflict, state add -> delete",
                        dev->host );
                dev->state = UPNPDEV_STATE_DEL;
            }
            else if( TR_HTTP_STATUS_OK( code ) ||
                     IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
            {
                tr_dbg( "upnp device %s: add attempt, state add -> get",
                        dev->host );
                dev->state = UPNPDEV_STATE_GET;
            }
            else
            {
                tr_dbg( "upnp device %s: add failed with http code %i",
                        dev->host, code );
            }
            break;

        case UPNPDEV_STATE_GET:
            dev->looping++;
            if( TR_HTTP_STATUS_OK( code ) )
            {
                switch( parseMapping( dev, body, len ) )
                {
                    case -1:
                        break;
                    case 0:
                        tr_dbg( "upnp device %s: invalid mapping, "
                                "state get -> delete", dev->host );
                        dev->state = UPNPDEV_STATE_DEL;
                        break;
                    case 1:
                        tr_dbg( "upnp device %s: good mapping, "
                                "state get -> mapped", dev->host );
                        dev->state = UPNPDEV_STATE_MAPPED;
                        dev->looping = 0;
                        dev->lastcheck = tr_date();
                        tr_inf( "upnp successful for port %i",
                                dev->mappedport );
                        break;
                    default:
                        assert( 0 );
                        break;
                }
            }
            else if( IGD_NO_MAPPING_EXISTS == code ||
                     IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
            {
                tr_dbg( "upnp device %s: no mapping, state get -> add",
                        dev->host );
                dev->state = UPNPDEV_STATE_ADD;
            }
            else
            {
                tr_dbg( "upnp device %s: get failed with http code %i",
                        dev->host, code );
            }
            break;

        case UPNPDEV_STATE_DEL:
            dev->looping++;
            if( TR_HTTP_STATUS_OK( code ) || IGD_NO_MAPPING_EXISTS == code ||
                IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
            {
                tr_dbg( "upnp device %s: deleted, state delete -> ready",
                        dev->host );
                dev->state = UPNPDEV_STATE_READY;
                dev->looping = 0;
            }
            else
            {
                tr_dbg( "upnp device %s: del failed with http code %i",
                        dev->host, code );
            }
            break;

        default:
            assert( 0 );
            break;
    }

    dev->lastrequest = tr_date();
    killHttp( fdlimit, &dev->http );

    if( UPNPDEV_STATE_ERROR == dev->state )
    {
        tr_dbg( "upnp device %s: error, state %hhu -> error",
                dev->host, laststate );
        return 0;
    }

    return 1;
}
void tr_chokingPulse( tr_choking_t * c )
{
    int peersTotalCount, unchoked, mustOptimistic = 1;
    tr_peer_t ** canChoke, ** canUnchoke;
    tr_peer_t ** canChokeZero, ** canUnchokeZero;
    tr_peer_t ** canChokeNonZero, ** canUnchokeNonZero;
    int canChokeCount, canUnchokeCount;
    int canChokeZeroCount, canUnchokeZeroCount;
    int canChokeNonZeroCount, canUnchokeNonZeroCount;
    tr_torrent_t * tor;
    uint64_t now = tr_date();

    tr_lockLock( &c->lock );

    /* Lock all torrents and get the total number of peers */
    peersTotalCount = 0;
    for( tor = c->h->torrentList; tor; tor = tor->next )
    {
        tr_lockLock( &tor->lock );
        peersTotalCount += tor->peerCount;
    }

    canChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
    canUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
    canChokeCount   = 0;
    canUnchokeCount = 0;
    unchoked        = 0;

    for( tor = c->h->torrentList; tor; tor = tor->next )
    {
        tr_peer_t * peer;
        int i;

        for( i = 0; i < tor->peerCount; i++ )
        {
            peer = tor->peers[i];

            if( !tr_peerIsConnected( peer ) )
                continue;

            /* Choke peers who have lost their interest in us */
            if( !tr_peerIsInterested( peer ) )
            {
                if( tr_peerIsUnchoked( peer ) )
                {
                    tr_peerChoke( peer );
                    tr_peerSetOptimistic( peer, 0 );
                }
                continue;
            }

            /* Build two lists of interested peers: those we may choke,
               those we may unchoke. Whatever happens, we never choke a
               peer less than 10 seconds after the time we unchoked him
               (or the other way around). */
            if( tr_peerIsUnchoked( peer ) )
            {
                if( tr_peerIsOptimistic( peer ) )
                {
                    if( tr_peerLastChoke( peer ) + 30000 < now )
                    {
                        /* He got his 30 seconds, now we see him like
                           any other unchoked peer */
                        tr_peerSetOptimistic( peer, 0 );
                    }
                    else
                    {
                        /* Keep him unchoked for 30 seconds */
                        mustOptimistic = 0;
                        continue;
                    }
                }

                unchoked++;
                if( tr_peerLastChoke( peer ) + 10000 < now )
                    canChoke[canChokeCount++] = peer;
            }
            else
            {
                if( tr_peerLastChoke( peer ) + 10000 < now )
                    canUnchoke[canUnchokeCount++] = peer;
            }
        }
    }

    canChokeZero      = malloc( canChokeCount * sizeof( tr_peer_t * ) );
    canChokeNonZero   = malloc( canChokeCount * sizeof( tr_peer_t * ) );
    canUnchokeZero    = malloc( canUnchokeCount * sizeof( tr_peer_t * ) );
    canUnchokeNonZero = malloc( canUnchokeCount * sizeof( tr_peer_t * ) );

    sortPeersDescending( canChoke, canChokeCount,
                         canChokeZero, &canChokeZeroCount,
                         canChokeNonZero, &canChokeNonZeroCount);
    sortPeersAscending( canUnchoke, canUnchokeCount,
                        canUnchokeZero, &canUnchokeZeroCount,
                        canUnchokeNonZero, &canUnchokeNonZeroCount);

    free( canChoke );
    free( canUnchoke );

    if( mustOptimistic )
    {
        tr_peer_t * peer;

        /* Open an extra slot for optimistic choking */
        if( canUnchokeZeroCount )
        {
            /* TODO: prefer peers with no pieces at all */
            peer = canUnchokeZero[--canUnchokeZeroCount];
            tr_peerUnchoke( peer );
            tr_peerSetOptimistic( peer, 1 );
        }
        else if( canUnchokeNonZeroCount )
        {
            peer = canUnchokeNonZero[--canUnchokeNonZeroCount];
            tr_peerUnchoke( peer );
            tr_peerSetOptimistic( peer, 1 );
        }
    }

    /* If we have more open slots than what we should have (the user has
       just lowered his upload limit), we need to choke some of the
       peers we are uploading to. We start with the peers who aren't
       uploading to us, then those we upload the least. */
    while( unchoked > c->slots && canChokeZeroCount > 0 )
    {
        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
        unchoked--;
    }
    while( unchoked > c->slots && canChokeNonZeroCount > 0 )
    {
        tr_peerChoke( canChokeNonZero[--canChokeNonZeroCount] );
        unchoked--;
    }

    /* If we have unused open slots, let's unchoke some people. We start
       with the peers who are uploading to us the most. */
    while( unchoked < c->slots && canUnchokeNonZeroCount > 0 )
    {
        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
        unchoked++;
    }
    while( unchoked < c->slots && canUnchokeZeroCount > 0 )
    {
        tr_peerUnchoke( canUnchokeZero[--canUnchokeZeroCount] );
        unchoked++;
    }

    /* Choke peers who aren't uploading if there are good peers waiting
       for an unchoke */
    while( canChokeZeroCount > 0 && canUnchokeNonZeroCount > 0 )
    {
        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
    }

    /* Choke peers who aren't uploading that much if there are choked
       peers who are uploading more */
    while( canChokeNonZeroCount > 0 && canUnchokeNonZeroCount > 0 )
    {
        if( tr_peerDownloadRate( canUnchokeNonZero[canUnchokeNonZeroCount - 1] )
            < tr_peerDownloadRate( canChokeNonZero[canChokeNonZeroCount - 1] ) )
            break;

        tr_peerChoke( canChokeNonZero[--canChokeNonZeroCount] );
        tr_peerUnchoke( canUnchokeNonZero[--canUnchokeNonZeroCount] );
    }

    /* Some unchoked peers still aren't uploading to us, let's give a
       chance to other non-uploaders */
    while( canChokeZeroCount > 0 && canUnchokeZeroCount > 0 )
    {
        tr_peerChoke( canChokeZero[--canChokeZeroCount] );
        tr_peerUnchoke( canUnchokeZero[--canUnchokeZeroCount] );
    }

    free( canChokeZero );
    free( canChokeNonZero );
    free( canUnchokeZero );
    free( canUnchokeNonZero );

    /* Unlock all torrents */
    for( tor = c->h->torrentList; tor; tor = tor->next )
    {
        tr_lockUnlock( &tor->lock );
    }

    tr_lockUnlock( &c->lock );
}
int tr_trackerPulse( tr_tracker_t * tc )
{
    tr_torrent_t * tor = tc->tor;
    tr_info_t    * inf = &tor->info;
    uint64_t       now = tr_date();

    if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
    {
        tc->resolve = tr_netResolveInit( inf->trackerAddress );

        tr_inf( "Tracker: connecting to %s:%d (%s)",
                inf->trackerAddress, inf->trackerPort,
                tc->started ? "sending 'started'" :
                ( tc->completed ? "sending 'completed'" :
                  ( tc->stopped ? "sending 'stopped'" :
                    ( 0 < tc->newPort ? "sending 'stopped' to change port" :
                      "getting peers" ) ) ) );

        tc->status  = TC_STATUS_RESOLVE;
        tc->dateTry = tr_date();
    }

    if( tc->status & TC_STATUS_RESOLVE )
    {
        int ret;
        struct in_addr addr;

        ret = tr_netResolvePulse( tc->resolve, &addr );
        if( ret == TR_RESOLVE_WAIT )
        {
            return 0;
        }
        else
        {
            tr_netResolveClose( tc->resolve );
        }

        if( ret == TR_RESOLVE_ERROR )
        {
            tc->status = TC_STATUS_IDLE;
            return 0;
        }

        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
        {
            tc->status = TC_STATUS_IDLE;
            return 0;
        }

        tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
        if( tc->socket < 0 )
        {
            tr_fdSocketClosed( tor->fdlimit, 1 );
            tc->status = TC_STATUS_IDLE;
            return 0;
        }

        tc->status = TC_STATUS_CONNECT;
    }

    if( tc->status & TC_STATUS_CONNECT )
    {
        /* We are connecting to the tracker. Try to send the query */
        sendQuery( tc );
    }

    if( tc->status & TC_STATUS_RECV )
    {
        /* Try to get something */
        recvAnswer( tc );
    }

    if( tc->status > TC_STATUS_IDLE && now > tc->dateTry + 60000 )
    {
        /* Give up if the request wasn't successful within 60 seconds */
        tr_inf( "Tracker: timeout reached (60 s)" );

        tr_netClose( tc->socket );
        tr_fdSocketClosed( tor->fdlimit, 1 );

        tc->status  = TC_STATUS_IDLE;
        tc->dateTry = tr_date();
    }

    return 0;
}
Exemple #24
0
void
tr_bandwidthAllocate( tr_bandwidth  * b,
                      tr_direction    dir,
                      int             period_msec )
{
    int i, n, peerCount;
    tr_ptrArray * tmp;
    struct tr_peerIo ** peers;
    const uint64_t now = tr_date( );
    const uint64_t cutoff = now + 100; /* 1/10th of a second */


    /* allocateBandwidth() is a helper function with two purposes:
     * 1. allocate bandwidth to b and its subtree
     * 2. accumulate an array of all the peerIos from b and its subtree. */
    tmp = tr_ptrArrayNew( );
    allocateBandwidth( b, dir, period_msec, tmp );
    peers = (struct tr_peerIo**) tr_ptrArrayPeek( tmp, &peerCount );

    /* Stop all peers from listening for the socket to be ready for IO.
     * See "Second phase of IO" lower in this function for more info. */
    for( i=0; i<peerCount; ++i )
        tr_peerIoSetEnabled( peers[i], dir, FALSE );

    /* First phase of IO.  Tries to distribute bandwidth fairly to keep faster
     * peers from starving the others.  Loop through the peers, giving each a
     * small chunk of bandwidth.  Keep looping until we run out of bandwidth
     * or peers that can use it */
    n = peerCount;
    i = n ? tr_cryptoWeakRandInt( n ) : 0; /* pick a random starting point */
    for( ; n>0 && tr_date()<=cutoff; )
    {
        const int increment = n==1 ? 4096 : 1024;
        const int byteCount = tr_peerIoFlush( peers[i], dir, increment);

        if( byteCount == increment )
            ++i;
        else {
            /* peer is done writing for now; move it to the end of the list */
            tr_peerIo * tmp = peers[i];
            peers[i] = peers[n-1];
            peers[n-1] = tmp;
            --n;
        }

        assert( i <= n );
        if( i == n )
            i = 0;
    }

    /* Second phase of IO.  To help us scale in high bandwidth situations,
     * enable on-demand IO for peers with bandwidth left to burn.
     * This on-demand IO is enabled until (1) the peer runs out of bandwidth,
     * or (2) the next tr_bandwidthAllocate() call, when we start over again. */
    for( i=0; i<peerCount; ++i )
        if( tr_peerIoHasBandwidthLeft( peers[i], dir ) )
            tr_peerIoSetEnabled( peers[i], dir, TRUE );

    /* cleanup */
    tr_ptrArrayFree( tmp, NULL );
}