Beispiel #1
0
void
tr_natpmpRemoveForwarding( tr_natpmp_t * pmp )
{
    tr_inf( "nat-pmp unset port" );
    pmp->newport = -1;
    unmap( pmp );
}
Beispiel #2
0
static void
watchdir_update_impl( dtr_watchdir * w )
{
    int ret;
    fd_set rfds;
    struct timeval time;
    const int fd = w->inotify_fd;

    /* timeout after one second */
    time.tv_sec = 1;
    time.tv_usec = 0;

    /* make the fd_set hold the inotify fd */
    FD_ZERO( &rfds );
    FD_SET( fd, &rfds );

    /* check for added files */
    ret = select( fd+1, &rfds, NULL, NULL, &time );
    if( ret < 0 ) {
        perror( "select" );
    } else if( !ret ) {
        /* timed out! */
    } else if( FD_ISSET( fd, &rfds ) ) {
        int i = 0;
        char buf[BUF_LEN];
        int len = read( fd, buf, sizeof( buf ) );
        while (i < len) {
            struct inotify_event * event = (struct inotify_event *) &buf[i];
            tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", event->name, w->dir );
            w->callback( w->session, w->dir, event->name );
            i += EVENT_SIZE +  event->len;
        }
    }
}
Beispiel #3
0
static void
deviceRemove( tr_upnp_device_t ** prevptr, tr_fd_t * fdlimit )
{
    tr_upnp_device_t * dead;

    dead = *prevptr;
    *prevptr = dead->next;

    tr_inf( "forgetting upnp device %s", dead->host );

    free( dead->id );
    free( dead->host );
    free( dead->root );
    free( dead->soap );
    free( dead->scpd );
    free( dead->myaddr );
    if( NULL != dead->http )
    {
        killHttp( fdlimit, &dead->http );
    }
    actionFree( &dead->getcmd );
    actionFree( &dead->addcmd );
    actionFree( &dead->delcmd );
    free( dead );
}
Beispiel #4
0
static void
moveFiles( const char * oldDir,
           const char * newDir )
{
    if( oldDir && newDir && strcmp( oldDir, newDir ) )
    {
        DIR * dirh = opendir( oldDir );
        if( dirh )
        {
            int             count = 0;
            struct dirent * dirp;
            while( ( dirp = readdir( dirh ) ) )
            {
                if( strcmp( dirp->d_name,
                            "." ) && strcmp( dirp->d_name, ".." ) )
                {
                    char * o = tr_buildPath( oldDir, dirp->d_name, NULL );
                    char * n = tr_buildPath( newDir, dirp->d_name, NULL );
                    rename( o, n );
                    ++count;
                    tr_free( n );
                    tr_free( o );
                }
            }

            if( count )
                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
                        count, oldDir, newDir );
            closedir( dirh );
        }
    }
}
Beispiel #5
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_mkdirp() and open().
 */
static int
TrOpenFile( int          i,
            const char * folder,
            const char * torrentFile,
            int          doWrite,
            int          doPreallocate,
            uint64_t     desiredFileSize )
{
    struct tr_openfile * file = &gFd->open[i];
    int                  flags;
    char               * filename;
    struct stat          sb;
    int                  alreadyExisted;

    /* confirm the parent folder exists */
    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
        return ENOENT;

    /* create subfolders, if any */
    filename = tr_buildPath( folder, torrentFile, NULL );
    if( doWrite )
    {
        char * tmp = tr_dirname( filename );
        const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0;
        tr_free( tmp );
        if( err ) {
            tr_free( filename );
            return err;
        }
    }

    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );

    if( doWrite && !alreadyExisted && doPreallocate )
        if( preallocateFile( filename, desiredFileSize ) )
            tr_inf( _( "Preallocated file \"%s\"" ), filename );
    
    /* open the file */
    flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
#ifdef O_LARGEFILE
    flags |= O_LARGEFILE;
