static void requestNextChunk( tr_webseed * w ) { tr_torrent * tor = tr_torrentFindFromHash( w->session, w->hash ); if( tor != NULL ) { const tr_info * inf = tr_torrentInfo( tor ); const uint32_t have = EVBUFFER_LENGTH( w->content ); const uint32_t left = w->byteCount - have; const uint32_t pieceOffset = w->pieceOffset + have; tr_file_index_t fileIndex; uint64_t fileOffset; uint32_t thisPass; char * url; char * range; tr_ioFindFileLocation( tor, w->pieceIndex, pieceOffset, &fileIndex, &fileOffset ); thisPass = MIN( left, inf->files[fileIndex].length - fileOffset ); url = makeURL( w, &inf->files[fileIndex] ); /*fprintf( stderr, "url is [%s]\n", url );*/ range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 ); /*fprintf( stderr, "range is [%s] ... we want %lu total, we have %lu, so %lu are left, and we're asking for %lu this time\n", range, (unsigned long)w->byteCount, (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );*/ tr_webRun( w->session, url, range, webResponseFunc, w ); tr_free( range ); tr_free( url ); } }
static int readPeerId( tr_handshake * handshake, struct evbuffer * inbuf ) { tr_bool peerIsGood; char client[128]; tr_torrent * tor; const uint8_t * tor_peer_id; uint8_t peer_id[PEER_ID_LEN]; if( evbuffer_get_length( inbuf ) < PEER_ID_LEN ) return READ_LATER; /* peer id */ tr_peerIoReadBytes( handshake->io, inbuf, peer_id, PEER_ID_LEN ); tr_peerIoSetPeersId( handshake->io, peer_id ); handshake->havePeerID = TRUE; tr_clientForId( client, sizeof( client ), peer_id ); dbgmsg( handshake, "peer-id is [%s] ... isIncoming is %d", client, tr_peerIoIsIncoming( handshake->io ) ); /* if we've somehow connected to ourselves, don't keep the connection */ tor = tr_torrentFindFromHash( handshake->session, tr_peerIoGetTorrentHash( handshake->io ) ); tor_peer_id = tor && tor->peer_id ? tor->peer_id : tr_getPeerId( ); peerIsGood = memcmp( peer_id, tor_peer_id, PEER_ID_LEN ) != 0; dbgmsg( handshake, "isPeerGood == %d", (int)peerIsGood ); return tr_handshakeDone( handshake, peerIsGood ); }
static int updateAddresses( tr_tracker * t, int success ) { int retry; tr_torrent * torrent = tr_torrentFindFromHash( t->session, t->hash ); if( success ) { /* multitracker spec: "if a connection with a tracker is successful, it will be moved to the front of the tier." */ t->trackerIndex = tr_torrentPromoteTracker( torrent, t->trackerIndex ); retry = FALSE; /* we succeeded; no need to retry */ } else if( ++t->trackerIndex >= torrent->info.trackerCount ) { t->trackerIndex = 0; retry = FALSE; /* we've tried them all */ } else { const tr_tracker_info * n = getCurrentAddressFromTorrent( t, torrent ); tr_ninf( t->name, _( "Trying tracker \"%s\"" ), n->announce ); retry = TRUE; } return retry; }
static void buildHandshakeMessage( tr_handshake * handshake, uint8_t * buf ) { uint8_t * walk = buf; const uint8_t * torrentHash = tr_cryptoGetTorrentHash( handshake->crypto ); const tr_torrent * tor = tr_torrentFindFromHash( handshake->session, torrentHash ); const uint8_t * peer_id = tor && tor->peer_id ? tor->peer_id : tr_getPeerId( ); memcpy( walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN ); walk += HANDSHAKE_NAME_LEN; memset( walk, 0, HANDSHAKE_FLAGS_LEN ); HANDSHAKE_SET_LTEP( walk ); HANDSHAKE_SET_FASTEXT( walk ); /* Note that this doesn't depend on whether the torrent is private. * We don't accept DHT peers for a private torrent, * but we participate in the DHT regardless. */ if( tr_dhtEnabled( handshake->session ) ) HANDSHAKE_SET_DHT( walk ); walk += HANDSHAKE_FLAGS_LEN; memcpy( walk, torrentHash, SHA_DIGEST_LENGTH ); walk += SHA_DIGEST_LENGTH; memcpy( walk, peer_id, PEER_ID_LEN ); walk += PEER_ID_LEN; assert( strlen( ( const char* )peer_id ) == PEER_ID_LEN ); assert( walk - buf == HANDSHAKE_SIZE ); }
static int readPeerId (tr_handshake * handshake, struct evbuffer * inbuf) { bool connected_to_self; char client[128]; uint8_t peer_id[PEER_ID_LEN]; tr_torrent * tor; if (evbuffer_get_length (inbuf) < PEER_ID_LEN) return READ_LATER; /* peer id */ tr_peerIoReadBytes (handshake->io, inbuf, peer_id, PEER_ID_LEN); tr_peerIoSetPeersId (handshake->io, peer_id); handshake->havePeerID = true; tr_clientForId (client, sizeof (client), peer_id); dbgmsg (handshake, "peer-id is [%s] ... isIncoming is %d", client, tr_peerIoIsIncoming (handshake->io)); /* if we've somehow connected to ourselves, don't keep the connection */ tor = tr_torrentFindFromHash (handshake->session, tr_peerIoGetTorrentHash (handshake->io)); connected_to_self = (tor != NULL) && !memcmp (peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN); return tr_handshakeDone (handshake, !connected_to_self); }
static int parseHandshake (tr_handshake * handshake, struct evbuffer * inbuf) { uint8_t name[HANDSHAKE_NAME_LEN]; uint8_t reserved[HANDSHAKE_FLAGS_LEN]; uint8_t hash[SHA_DIGEST_LENGTH]; tr_torrent * tor; uint8_t peer_id[PEER_ID_LEN]; dbgmsg (handshake, "payload: need %d, got %zu", HANDSHAKE_SIZE, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < HANDSHAKE_SIZE) return READ_LATER; /* confirm the protocol */ tr_peerIoReadBytes (handshake->io, inbuf, name, HANDSHAKE_NAME_LEN); if (memcmp (name, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN)) return HANDSHAKE_ENCRYPTION_WRONG; /* read the reserved bytes */ tr_peerIoReadBytes (handshake->io, inbuf, reserved, HANDSHAKE_FLAGS_LEN); /* torrent hash */ tr_peerIoReadBytes (handshake->io, inbuf, hash, sizeof (hash)); assert (tr_peerIoHasTorrentHash (handshake->io)); if (!tr_torrentExists (handshake->session, hash) || memcmp (hash, tr_peerIoGetTorrentHash (handshake->io), SHA_DIGEST_LENGTH)) { dbgmsg (handshake, "peer returned the wrong hash. wtf?"); return HANDSHAKE_BAD_TORRENT; } /* peer_id */ tr_peerIoReadBytes (handshake->io, inbuf, peer_id, sizeof (peer_id)); tr_peerIoSetPeersId (handshake->io, peer_id); /* peer id */ handshake->havePeerID = true; dbgmsg (handshake, "peer-id is [%*.*s]", PEER_ID_LEN, PEER_ID_LEN, peer_id); tor = tr_torrentFindFromHash (handshake->session, hash); if (!memcmp (peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN)) { dbgmsg (handshake, "streuth! we've connected to ourselves."); return HANDSHAKE_PEER_IS_SELF; } /** *** Extensions **/ tr_peerIoEnableDHT (handshake->io, HANDSHAKE_HAS_DHT (reserved)); tr_peerIoEnableLTEP (handshake->io, HANDSHAKE_HAS_LTEP (reserved)); tr_peerIoEnableFEXT (handshake->io, HANDSHAKE_HAS_FASTEXT (reserved)); return HANDSHAKE_OK; }
static tr_tracker * findTracker( tr_session * session, const uint8_t * hash ) { tr_torrent * torrent = tr_torrentFindFromHash( session, hash ); return torrent ? torrent->tracker : NULL; }
static const tr_tracker_info * getCurrentAddress( tr_tracker * t ) { const tr_torrent * torrent; if( ( torrent = tr_torrentFindFromHash( t->session, t->hash ) ) ) return getCurrentAddressFromTorrent( t, torrent ); return NULL; }
static void gotError( tr_peerIo * io, short what, void * vhandshake ) { int errcode = errno; tr_handshake * handshake = vhandshake; if( io->utp_socket && !io->isIncoming && handshake->state == AWAITING_YB ) { /* This peer probably doesn't speak uTP. */ tr_torrent *tor = tr_peerIoHasTorrentHash( io ) ? tr_torrentFindFromHash( handshake->session, tr_peerIoGetTorrentHash( io ) ) : NULL; /* Don't mark a peer as non-uTP unless it's really a connect failure. */ if( tor && ( errcode == ETIMEDOUT || errcode == ECONNREFUSED ) ) { tr_torrentLock( tor ); tr_peerMgrSetUtpFailed( tor, tr_peerIoGetAddress( io, NULL ), TRUE ); tr_torrentUnlock( tor ); } if( !tr_peerIoReconnect( handshake->io ) ) { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); handshake->haveSentBitTorrentHandshake = 1; setReadState( handshake, AWAITING_HANDSHAKE ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); } } /* if the error happened while we were sending a public key, we might * have encountered a peer that doesn't do encryption... reconnect and * try a plaintext handshake */ if( ( ( handshake->state == AWAITING_YB ) || ( handshake->state == AWAITING_VC ) ) && ( handshake->encryptionMode != TR_ENCRYPTION_REQUIRED ) && ( !tr_peerIoReconnect( handshake->io ) ) ) { uint8_t msg[HANDSHAKE_SIZE]; dbgmsg( handshake, "handshake failed, trying plaintext..." ); buildHandshakeMessage( handshake, msg ); handshake->haveSentBitTorrentHandshake = 1; setReadState( handshake, AWAITING_HANDSHAKE ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); } else { dbgmsg( handshake, "libevent got an error what==%d, errno=%d (%s)", (int)what, errno, tr_strerror( errno ) ); tr_handshakeDone( handshake, FALSE ); } }
static bool buildHandshakeMessage (tr_handshake * handshake, uint8_t * buf) { const unsigned char * peer_id = NULL; const uint8_t * torrentHash; tr_torrent * tor; bool success; if ((torrentHash = tr_cryptoGetTorrentHash (handshake->crypto))) if ((tor = tr_torrentFindFromHash (handshake->session, torrentHash))) peer_id = tr_torrentGetPeerId (tor); if (peer_id == NULL) { success = false; } else { uint8_t * walk = buf; memcpy (walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN); walk += HANDSHAKE_NAME_LEN; memset (walk, 0, HANDSHAKE_FLAGS_LEN); HANDSHAKE_SET_LTEP (walk); HANDSHAKE_SET_FASTEXT (walk); /* Note that this doesn't depend on whether the torrent is private. * We don't accept DHT peers for a private torrent, * but we participate in the DHT regardless. */ if (tr_dhtEnabled (handshake->session)) HANDSHAKE_SET_DHT (walk); walk += HANDSHAKE_FLAGS_LEN; memcpy (walk, torrentHash, SHA_DIGEST_LENGTH); walk += SHA_DIGEST_LENGTH; memcpy (walk, peer_id, PEER_ID_LEN); walk += PEER_ID_LEN; assert (walk - buf == HANDSHAKE_SIZE); success = true; } return success; }
static void webResponseFunc( tr_session * session, long response_code, const void * response, size_t response_byte_count, void * vw ) { tr_webseed * w = vw; tr_torrent * tor = tr_torrentFindFromHash( session, w->hash ); const int success = ( response_code == 206 ); /*fprintf( stderr, "server responded with code %ld and %lu bytes\n", response_code, (unsigned long)response_byte_count );*/ if( !success ) { /* FIXME */ } else if( tor != NULL ) { evbuffer_add( w->content, response, response_byte_count ); if( !w->dead ) { fireClientGotData( w, response_byte_count ); tr_rcTransferred( &w->rateDown, response_byte_count ); } if( EVBUFFER_LENGTH( w->content ) < w->byteCount ) requestNextChunk( w ); else { tr_ioWrite( tor, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) ); evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) ); w->busy = 0; if( w->dead ) tr_webseedFree( w ); else { fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount ); fireNeedReq( w ); } } } }
case TR_DHT_BROKEN: return "broken"; case TR_DHT_POOR: return "poor"; case TR_DHT_FIREWALLED: return "firewalled"; case TR_DHT_GOOD: return "good"; default: return "???"; } } static void callback( void *ignore UNUSED, int event, unsigned char *info_hash, void *data, size_t data_len ) { if( event == DHT_EVENT_VALUES || event == DHT_EVENT_VALUES6 ) { tr_torrent *tor; tr_sessionLock( session ); tor = tr_torrentFindFromHash( session, info_hash ); if( tor && tr_torrentAllowsDHT( tor )) { size_t i, n; tr_pex * pex; if( event == DHT_EVENT_VALUES ) pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n); else pex = tr_peerMgrCompact6ToPex(data, data_len, NULL, 0, &n); for( i=0; i<n; ++i ) tr_peerMgrAddPex( tor, TR_PEER_FROM_DHT, pex+i, -1 ); tr_free(pex); tr_tordbg(tor, "Learned %d%s peers from DHT", (int)n, event == DHT_EVENT_VALUES6 ? " IPv6" : ""); }