Changeset 8351


Ignore:
Timestamp:
May 8, 2009, 2:37:46 AM (12 years ago)
Author:
charles
Message:

(trunk libT) add a session_id cookie to the rpc server

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/daemon/remote.c

    r8274 r8351  
    133133static char * auth = NULL;
    134134static char * netrc = NULL;
     135static char * cookies = NULL;
    135136
    136137static char*
     
    12741275}
    12751276
     1277/* very basic handling of cookies: when we get Set-Cookie, throw out all
     1278 * the previous cookies...  T only uses one cookie (session_id) */
     1279static size_t
     1280parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
     1281{
     1282    const char * line = ptr;
     1283    const size_t linelen = size * nmemb;
     1284    const char * lineend = line + linelen;
     1285    const char * key = "Set-Cookie: ";
     1286    const size_t keylen = strlen( key );
     1287    if( ( linelen >= keylen ) && !memcmp( line, key, keylen ) ) {
     1288        const char * begin = line + keylen;
     1289        const char * end = begin;
     1290        while(( end!=lineend ) && !strchr("\r\n",*end))
     1291            ++end;
     1292        tr_free( cookies );
     1293        cookies = tr_strndup( begin, end-begin );
     1294    }
     1295
     1296    return linelen;
     1297}
     1298
     1299static CURL*
     1300tr_curl_easy_init( struct evbuffer * writebuf )
     1301{
     1302    CURL * curl = curl_easy_init( );
     1303    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
     1304    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
     1305    curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
     1306    curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, parseResponseHeader );
     1307    curl_easy_setopt( curl, CURLOPT_POST, 1 );
     1308    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
     1309    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
     1310    curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60L );
     1311    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
     1312#ifdef HAVE_ZLIB
     1313    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
     1314#endif
     1315    if( cookies )
     1316        curl_easy_setopt( curl, CURLOPT_COOKIE, cookies );
     1317    if( netrc )
     1318        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
     1319    if( auth )
     1320        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
     1321    return curl;
     1322}
     1323   
     1324
    12761325static void
    12771326processRequests( const char *  host,
     
    12801329                 int           reqCount )
    12811330{
    1282     int               i;
    1283     CURL *            curl;
     1331    int i;
     1332    CURL * curl = NULL;
    12841333    struct evbuffer * buf = evbuffer_new( );
    1285     char *            url = tr_strdup_printf(
    1286         "http://%s:%d/transmission/rpc", host, port );
    1287 
    1288     curl = curl_easy_init( );
    1289     curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
    1290 #ifdef HAVE_ZLIB
    1291     curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
    1292 #endif
    1293     curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
    1294     curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
    1295     curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
    1296     curl_easy_setopt( curl, CURLOPT_POST, 1 );
    1297     curl_easy_setopt( curl, CURLOPT_URL, url );
    1298     curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
    1299     curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
    1300     curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60L );
    1301     if( netrc )
    1302         curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
    1303     if( auth )
    1304         curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
    1305 
    1306     for( i = 0; i < reqCount; ++i )
     1334    char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port );
     1335
     1336    for( i=0; i<reqCount; ++i )
    13071337    {
    13081338        CURLcode res;
     1339        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
     1340
     1341        if( curl == NULL )
     1342        {
     1343            curl = tr_curl_easy_init( buf );
     1344            curl_easy_setopt( curl, CURLOPT_URL, url );
     1345        }
     1346
    13091347        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
     1348
    13101349        if( debug )
    13111350            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
    13121351        if( ( res = curl_easy_perform( curl ) ) )
    1313             tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
    1314                     curl_easy_strerror( res ) );
    1315         else
    1316             processResponse( host, port, EVBUFFER_DATA(
    1317                                 buf ), EVBUFFER_LENGTH( buf ) );
    1318 
    1319         evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
     1352            tr_nerr( MY_NAME, "(%s:%d) %s", host, port, curl_easy_strerror( res ) );
     1353        else {
     1354            long response;
     1355            curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
     1356            switch( response ) {
     1357                case 200:
     1358                    processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
     1359                    break;
     1360                case 409:
     1361                    /* session_id cookie expired.  by the time we reach line this our
     1362                     * curl header func has already found the new session_id, so make
     1363                     * a new CURL* and try again... */
     1364                    curl_easy_cleanup( curl );
     1365                    curl = NULL;
     1366                    --i;
     1367                    break;
     1368                default:
     1369                    fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
     1370                    break;
     1371            }
     1372        }
    13201373    }
    13211374
     
    13231376    tr_free( url );
    13241377    evbuffer_free( buf );
    1325     curl_easy_cleanup( curl );
     1378    if( curl != NULL )
     1379        curl_easy_cleanup( curl );
    13261380}
    13271381
  • trunk/libtransmission/rpc-server.c

    r8340 r8351  
    5757    struct evhttp *    httpd;
    5858    tr_session *       session;
     59    char *             sessionId;
    5960    char *             username;
    6061    char *             password;
     
    451452}
    452453
    453 static void
    454 handle_request( struct evhttp_request * req,
    455                 void *                  arg )
     454static char*
     455session_id_new( void )
     456{
     457    int i;
     458    const int n = 48;
     459    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
     460    const size_t pool_size = strlen( pool );
     461    char * buf = tr_new( char, n+1 );
     462    for( i=0; i<n; ++i )
     463        buf[i] = pool[ tr_cryptoRandInt( pool_size ) ];
     464    buf[n] = '\0';
     465    return buf;
     466}
     467
     468static tr_bool
     469test_session_id( struct tr_rpc_server * server, struct evhttp_request * req )
     470{
     471    char * needle = tr_strdup_printf( "session_id=%s", server->sessionId );
     472    const char * haystack = evhttp_find_header( req->input_headers, "Cookie" );
     473    const tr_bool success = (haystack!=NULL) && (strstr(haystack,needle)!=NULL);
     474    tr_free( needle );
     475    return success;
     476}
     477
     478static void
     479handle_request( struct evhttp_request * req, void * arg )
    456480{
    457481    struct tr_rpc_server * server = arg;
     
    460484    {
    461485        const char * auth;
    462         char *       user = NULL;
    463         char *       pass = NULL;
     486        char * user = NULL;
     487        char * pass = NULL;
     488        char * cookie;
    464489
    465490        evhttp_add_header( req->output_headers, "Server", MY_REALM );
     491        cookie = tr_strdup_printf( "session_id=%s;Path=/;Discard", server->sessionId );
     492        evhttp_add_header( req->output_headers, "Set-Cookie", cookie );
     493        tr_free( cookie );
    466494
    467495        auth = evhttp_find_header( req->input_headers, "Authorization" );
    468 
    469496        if( auth && !strncasecmp( auth, "basic ", 6 ) )
    470497        {
     
    480507        if( !isAddressAllowed( server, req->remote_host ) )
    481508        {
    482             send_simple_response( req, 401,
     509            send_simple_response( req, 403,
    483510                "<p>Unauthorized IP Address.</p>"
    484511                "<p>Either disable the IP address whitelist or add your address to it.</p>"
     
    511538        {
    512539            handle_clutch( req, server );
     540        }
     541        else if( !test_session_id( server, req ) )
     542        {
     543            send_simple_response( req, 409, "<p>Invalid session_id cookie.</p>" );
    513544        }
    514545        else if( !strncmp( req->uri, "/transmission/rpc", 17 ) )
     
    767798    s = tr_new0( tr_rpc_server, 1 );
    768799    s->session = session;
     800    s->sessionId = session_id_new( );
    769801
    770802    found = tr_bencDictFindBool( settings, TR_PREFS_KEY_RPC_ENABLED, &boolVal );
  • trunk/qt/session.cc

    r8334 r8351  
    542542        header.setValue( "User-Agent", QCoreApplication::instance()->applicationName() + "/" + LONG_VERSION_STRING );
    543543        header.setValue( "Content-Type", "application/json; charset=UTF-8" );
    544         myHttp.request( header, data, &myBuffer );
     544        if( !myCookies.isEmpty( ) )
     545            header.setValue( "Cookie", myCookies );
     546        QBuffer * buf = new QBuffer;
     547        buf->setData( data );
     548        myHttp.request( header, buf, &myBuffer );
    545549#ifdef DEBUG_HTTP
    546550        std::cerr << "sending " << qPrintable(header.toString()) << "\nBody:\n" << request << std::endl;
     
    561565{
    562566    Q_UNUSED( id );
     567
     568    QHttpResponseHeader response = myHttp.lastResponse();
     569    QIODevice * sourceDevice = myHttp.currentSourceDevice( );
    563570
    564571#ifdef DEBUG_HTTP
     
    570577#endif
    571578
    572     if( error )
     579    // very basic handling of cookies: when we get Set-Cookie, throw out all
     580    // the previous cookies...  T only uses one cookie (session_id)
     581    const QString responseCookies = response.value( "Set-Cookie" );
     582    if( !responseCookies.isEmpty( ) )
     583        myCookies = responseCookies;
     584       
     585    if( response.statusCode() == 409 )
     586    {
     587        // we got a 409 telling us our session cookie has expired.
     588        // now that we've updated our cookie, try again.
     589        exec( qobject_cast<QBuffer*>(sourceDevice)->buffer().constData( ) );
     590    }
     591    else if( error )
     592    {
    573593        std::cerr << "http error: " << qPrintable(myHttp.errorString()) << std::endl;
    574     else {
     594    }
     595    else
     596    {
    575597        const QByteArray& response( myBuffer.buffer( ) );
    576598        const char * json( response.constData( ) );
    577599        int jsonLength( response.size( ) );
    578600        if( jsonLength>0 && json[jsonLength-1] == '\n' ) --jsonLength;
    579 
    580601        parseResponse( json, jsonLength );
    581602    }
    582603
     604    delete sourceDevice;
    583605    myBuffer.buffer( ).clear( );
    584606    myBuffer.reset( );
  • trunk/qt/session.h

    r8345 r8351  
    133133        tr_session * mySession;
    134134        QString myConfigDir;
     135        QString myCookies;
    135136        QUrl myUrl;
    136137        QBuffer myBuffer;
Note: See TracChangeset for help on using the changeset viewer.