#endif
#ifdef WIN32
    flags |= O_BINARY;
#endif
    file->fd = open( filename, flags, 0666 );
    if( file->fd == -1 )
    {
        const int err = errno;
        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename,
               tr_strerror( err ) );
        tr_free( filename );
        return err;
    }

    tr_free( filename );
    return 0;
}
Beispiel #6
0
static int
isClutchDir( const char * path )
{
    struct stat sb;
    char * tmp = tr_buildPath( path, "javascript", "transmission.js", NULL );
    const int ret = !stat( tmp, &sb );
    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
    tr_free( tmp );
    return ret;
}
Beispiel #7
0
static int
isWebClientDir( const char * path )
{
    struct stat sb;
    char * tmp = tr_buildPath( path, "index.html", NULL );
    const int ret = !stat( tmp, &sb );
    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
    tr_free( tmp );
    return ret;
}
Beispiel #8
0
static void
watchdir_new_impl( dtr_watchdir * w )
{
    int i;
    w->inotify_fd = inotify_init( );
    tr_inf( "Using inotify to watch directory \"%s\"", w->dir );
    i = inotify_add_watch( w->inotify_fd, w->dir, DTR_INOTIFY_MASK );
    if( i < 0 )
        tr_err( "Unable to watch \"%s\": %s", w->dir, strerror (errno) );
}
Beispiel #9
0
void
tr_natpmpStop( tr_natpmp_t * pmp )
{
    if( pmp->active )
    {
        tr_inf( "stopping nat-pmp" );
        pmp->active = 0;
        killsock( &pmp->mcastfd );
        unmap( pmp );
    }
}
Beispiel #10
0
static void
watchdir_new_impl (dtr_watchdir * w)
{
    int i;
    DIR * odir;
    w->inotify_fd = inotify_init ();

    if (w->inotify_fd < 0)
    {
        i = -1;
    }
    else
    {
        tr_inf ("Using inotify to watch directory \"%s\"", w->dir);
        i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK);
    }

    if (i < 0)
    {
        tr_err ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno));
    }
    else if ((odir = opendir (w->dir)))
    {
        struct dirent * d;

        while ((d = readdir (odir)))
        {
            const char * name = d->d_name;

            if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
                continue;

            tr_inf ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
            w->callback (w->session, w->dir, name);
        }

        closedir (odir);
    }

}
Beispiel #11
0
void
tr_natpmpStart( tr_natpmp_t * pmp )
{
    if( !pmp->active )
    {
        tr_inf( "starting nat-pmp" );
        pmp->active = 1;
        if( 0 > pmp->mcastfd )
        {
            pmp->mcastfd = mcastsetup();
        }
    }
}
Beispiel #12
0
static void
onFileAdded( tr_session * session, const char * dir, const char * file )
{
    char * filename = tr_buildPath( dir, file, NULL );
    tr_ctor * ctor = tr_ctorNew( session );
    int err = tr_ctorSetMetainfoFromFile( ctor, filename );

    if( !err )
    {
        tr_torrentNew( ctor, &err );

        if( err == TR_PARSE_ERR )
            tr_err( "Error parsing .torrent file \"%s\"", file );
        else
        {
            tr_bool trash = FALSE;
            int test = tr_ctorGetDeleteSource( ctor, &trash );

            tr_inf( "Parsing .torrent file successful \"%s\"", file );

            if( !test && trash )
            {
                tr_inf( "Deleting input .torrent file \"%s\"", file );
                if( remove( filename ) )
                    tr_err( "Error deleting .torrent file: %s", tr_strerror( errno ) );
            }
            else
            {
                char * new_filename = tr_strdup_printf( "%s.added", filename );
                rename( filename, new_filename );
                tr_free( new_filename );
            }
        }
    }

    tr_ctorFree( ctor );
    tr_free( filename );
}
Beispiel #13
0
static void
mcastpulse( tr_natpmp_t * pmp )
{
    struct sockaddr_in sin;
    uint8_t            buf[16];
    int                res;
    char               dbgstr[INET_ADDRSTRLEN];
    tr_natpmp_parse_t  parse;

    res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
    if( TR_NET_BLOCK & res )
    {
        return;
    }
    else if( TR_NET_CLOSE & res )
    {
        tr_err( "error reading nat-pmp multicast message" );
        killsock( &pmp->mcastfd );
        return;
    }

    tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
    tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );

    if( pmp->dest.s_addr != sin.sin_addr.s_addr )
    {
        tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
                dbgstr );
        return;
    }

    if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) )
    {
        if( checktime( &pmp->uptime, parse.seconds ) )
        {
            pmp->renew = 0;
            tr_inf( "detected nat-pmp device reset" );
            if( NULL != pmp->req )
            {
                resetreq( pmp->req );
            }
        }
        if( PMP_STATE_NOBODYHOME == pmp->state )
        {
            tr_dbg( "nat-pmp state notfound -> idle" );
            pmp->state = PMP_STATE_IDLE;
        }
    }
}
Beispiel #14
0
void
tr_upnpStop( tr_upnp_t * upnp )
{
    tr_lockLock( &upnp->lock );

    if( upnp->active )
    {
        tr_inf( "stopping upnp" );
        upnp->active = 0;
        killSock( upnp->fdlimit, &upnp->infd );
        killSock( upnp->fdlimit, &upnp->outfd );
    }

    tr_lockUnlock( &upnp->lock );
}
Beispiel #15
0
void
tr_upnpStart( tr_upnp_t * upnp )
{
    tr_lockLock( &upnp->lock );

    if( !upnp->active )
    {
        tr_inf( "starting upnp" );
        upnp->active = 1;
        upnp->discovering = 1;
        upnp->infd = mcastStart( upnp->fdlimit );
        upnp->lastdiscover = 0;
        upnp->lastdelay = SSDP_FIRST_DELAY / 2;
    }

    tr_lockUnlock( &upnp->lock );
}
Beispiel #16
0
static void
blocklistLoad( tr_blocklist * b )
{
    int          fd;
    struct stat  st;
    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );

    blocklistClose( b );

    if( stat( b->filename, &st ) == -1 )
        return;

    fd = open( b->filename, O_RDONLY );
    if( fd == -1 )
    {
        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
        return;
    }

#ifndef WIN32
    b->rules = mmap( NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );
#else
    b->rules = mmap( NULL, st.st_size, 0, 0, fd, 0 );
#endif
    if( !b->rules )
    {
        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
        close( fd );
        return;
    }

    b->byteCount = st.st_size;
    b->ruleCount = st.st_size / sizeof( struct tr_ip_range );
    b->fd = fd;

    {
        char * base = tr_basename( b->filename );
        tr_inf( _( "Blocklist \"%s\" contains %'zu entries" ), base, b->ruleCount );
        tr_free( base );
    }
}
Beispiel #17
0
static void
blocklistLoad (tr_blocklist * b)
{
    int fd;
    size_t byteCount;
    struct stat st;
    const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");

    blocklistClose (b);

    if (stat (b->filename, &st) == -1)
        return;

    fd = open (b->filename, O_RDONLY | O_BINARY);
    if (fd == -1)
    {
        tr_err (err_fmt, b->filename, tr_strerror (errno));
        return;
    }

    byteCount = (size_t) st.st_size;
    b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0);
    if (!b->rules)
    {
        tr_err (err_fmt, b->filename, tr_strerror (errno));
        close (fd);
        return;
    }

    b->fd = fd;
    b->byteCount = byteCount;
    b->ruleCount = byteCount / sizeof (struct tr_ipv4_range);

    {
        char * base = tr_basename (b->filename);
        tr_inf (_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount);
        tr_free (base);
    }
}
Beispiel #18
0
static void
deviceAdd( tr_upnp_device_t ** first, const char * id, int idLen,
           const char * url, int urlLen )
{
    tr_upnp_device_t * ii;

    for( ii = *first; NULL != ii; ii = ii->next )
    {
        if( 0 == tr_strncasecmp( ii->id, id, idLen ) )
        {
            /* this device may have gone away and came back, recheck it */
            ii->lastcheck = 0;
            return;
        }
    }

    ii = malloc( sizeof( *ii ) );
    if( NULL == ii )
    {
        return;
    }
    memset( ii, 0, sizeof( *ii ) );
    if( tr_httpParseUrl( url, urlLen, &ii->host, &ii->port, &ii->root ) )
    {
        free( ii );
        return;
    }
    ii->id = tr_dupstr( id, idLen );
    ii->state = UPNPDEV_STATE_ROOT;
    actionSetup( &ii->getcmd, "GetSpecificPortMappingEntry", 8 );
    actionSetup( &ii->addcmd, "AddPortMapping", 8 );
    actionSetup( &ii->delcmd, "DeletePortMapping", 3 );
    ii->next = *first;
    *first = ii;

    tr_inf( "new upnp device %s, port %i, path %s",
            ii->host, ii->port, ii->root );
}
Beispiel #19
0
void
tr_sessionSaveSettings( tr_session * session, const char * configDir, tr_benc * settings )
{
    tr_benc fileSettings;
    char * filename;

    assert( tr_bencIsDict( settings ) );

    filename = tr_buildPath( configDir, "settings.json", NULL );

    tr_sessionGetSettings( session, settings );

    if( tr_bencLoadJSONFile( filename, &fileSettings ) ) {
        tr_bencSaveJSONFile( filename, settings );
    } else {
        tr_bencMergeDicts( &fileSettings, settings );
        tr_bencSaveJSONFile( filename, &fileSettings );
        tr_bencFree( &fileSettings );
    }

    tr_inf( "Saved \"%s\"", filename );
    tr_free( filename );
}
Beispiel #20
0
static void
gotsig( int sig )
{
    switch( sig )
    {
        case SIGHUP:
        {
            tr_benc settings;
            const char * configDir = tr_sessionGetConfigDir( mySession );
            tr_inf( "Reloading settings from \"%s\"", configDir );
            tr_bencInitDict( &settings, 0 );
            tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, TRUE );
            tr_sessionLoadSettings( &settings, configDir, MY_NAME );
            tr_sessionSet( mySession, &settings );
            tr_bencFree( &settings );
            tr_sessionReloadBlocklists( mySession );
            break;
        }

        default:
            closing = TRUE;
            break;
    }
}
Beispiel #21
0
int
_tr_blocklistSetContent( tr_blocklist * b,
                         const char *   filename )
{
    FILE * in;
    FILE * out;
    int inCount = 0;
    int outCount = 0;
    char line[2048];
    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );

    if( !filename )
    {
        blocklistDelete( b );
        return 0;
    }

    in = fopen( filename, "rb" );
    if( !in )
    {
        tr_err( err_fmt, filename, tr_strerror( errno ) );
        return 0;
    }

    blocklistClose( b );

    out = fopen( b->filename, "wb+" );
    if( !out )
    {
        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
        fclose( in );
        return 0;
    }

    while( fgets( line, sizeof( line ), in ) != NULL )
    {
        char * walk;
        struct tr_ip_range range;

        ++inCount;

        /* zap the linefeed */
        if(( walk = strchr( line, '\r' ))) *walk = '\0';
        if(( walk = strchr( line, '\n' ))) *walk = '\0';

        if( !parseLine( line, &range ) )
        {
            /* don't try to display the actual lines - it causes issues */
            tr_err( _( "blocklist skipped invalid address at line %d" ), inCount );
            continue;
        }

        if( fwrite( &range, sizeof( struct tr_ip_range ), 1, out ) != 1 )
        {
            tr_err( _( "Couldn't save file \"%1$s\": %2$s" ), b->filename,
                   tr_strerror( errno ) );
            break;
        }

        ++outCount;
    }

    {
        char * base = tr_basename( b->filename );
        tr_inf( _( "Blocklist \"%s\" updated with %d entries" ), base, outCount );
        tr_free( base );
    }

    fclose( out );
    fclose( in );

    blocklistLoad( b );

    return outCount;
}
Beispiel #22
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;
}
Beispiel #23
0
void
tr_natpmpForwardPort( tr_natpmp_t * pmp, int port )
{
    tr_inf( "nat-pmp set port %i", port );
    pmp->newport = port;
}
Beispiel #24
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;
        }
    }
}
Beispiel #25
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;
}
Beispiel #26
0
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;
}
Beispiel #27
0
int
main( int argc, char ** argv )
{
    int c;
    const char * optarg;
    tr_benc settings;
    tr_bool boolVal;
    tr_bool loaded;
    tr_bool foreground = FALSE;
    tr_bool dumpSettings = FALSE;
    const char * configDir = NULL;
    const char * pid_filename;
    dtr_watchdir * watchdir = NULL;
    FILE * logfile = NULL;
    tr_bool pidfile_created = FALSE;

    signal( SIGINT, gotsig );
    signal( SIGTERM, gotsig );
#ifndef WIN32
    signal( SIGHUP, gotsig );
#endif

    /* load settings from defaults + config file */
    tr_bencInitDict( &settings, 0 );
    tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, TRUE );
    configDir = getConfigDir( argc, (const char**)argv );
    loaded = tr_sessionLoadSettings( &settings, configDir, MY_NAME );

    /* overwrite settings from the comamndline */
    tr_optind = 1;
    while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) {
        switch( c ) {
            case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, TRUE );
                      break;
            case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
                      break;
            case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
                      break;
            case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg );
                      tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, TRUE );
                      break;
            case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, FALSE );
                      break;
            case 941: tr_bencDictAddStr( &settings, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
                      break;
            case 942: tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
                      break;
            case 'd': dumpSettings = TRUE;
                      break;
            case 'e': logfile = fopen( optarg, "a+" );
                      if( logfile == NULL )
                          fprintf( stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror( errno ) );
                      break;
            case 'f': foreground = TRUE;
                      break;
            case 'g': /* handled above */
                      break;
            case 'V': /* version */
                      fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
                      exit( 0 );
            case 'o': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, TRUE );
                      break;
            case 'O': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, FALSE );
                      break;
            case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) );
                      break;
            case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, TRUE );
                      break;
            case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE );
                      break;
            case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg );
                      break;
            case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg );
                      break;
            case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
                      break;
            case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
                      break;
            case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
                      break;
            case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
                      break;
            case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) );
                      break;
            case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) );
                      break;
            case 800: paused = TRUE;
                      break;
            case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
                      break;
            case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
                      break;
            case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
                      break;
            case 'i': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg );
                      break;
            case 'I': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg );
                      break;
            case 'r': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg );
                      break;
            case 953: tr_bencDictAddReal( &settings, TR_PREFS_KEY_RATIO, atof(optarg) );
                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, TRUE );
                      break;
            case 954: tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, FALSE );
                      break;
            case 'x': tr_bencDictAddStr( &settings, PREF_KEY_PIDFILE, optarg );
                      break;
            case 'y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, TRUE );
                      break;
            case 'Y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, FALSE );
                      break;
            case 810: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_ERR );
                      break;
            case 811: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF );
                      break;
            case 812: tr_bencDictAddInt( &settings,  TR_PREFS_KEY_MSGLEVEL, TR_MSG_DBG );
                      break;
            default:  showUsage( );
                      break;
        }
    }

    if( foreground && !logfile )
        logfile = stderr;

    if( !loaded )
    {
        printMessage( logfile, TR_MSG_ERR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__ );
        return -1;
    }

    if( dumpSettings )
    {
        char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL );
        fprintf( stderr, "%s", str );
        tr_free( str );
        return 0;
    }

    if( !foreground && tr_daemon( TRUE, FALSE ) < 0 )
    {
        char buf[256];
        tr_snprintf( buf, sizeof( buf ), "Failed to daemonize: %s", tr_strerror( errno ) );
        printMessage( logfile, TR_MSG_ERR, MY_NAME, buf, __FILE__, __LINE__ );
        exit( 1 );
    }

    /* start the session */
    tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
    tr_formatter_size_init( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
    tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
    mySession = tr_sessionInit( "daemon", configDir, TRUE, &settings );
    tr_ninf( NULL, "Using settings from \"%s\"", configDir );
    tr_sessionSaveSettings( mySession, configDir, &settings );

    pid_filename = NULL;
    tr_bencDictFindStr( &settings, PREF_KEY_PIDFILE, &pid_filename );
    if( pid_filename && *pid_filename )
    {
        FILE * fp = fopen( pid_filename, "w+" );
        if( fp != NULL )
        {
            fprintf( fp, "%d", (int)getpid() );
            fclose( fp );
            tr_inf( "Saved pidfile \"%s\"", pid_filename );
            pidfile_created = TRUE;
        }
        else
            tr_err( "Unable to save pidfile \"%s\": %s", pid_filename, strerror( errno ) );
    }

    if( tr_bencDictFindBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal ) && boolVal )
        tr_ninf( MY_NAME, "requiring authentication" );

    /* maybe add a watchdir */
    {
        const char * dir;

        if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal )
            && boolVal
            && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
            && dir
            && *dir )
        {
            tr_inf( "Watching \"%s\" for new .torrent files", dir );
            watchdir = dtr_watchdir_new( mySession, dir, onFileAdded );
        }
    }

    /* load the torrents */
    {
        tr_torrent ** torrents;
        tr_ctor * ctor = tr_ctorNew( mySession );
        if( paused )
            tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
        torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
        tr_free( torrents );
        tr_ctorFree( ctor );
    }

#ifdef HAVE_SYSLOG
    if( !foreground )
        openlog( MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON );
#endif

    while( !closing ) {
        tr_wait_msec( 1000 ); /* sleep one second */
        dtr_watchdir_update( watchdir );
        pumpLogMessages( logfile );
    }

    /* shutdown */
#if HAVE_SYSLOG
    if( !foreground )
    {
        syslog( LOG_INFO, "%s", "Closing session" );
        closelog( );
    }
#endif

    printf( "Closing transmission session..." );
    tr_sessionSaveSettings( mySession, configDir, &settings );
    dtr_watchdir_free( watchdir );
    tr_sessionClose( mySession );
    printf( " done.\n" );

    /* cleanup */
    if( pidfile_created )
        remove( pid_filename );
    tr_bencFree( &settings );
    return 0;
}
Beispiel #28
0
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;
    }
}
Beispiel #29
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 );
}
Beispiel #30
0
static void
tr_sessionInitImpl( void * vdata )
{
    int64_t i;
    int64_t j;
    double  d;
    tr_bool found;
    const char * str;
    tr_benc settings;
    char * filename;
    struct init_data * data = vdata;
    tr_benc * clientSettings = data->clientSettings;
    tr_session * session = data->session;

    assert( tr_amInEventThread( session ) );
    assert( tr_bencIsDict( clientSettings ) );

    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p", session->bandwidth );

    tr_bencInitDict( &settings, 0 );
    tr_sessionGetDefaultSettings( &settings );
    tr_bencMergeDicts( &settings, clientSettings );

#ifndef WIN32
    /* Don't exit when writing on a broken socket */
    signal( SIGPIPE, SIG_IGN );
#endif

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i );
    assert( found );
    session->peerLimitPerTorrent = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_MSGLEVEL, &i );
    assert( found );
    tr_setMessageLevel( i );
    tr_setMessageQueuing( data->messageQueuingEnabled );


    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEX_ENABLED, &i );
    assert( found );
    session->isPexEnabled = i != 0;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i );
    assert( found );
    assert( tr_isEncryptionMode( i ) );
    session->encryptionMode = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PREALLOCATION, &i );
    assert( found );
    assert( tr_isPreallocationMode( i ) );
    session->preallocationMode = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i );
    assert( found );
    session->peerSocketTOS = i;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str );
    assert( found );
    session->downloadDir = tr_strdup( str );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_ENABLED, &i );
    assert( found );
    session->isProxyEnabled = i != 0;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY, &str );
    assert( found );
    session->proxy = tr_strdup( str );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_PORT, &i );
    assert( found );
    session->proxyPort = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_TYPE, &i );
    assert( found );
    session->proxyType = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &i );
    assert( found );
    session->isProxyAuthEnabled = i != 0;

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_USERNAME, &str );
    assert( found );
    session->proxyUsername = tr_strdup( str );

    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_PASSWORD, &str );
    assert( found );
    session->proxyPassword = tr_strdup( str );

    session->so_sndbuf = 1500 * 3; /* 3x MTU for most ethernet/wireless */
    session->so_rcvbuf = 8192;

    tr_setConfigDir( session, data->configDir );

    tr_trackerSessionInit( session );
    assert( session->tracker != NULL );

    session->peerMgr = tr_peerMgrNew( session );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_LAZY_BITFIELD, &i );
    assert( found );
    session->useLazyBitfield = i != 0;

    /* Initialize rate and file descripts controls */

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i );
    assert( found );
    session->openFileLimit = i;
    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &j );
    assert( found );
    tr_fdInit( session->openFileLimit, j );

    /**
    *** random port
    **/

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, &i );
    assert( found );
    session->isPortRandom = i != 0;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i );
    assert( found );
    session->randomPortLow = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i );
    assert( found );
    session->randomPortHigh = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j );
    assert( found );
    session->peerPort = session->isPortRandom ? getRandomPort( session ) : j;
    session->shared = tr_sharedInit( session, i, session->peerPort );
    session->isPortSet = session->isPortRandom || j>0;

    /**
    **/

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i );
    assert( found );
    session->uploadSlotsPerTorrent = i;

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED_ENABLED, &j );
    assert( found );
    tr_sessionSetSpeedLimit( session, TR_UP, i );
    tr_sessionSetSpeedLimitEnabled( session, TR_UP, j );

    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED, &i )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED_ENABLED, &j );
    assert( found );
    tr_sessionSetSpeedLimit( session, TR_DOWN, i );
    tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, j );

    found = tr_bencDictFindDouble( &settings, TR_PREFS_KEY_RATIO, &d )
         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RATIO_ENABLED, &j );
    assert( found );
    tr_sessionSetRatioLimit( session, d );
    tr_sessionSetRatioLimited( session, j );

    /* initialize the blocklist */
    filename = tr_buildPath( session->configDir, "blocklists", NULL );
    tr_mkdirp( filename, 0777 );
    tr_free( filename );
    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &i );
    assert( found );
    session->isBlocklistEnabled = i;
    loadBlocklists( session );

    session->rpcServer = tr_rpcInit( session, &settings );

    tr_bencFree( &settings );

    assert( tr_isSession( session ) );

    /* first %s is the application name
       second %s is the version number */
    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );

    tr_statsInit( session );
    session->web = tr_webInit( session );
    metainfoLookupRescan( session );
    session->isWaiting = FALSE;
    dbgmsg( "returning session %p; session->tracker is %p", session, session->tracker );
}