diff --git a/doc/encryption.txt b/doc/encryption.txt new file mode 100644 index 000000000..b59a2f7f9 --- /dev/null +++ b/doc/encryption.txt @@ -0,0 +1,175 @@ +###################################################### +### ----- Message Stream Encryption protocol ----- ### +### specification by Ludde/uau/The_8472/Parg/Nolar ### +###################################################### + +The following protocol describes a transparent wrapper for bidirectional +data streams (e.g. TCP transports) that prevents passive eavesdroping +and thus protocol or content identification. + +It is also designed to provide limited protection against active MITM attacks +and portscanning by requiring a weak shared secret to complete the handshake. +You should note that the major design goal was payload and protocol obfuscation, +not peer authentication and data integrity verification. Thus it does not offer +protection against adversaries which already know the necessary data to establish +connections (that is IP/Port/Shared Secret/Payload protocol). + +To minimize the load on systems that employ this protocol fast cryptographic +methods have been chosen over maximum-security algorithms. + + +---------------- +- Declarations - +---------------- + +The entire handshake is in big-endian. +The crypto handshake is transparent to the next upper protocol, +thus the payload endianness doesn't matter. + + +A is the initiator of the underlying transport (e.g. a TCP connection) +B is the receiver + +##### DH Parameters + +Prime P is a 768 bit safe prime, "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563" +Generator G is "2" + +Xa and Xb are a variable size random integers. +They are the private key for each side and have to be discarded after +the DH handshake is done. Minimum length is 128 bit. Anything beyond 180 bit +is not believed to add any further security and only increases the necessary +calculation time. You should use a length of 160bits whenever possible, lower +values may be used when CPU time is scarce. + +Pubkey of A: Ya = (G^Xa) mod P +Pubkey of B: Yb = (G^Xb) mod P + +DH secret: S = (Ya^Xb) mod P = (Yb^Xa) mod P + +P, S, Ya and Yb are 768bits long + +##### Constants/Variables + + +PadA, PadB: Random data with a random length of 0 to 512 bytes each + +PadC, PadD: Arbitrary data with a length of 0 to 512 bytes, can be +used to extend the crypto handshake in future versions. +Current implementations may choose to set them to 0-length. +For padding-only usage in the current version they should be zeroed. + + +VC is a verification constant that is used to verify whether the other +side knows S and SKEY and thus defeats replay attacks of the SKEY hash. +As of this version VC is a String of 8 bytes set to 0x00. + + +crypto_provide and crypto_select are a 32bit bitfields. +As of now 0x01 means plaintext, 0x02 means RC4. (see Functions) +The remaining bits are reserved for future use. + +The initiating peer A should provide all methods he supports in the bitfield, +but he may choose only to provide higher encryption levels e.g. if plaintext +isn't sufficient for it's security needs. +The responding peer B should set a bit corresponding to the single method +which he selected from the provided ones. + +Bits with an unknown meaning in crypto_provide and crypto_select +should be ignored as they might be used in future versions. + + + + + +SKEY = Stream Identifier/Shared secret used to drop connections early if we +don't have a matching stream. It's additionally used to harden the protocol +against MITM attacks and portscanning. +Protocols w/o unique stream properties may use a constant. + Note: For BitTorrent, the SKEY should be the torrent info hash. + + +IA = initial payload data from A +may be 0-sized if you want to wait for the encryption negotation. + +Peer A may buffer up to 65535 bytes before or during the DH handshake to append +it to the 3rd step. IA is considered as atomic and thus an implementation may +not expect that anything is handed to the upper layer before IA is completely +transmitted. Thus there must be no blocking operations within IA. + +Note, Example for Bittorrent: + After \19Bittorrent protocol + the BT handshake a block occurs since A waits + for B to send his handshake before A continues to send his bitfield, + thus IA can only include the prefix + the bt handshake but not the bitfield + + +###### Functions + +len(X) specifies the length of X in 2 bytes. +Thus the maximum length that can be specified is 65535 bytes, this is +important for the IA block. + + +ENCRYPT() is RC4, that uses one of the following keys to send data: +"HASH('keyA', S, SKEY)" if you're A +"HASH('keyB', S, SKEY)" if you're B +The first 1024 bytes of the RC4 output are discarded. +consecutive calls to ENCRYPT() by one side continue the encryption +stream (no reinitialization, no keychange). They are only used to distinguish +semantically seperate content. + + +ENCRYPT2() is the negotiated crypto method. +Current options are: + 0x01 Plaintext. After the specified length (see IA/IB) each side sends unencrypted payload + 0x02 RC4-128. The ENCRYPT() RC4 encoding is continued (no reinitialization, no keychange) + + +HASH() is SHA1 binary output (20 bytes) + + +###### The handshake "packet" format + + +The handshake is seperated into 5 blocking steps. + +1 A->B: Diffie Hellman Ya, PadA +2 B->A: Diffie Hellman Yb, PadB +3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) +4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream) +5 A->B: ENCRYPT2(Payload Stream) + + +Since the length of PadA and PadB are unknown +B will be able to resynchronize on HASH('req1', S) +A will be able to resynchronize on ENCRYPT(VC) + + +##### Optional early termination conditions +(should verified before the indicated step is started). + +If a fail-fast behavior is prefered the following conditions can be used to +disconnect the peer immediately. If less recognizable patterns are preferred +a peer may wait and disconnect at a later point. If any of these conditions +are met the handshake can be considered as invalid. + +2 (termination by B) +if A sent less than 96 Bytes within 30 seconds +if A sent more than 608 bytes + +3 (termination by A) +if B sent less than 96 Bytes within 30 seconds +if B sent more than 608 bytes + +4 (termination by B) +if A didn't send the correct S hash within 628 bytes after the connection start (synchronisation point) +if A didn't send a supported SKEY hash after the S hash +if VC can't be decoded correctly after the SKEY hash +if none of the crypto_provide options are supported or the bitfield is zeroed +from here on it's up to the next protocol layer to terminate the connection + +5 (termination by A) +if VC can't be decoded correctly within 616 bytes after the connection start (synchronisation point) +if the selected crypto method wasn't provided +from here on it's up to the next protocol layer to terminate the connection + diff --git a/doc/properties.txt b/doc/properties.txt new file mode 100644 index 000000000..a221209e6 --- /dev/null +++ b/doc/properties.txt @@ -0,0 +1,5 @@ +This file is here because I can never remember these commands... + +svn propset svn:keywords 'Date Rev Author Id' filename + +find . -type f \! -path \*.svn\* | xargs grep '\$Id\$' diff --git a/gtk/lock-icon.h b/gtk/lock-icon.h new file mode 100644 index 000000000..f51ddd2eb --- /dev/null +++ b/gtk/lock-icon.h @@ -0,0 +1,53 @@ +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +#ifdef __SUNPRO_C +#pragma align 4 (my_pixbuf) +#endif +#ifdef __GNUC__ +static const guint8 lock_icon[] __attribute__ ((__aligned__ (4))) = +#else +static const guint8 lock_icon[] = +#endif +{ "" + /* Pixbuf magic (0x47646b50) */ + "GdkP" + /* length: header (24) + pixel_data (643) */ + "\0\0\2\233" + /* pixdata_type (0x2010002) */ + "\2\1\0\2" + /* rowstride (64) */ + "\0\0\0@" + /* width (16) */ + "\0\0\0\20" + /* height (16) */ + "\0\0\0\20" + /* pixel_data: */ + "\225\0\0\0\0\2\0\0\0i\0\0\0\273\202\0\0\0\306\3\0\0\0\301\0\0\0;\0\0" + "\0\1\207\0\0\0\0\4\0\0\0\20\36\36\36\224zzz\373\322\322\322\377\202\333" + "\333\333\377\4\274\274\274\376rrr\361\10\10\10\206\0\0\0\3\206\0\0\0" + "\0\4\23\23\23~\204\204\204\374\276\276\276\367888\362\202\34\34\34\371" + "\4MMM\374\261\261\261\377\204\204\204\366\0\0\0o\206\0\0\0\0\3---\353" + "\302\302\302\375\11\11\11\336\204\0\0\0\0\3...\374\252\252\252\376\0" + "\0\0\275\206\0\0\0\0\3+++\374\300\300\300\374\0\0\0\334\204\0\0\0\0\3" + "\26\26\26\372\301\300\300\377\0\0\0\340\205\0\0\0\0\5\0\0\0`\23\21\16" + "\370\233\227\224\377\40\35\34\377\13\13\7\377\202\4\3\2\377\7\5\4\3\377" + "/)!\377\250\243\236\377\32\23\13\377\0\0\0`\0\0\0\0\0\0\0\1\202\0\0\0" + "\0\3\0\0\0\377\245\221v\377\340\304\240\377\202\337\304\243\377\203\332" + "\276\232\377\6\326\272\224\377\315\246v\377\201Z+\377\0\0\0\377\0\0\0" + "\0\0\0\0\4\202\0\0\0\0\5\0\0\0\377\342\305\241\377\330\265\214\377\312" + "\237h\377\254\207X\377\202\247\202R\377\7\247\202S\377\243{K\377\274" + "\210I\377\254q.\377\0\0\0\377\0\0\0\0\0\0\0\5\202\0\0\0\0\5\0\0\0\377" + "\343\306\242\377\325\256~\377\260\215_\377\325\262\204\377\202\317\253" + "|\377\5\317\254{\377\313\244q\377\274\207F\377\252o+\377\0\0\0\377\204" + "\0\0\0\0\5\0\0\0\377\341\304\240\377\320\246t\377\307\233d\377\252\204" + "U\377\203\244~N\377\4\240wD\377\272\204B\377\254q.\377\0\0\0\377\204" + "\0\0\0\0\5\0\0\0\377\341\302\234\377\316\244p\377\255\215d\377\322\262" + "\211\377\202\315\254\203\377\5\316\255\203\377\314\247{\377\270\200>" + "\377\255s2\377\0\0\0\377\204\0\0\0\0\5\0\0\0\377\340\302\236\377\324" + "\254y\377\315\246n\377\257\215^\377\202\253\207X\377\5\253\210X\377\250" + "\201L\377\274\207F\377\264~>\377\0\0\0\377\204\0\0\0\0\3\0\0\0\377\320" + "\250t\377\306\226]\377\202\276\215P\377\202\272\211K\377\5\275\214N\377" + "\274\212L\377\272\206G\377\244q6\377\0\0\0\377\204\0\0\0\0\1\0\0\0`\212" + "\0\0\0\377\1\0\0\0w\222\0\0\0\0"}; + + diff --git a/gtk/main.c b/gtk/main.c index 54bc98515..53dc8c306 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -166,8 +166,6 @@ boolwindclosed(GtkWidget *widget, gpointer gdata); static GList * getselection( struct cbdata * cbdata ); -static void -safepipe(void); static void setupsighandlers(void); static void @@ -228,7 +226,6 @@ main( int argc, char ** argv ) GList * argfiles; gboolean didinit, didlock, sendquit, startpaused; - safepipe(); /* ignore SIGPIPE */ argfiles = readargs( argc, argv, &sendquit, &startpaused ); didinit = cf_init( tr_getPrefsDirectory(), NULL ); didlock = FALSE; @@ -1106,15 +1103,6 @@ doAction ( const char * action_name, gpointer user_data ) } -static void -safepipe(void) { - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); -} - static void setupsighandlers(void) { int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM}; diff --git a/gtk/torrent-inspector.c b/gtk/torrent-inspector.c index 92d2c29c5..3a1fcb98d 100644 --- a/gtk/torrent-inspector.c +++ b/gtk/torrent-inspector.c @@ -33,6 +33,7 @@ #include "actions.h" #include "tr_torrent.h" #include "dot-icons.h" +#include "lock-icon.h" #include "hig.h" #include "torrent-inspector.h" #include "util.h" @@ -236,6 +237,7 @@ enum PEER_COL_CLIENT, PEER_COL_PROGRESS, PEER_COL_IS_CONNECTED, + PEER_COL_IS_ENCRYPTED, PEER_COL_IS_DOWNLOADING, PEER_COL_DOWNLOAD_RATE, PEER_COL_IS_UPLOADING, @@ -250,6 +252,7 @@ static const char* peer_column_names[N_PEER_COLS] = N_("Client"), N_("Progress"), " ", + " ", N_("Downloading"), N_("DL Rate"), N_("Uploading"), @@ -283,6 +286,7 @@ peer_row_set (GtkTreeStore * store, PEER_COL_ADDRESS, peer->addr, PEER_COL_PORT, peer->port, PEER_COL_CLIENT, client, + PEER_COL_IS_ENCRYPTED, peer->isEncrypted, PEER_COL_PROGRESS, (int)(100.0*peer->progress + 0.5), PEER_COL_IS_CONNECTED, peer->isConnected, PEER_COL_IS_DOWNLOADING, peer->isDownloading, @@ -314,6 +318,7 @@ peer_model_new (tr_torrent_t * tor) G_TYPE_STRING, /* client */ G_TYPE_INT, /* progress [0..100] */ G_TYPE_BOOLEAN, /* isConnected */ + G_TYPE_BOOLEAN, /* isEncrypted */ G_TYPE_BOOLEAN, /* isDownloading */ G_TYPE_FLOAT, /* downloadFromRate */ G_TYPE_BOOLEAN, /* isUploading */ @@ -347,6 +352,24 @@ render_connection (GtkTreeViewColumn * column UNUSED, NULL); } +static void +render_encrypted (GtkTreeViewColumn * column UNUSED, + GtkCellRenderer * renderer, + GtkTreeModel * tree_model, + GtkTreeIter * iter, + gpointer data UNUSED) +{ + static GdkPixbuf * lock = NULL; + gboolean is_encrypted = FALSE; + gtk_tree_model_get (tree_model, iter, PEER_COL_IS_ENCRYPTED, &is_encrypted, + -1); + if (!lock) lock = gdk_pixbuf_new_from_inline (-1, lock_icon, FALSE, NULL); + g_object_set (renderer, "xalign", (gfloat)0.0, + "yalign", (gfloat)0.5, + "pixbuf", (is_encrypted ? lock : NULL), + NULL); +} + static void render_ul_rate (GtkTreeViewColumn * column UNUSED, GtkCellRenderer * renderer, @@ -494,6 +517,7 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor ) /* TODO: make this configurable? */ int view_columns[] = { PEER_COL_IS_CONNECTED, + PEER_COL_IS_ENCRYPTED, PEER_COL_ADDRESS, PEER_COL_CLIENT, PEER_COL_PROGRESS, @@ -538,6 +562,16 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor ) _("Progress"), r, "value", PEER_COL_PROGRESS, NULL); break; + case PEER_COL_IS_ENCRYPTED: + resizable = FALSE; + r = gtk_cell_renderer_pixbuf_new (); + c = gtk_tree_view_column_new_with_attributes (t, r, NULL); + gtk_tree_view_column_set_sizing (c, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (c, 32); + gtk_tree_view_column_set_cell_data_func (c, r, render_encrypted, + NULL, NULL); + break; + case PEER_COL_IS_CONNECTED: resizable = FALSE; r = gtk_cell_renderer_pixbuf_new (); diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index 5fafb1730..44768677a 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -9,12 +9,13 @@ EXTRA_DIST = version.h.in libtransmission_a_SOURCES = \ basename.c \ bencode.c \ - choking.c \ clients.c \ completion.c \ + crypto.c \ dirname.c \ fastresume.c \ fdlimit.c \ + handshake.c \ http.c \ inout.c \ ipcparse.c \ @@ -23,16 +24,16 @@ libtransmission_a_SOURCES = \ metainfo.c \ natpmp.c \ net.c \ - peer.c \ + peer-io.c \ + peer-mgr.c \ + peer-msgs.c \ platform.c \ ptrarray.c \ publish.c \ ratecontrol.c \ - sha1.c \ shared.c \ strlcat.c \ strlcpy.c \ - timer.c \ torrent.c \ tracker.c \ transmission.c \ @@ -45,11 +46,12 @@ noinst_HEADERS = \ bencode.h \ bsdqueue.h \ bsdtree.h \ - choking.h \ clients.h \ + crypto.h \ completion.h \ fastresume.h \ fdlimit.h \ + handshake.h \ http.h \ inout.h \ internal.h \ @@ -59,20 +61,15 @@ noinst_HEADERS = \ metainfo.h \ natpmp.h \ net.h \ - peeraz.h \ - peerext.h \ - peer.h \ - peermessages.h \ - peerparse.h \ - peertree.h \ - peerutils.h \ + peer-io.h \ + peer-mgr.h \ + peer-mgr-private.h \ + peer-msgs.h \ platform.h \ ptrarray.h \ publish.h \ ratecontrol.h \ - sha1.h \ shared.h \ - timer.h \ tracker.h \ transmission.h \ trcompat.h \ diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c index 8ff3ef37c..689ca301d 100644 --- a/libtransmission/bencode.c +++ b/libtransmission/bencode.c @@ -364,7 +364,7 @@ benc_val_t * tr_bencListAdd( benc_val_t * list ) { benc_val_t * item; - assert( TYPE_LIST == list->type ); + assert( tr_bencIsList( list ) ); assert( list->val.l.count < list->val.l.alloc ); item = &list->val.l.vals[list->val.l.count]; @@ -378,7 +378,7 @@ benc_val_t * tr_bencDictAdd( benc_val_t * dict, const char * key ) { benc_val_t * keyval, * itemval; - assert( TYPE_DICT == dict->type ); + assert( tr_bencIsDict( dict ) ); assert( dict->val.l.count + 2 <= dict->val.l.alloc ); keyval = dict->val.l.vals + dict->val.l.count++; @@ -479,3 +479,31 @@ int tr_bencSave( benc_val_t * val, char ** buf, int * used, int * max ) return 0; } + +/** +*** +**/ + +int +tr_bencIsStr ( const benc_val_t * val ) +{ + return val!=NULL && val->type==TYPE_STR; +} + +int +tr_bencIsInt ( const benc_val_t * val ) +{ + return val!=NULL && val->type==TYPE_INT; +} + +int +tr_bencIsList( const benc_val_t * val ) +{ + return val!=NULL && val->type==TYPE_LIST; +} + +int +tr_bencIsDict( const benc_val_t * val ) +{ + return val!=NULL && val->type==TYPE_DICT; +} diff --git a/libtransmission/bencode.h b/libtransmission/bencode.h index 9b430701a..b17987788 100644 --- a/libtransmission/bencode.h +++ b/libtransmission/bencode.h @@ -90,4 +90,9 @@ char * tr_bencSaveMalloc( benc_val_t * val, int * len ); int tr_bencSave( benc_val_t * val, char ** buf, int * used, int * max ); +int tr_bencIsStr ( const benc_val_t * val ); +int tr_bencIsInt ( const benc_val_t * val ); +int tr_bencIsList ( const benc_val_t * val ); +int tr_bencIsDict ( const benc_val_t * val ); + #endif diff --git a/libtransmission/choking.c b/libtransmission/choking.c deleted file mode 100644 index a5a2cdad8..000000000 --- a/libtransmission/choking.c +++ /dev/null @@ -1,323 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Copyright (c) 2006 Transmission authors and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *****************************************************************************/ - -#include -#include -#include - -#include "transmission.h" -#include "choking.h" -#include "peer.h" -#include "platform.h" -#include "utils.h" - -#ifndef HAVE_LRINTF -# define lrintf(a) ((int)(0.5+(a))) -#endif - -struct tr_choking_s -{ - tr_lock_t * lock; - tr_handle_t * h; - int slots; -}; - -tr_choking_t * tr_chokingInit( tr_handle_t * h ) -{ - tr_choking_t * c; - - c = tr_new0( tr_choking_t, 1 ); - c->h = h; - c->slots = 4242; - c->lock = tr_lockNew( ); - - return c; -} - -void tr_chokingSetLimit( tr_choking_t * c, int limit ) -{ - tr_lockLock( c->lock ); - if( limit < 0 ) - c->slots = 4242; - else - /* Reckon a number of slots from the upload limit. There is no - official right way to do this, the formula below e.g. gives: - 10 KB/s -> 4 * 2.50 KB/s - 20 KB/s -> 6 * 3.33 KB/s - 50 KB/s -> 10 * 5.00 KB/s - 100 KB/s -> 14 * 7.14 KB/s */ - c->slots = lrintf( sqrt( 2 * limit ) ); - tr_lockUnlock( c->lock ); -} - -#define sortPeersAscending(a,ac,z,zc,n,nc) sortPeers(a,ac,z,zc,n,nc,0) -#define sortPeersDescending(a,ac,z,zc,n,nc) sortPeers(a,ac,z,zc,n,nc,1) -static void sortPeers( tr_peer_t ** all, int allCount, - tr_peer_t ** zero, int * zeroCount, - tr_peer_t ** nonZero, int * nonZeroCount, - int order ) -{ - int i, shuffle; - - /* Seperate uploaders from non-uploaders */ - *zeroCount = 0; - *nonZeroCount = 0; - for( i = 0; i < allCount; i++ ) - { - if( tr_peerDownloadRate( all[i] ) < 0.1 ) - zero[(*zeroCount)++] = all[i]; - else - nonZero[(*nonZeroCount)++] = all[i]; - } - - /* Randomly shuffle non-uploaders, so they are treated equally */ - if( *zeroCount && ( shuffle = tr_rand( *zeroCount ) ) ) - { - tr_peer_t ** bak = tr_new( tr_peer_t*, shuffle );; - memcpy( bak, zero, shuffle * sizeof( tr_peer_t * ) ); - memmove( zero, &zero[shuffle], - ( *zeroCount - shuffle ) * sizeof( tr_peer_t * ) ); - memcpy( &zero[*zeroCount - shuffle], bak, - shuffle * sizeof( tr_peer_t * ) ); - tr_free( bak ); - } - - /* Sort uploaders by download rate */ - for( i = *nonZeroCount - 1; i > 0; i-- ) - { - float rate1, rate2; - tr_peer_t * tmp; - int j, sorted; - - sorted = 1; - for( j = 0; j < i; j++ ) - { - rate1 = tr_peerDownloadRate( nonZero[j] ); - rate2 = tr_peerDownloadRate( nonZero[j+1] ); - if( order ? ( rate1 < rate2 ) : ( rate1 > rate2 ) ) - { - tmp = nonZero[j]; - nonZero[j] = nonZero[j+1]; - nonZero[j+1] = tmp; - sorted = 0; - } - } - if( sorted ) - break; - } -} - -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_torrentWriterLock( tor ); - peersTotalCount += tor->peerCount; - } - - canChoke = tr_new( tr_peer_t*, peersTotalCount ); - canUnchoke = tr_new( tr_peer_t*, peersTotalCount ); - 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_peerIsChokedByUs( 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_peerIsChokedByUs( peer ) ) - { - if( !tr_peerTimesChoked(peer) || tr_peerLastChoke( peer ) + 10000 < now ) - canUnchoke[canUnchokeCount++] = peer; - } - else - { - 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; - } - } - } - - canChokeZero = tr_new( tr_peer_t*, canChokeCount ); - canChokeNonZero = tr_new( tr_peer_t*, canChokeCount ); - canUnchokeZero = tr_new( tr_peer_t*, canUnchokeCount ); - canUnchokeNonZero = tr_new( tr_peer_t*, canUnchokeCount ); - - sortPeersDescending( canChoke, canChokeCount, - canChokeZero, &canChokeZeroCount, - canChokeNonZero, &canChokeNonZeroCount); - sortPeersAscending( canUnchoke, canUnchokeCount, - canUnchokeZero, &canUnchokeZeroCount, - canUnchokeNonZero, &canUnchokeNonZeroCount); - - tr_free( canChoke ); - tr_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] ); - } - - tr_free( canChokeZero ); - tr_free( canChokeNonZero ); - tr_free( canUnchokeZero ); - tr_free( canUnchokeNonZero ); - - /* Unlock all torrents */ - for( tor = c->h->torrentList; tor; tor = tor->next ) - tr_torrentWriterUnlock( tor ); - - tr_lockUnlock( c->lock ); -} - -void tr_chokingClose( tr_choking_t * c ) -{ - tr_lockFree( c->lock ); - tr_free( c ); -} diff --git a/libtransmission/clients.c b/libtransmission/clients.c index 150ee1dad..ce4d9e944 100644 --- a/libtransmission/clients.c +++ b/libtransmission/clients.c @@ -44,7 +44,7 @@ static int charToInt( char character ) return value; } -char * tr_clientForId( uint8_t * id ) +char * tr_clientForId( const uint8_t * id ) { char * ret = NULL; diff --git a/libtransmission/clients.h b/libtransmission/clients.h index aa85aa195..16a4cefb9 100644 --- a/libtransmission/clients.h +++ b/libtransmission/clients.h @@ -22,4 +22,4 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -char * tr_clientForId( uint8_t * ); +char * tr_clientForId( const uint8_t * ); diff --git a/libtransmission/completion.c b/libtransmission/completion.c index 512f50874..e0688d41d 100644 --- a/libtransmission/completion.c +++ b/libtransmission/completion.c @@ -29,18 +29,15 @@ #include "completion.h" #include "utils.h" -struct tr_completion_s +struct tr_completion { - tr_torrent_t * tor; - - /* true if a peer is requesting this block */ - tr_bitfield_t * blockRequested; + tr_torrent * tor; /* do we have this block? */ - tr_bitfield_t * blockBitfield; + tr_bitfield * blockBitfield; /* do we have this piece? */ - tr_bitfield_t * pieceBitfield; + tr_bitfield * pieceBitfield; /* a block is complete if and only if we have it */ uint16_t * completeBlocks; @@ -51,14 +48,13 @@ struct tr_completion_s uint64_t completeHave; }; -tr_completion_t * tr_cpInit( tr_torrent_t * tor ) +tr_completion * tr_cpInit( tr_torrent * tor ) { - tr_completion_t * cp; + tr_completion * cp; - cp = tr_new( tr_completion_t, 1 ); + cp = tr_new( tr_completion, 1 ); cp->tor = tor; cp->blockBitfield = tr_bitfieldNew( tor->blockCount ); - cp->blockRequested = tr_bitfieldNew( tor->blockCount ); cp->pieceBitfield = tr_bitfieldNew( tor->info.pieceCount ); cp->completeBlocks = tr_new( uint16_t, tor->info.pieceCount ); @@ -67,22 +63,20 @@ tr_completion_t * tr_cpInit( tr_torrent_t * tor ) return cp; } -void tr_cpClose( tr_completion_t * cp ) +void tr_cpClose( tr_completion * cp ) { tr_free( cp->completeBlocks ); tr_bitfieldFree( cp->pieceBitfield ); - tr_bitfieldFree( cp->blockRequested ); tr_bitfieldFree( cp->blockBitfield ); tr_free( cp ); } -void tr_cpReset( tr_completion_t * cp ) +void tr_cpReset( tr_completion * cp ) { - tr_torrent_t * tor = cp->tor; + tr_torrent * tor = cp->tor; tr_bitfieldClear( cp->pieceBitfield ); tr_bitfieldClear( cp->blockBitfield ); - tr_bitfieldClear( cp->blockRequested ); memset( cp->completeBlocks, 0, sizeof(uint16_t) * tor->info.pieceCount ); cp->doneDirty = TRUE; @@ -96,19 +90,19 @@ void tr_cpReset( tr_completion_t * cp ) **/ static void -tr_cpEnsureDoneValid( const tr_completion_t * ccp ) +tr_cpEnsureDoneValid( const tr_completion * ccp ) { - const tr_torrent_t * tor = ccp->tor; + const tr_torrent * tor = ccp->tor; const tr_info_t * info = &tor->info; uint64_t have=0, total=0; int i; - tr_completion_t * cp ; + tr_completion * cp ; if( !ccp->doneDirty ) return; /* too bad C doesn't have 'mutable' */ - cp = (tr_completion_t*) ccp; + cp = (tr_completion*) ccp; cp->doneDirty = FALSE; for( i=0; ipieceCount; ++i ) { @@ -135,24 +129,25 @@ tr_cpEnsureDoneValid( const tr_completion_t * ccp ) } void -tr_cpInvalidateDND ( tr_completion_t * cp ) +tr_cpInvalidateDND ( tr_completion * cp ) { cp->doneDirty = TRUE; } -int tr_cpPieceIsComplete( const tr_completion_t * cp, int piece ) +int +tr_cpPieceIsComplete( const tr_completion * cp, int piece ) { return cp->completeBlocks[piece] >= tr_torPieceCountBlocks(cp->tor,piece); } -const tr_bitfield_t * tr_cpPieceBitfield( const tr_completion_t * cp ) +const tr_bitfield * tr_cpPieceBitfield( const tr_completion * cp ) { return cp->pieceBitfield; } -void tr_cpPieceAdd( tr_completion_t * cp, int piece ) +void tr_cpPieceAdd( tr_completion * cp, int piece ) { - const tr_torrent_t * tor = cp->tor; + const tr_torrent * tor = cp->tor; const int start = tr_torPieceFirstBlock(tor,piece); const int end = start + tr_torPieceCountBlocks(tor,piece); int i; @@ -161,9 +156,9 @@ void tr_cpPieceAdd( tr_completion_t * cp, int piece ) tr_cpBlockAdd( cp, i ); } -void tr_cpPieceRem( tr_completion_t * cp, int piece ) +void tr_cpPieceRem( tr_completion * cp, int piece ) { - const tr_torrent_t * tor = cp->tor; + const tr_torrent * tor = cp->tor; const int start = tr_torPieceFirstBlock(tor,piece); const int end = start + tr_torPieceCountBlocks(tor,piece); int block; @@ -190,26 +185,15 @@ void tr_cpPieceRem( tr_completion_t * cp, int piece ) tr_bitfieldRem( cp->pieceBitfield, piece ); } -/* Blocks */ -void tr_cpDownloaderAdd( tr_completion_t * cp, int block ) +int tr_cpBlockIsComplete( const tr_completion * cp, int block ) { - tr_bitfieldAdd( cp->blockRequested, block ); -} - -void tr_cpDownloaderRem( tr_completion_t * cp, int block ) -{ - tr_bitfieldRem( cp->blockRequested, block ); -} - -int tr_cpBlockIsComplete( const tr_completion_t * cp, int block ) -{ - return tr_bitfieldHas( cp->blockBitfield, block ); + return tr_bitfieldHas( cp->blockBitfield, block ) ? 1 : 0; } void -tr_cpBlockAdd( tr_completion_t * cp, int block ) +tr_cpBlockAdd( tr_completion * cp, int block ) { - const tr_torrent_t * tor = cp->tor; + const tr_torrent * tor = cp->tor; if( !tr_cpBlockIsComplete( cp, block ) ) { @@ -230,7 +214,7 @@ tr_cpBlockAdd( tr_completion_t * cp, int block ) } } -const tr_bitfield_t * tr_cpBlockBitfield( const tr_completion_t * cp ) +const tr_bitfield * tr_cpBlockBitfield( const tr_completion * cp ) { assert( cp != NULL ); @@ -238,7 +222,7 @@ const tr_bitfield_t * tr_cpBlockBitfield( const tr_completion_t * cp ) } void -tr_cpBlockBitfieldSet( tr_completion_t * cp, tr_bitfield_t * bitfield ) +tr_cpBlockBitfieldSet( tr_completion * cp, tr_bitfield * bitfield ) { int i; @@ -252,62 +236,31 @@ tr_cpBlockBitfieldSet( tr_completion_t * cp, tr_bitfield_t * bitfield ) tr_cpBlockAdd( cp, i ); } -float tr_cpPercentBlocksInPiece( const tr_completion_t * cp, int piece ) +float tr_cpPercentBlocksInPiece( const tr_completion * cp, int piece ) { assert( cp != NULL ); return (double)cp->completeBlocks[piece] / tr_torPieceCountBlocks(cp->tor,piece); } -int -tr_cpMissingBlocksForPiece( const tr_completion_t * cp, int piece ) -{ - int i; - int n; - const tr_torrent_t * tor = cp->tor; - const int start = tr_torPieceFirstBlock(tor,piece); - const int end = start + tr_torPieceCountBlocks(tor,piece); - - n = 0; - for( i = start; i < end; ++i ) - if( !tr_cpBlockIsComplete( cp, i ) && !tr_bitfieldHas( cp->blockRequested, i ) ) - ++n; - - return n; -} - -int tr_cpMissingBlockInPiece( const tr_completion_t * cp, int piece ) -{ - int i; - const tr_torrent_t * tor = cp->tor; - const int start = tr_torPieceFirstBlock(tor,piece); - const int end = start + tr_torPieceCountBlocks(tor,piece); - - for( i = start; i < end; ++i ) - if( !tr_cpBlockIsComplete( cp, i ) && !tr_bitfieldHas( cp->blockRequested, i ) ) - return i; - - return -1; -} - /*** **** ***/ float -tr_cpPercentComplete ( const tr_completion_t * cp ) +tr_cpPercentComplete ( const tr_completion * cp ) { return (double)cp->completeHave / cp->tor->info.totalSize; } uint64_t -tr_cpLeftUntilComplete ( const tr_completion_t * cp ) +tr_cpLeftUntilComplete ( const tr_completion * cp ) { return cp->tor->info.totalSize - cp->completeHave; } float -tr_cpPercentDone( const tr_completion_t * cp ) +tr_cpPercentDone( const tr_completion * cp ) { tr_cpEnsureDoneValid( cp ); @@ -315,7 +268,7 @@ tr_cpPercentDone( const tr_completion_t * cp ) } uint64_t -tr_cpLeftUntilDone ( const tr_completion_t * cp ) +tr_cpLeftUntilDone ( const tr_completion * cp ) { tr_cpEnsureDoneValid( cp ); @@ -323,7 +276,7 @@ tr_cpLeftUntilDone ( const tr_completion_t * cp ) } cp_status_t -tr_cpGetStatus ( const tr_completion_t * cp ) +tr_cpGetStatus ( const tr_completion * cp ) { if( cp->completeHave >= cp->tor->info.totalSize ) return TR_CP_COMPLETE; @@ -337,10 +290,10 @@ tr_cpGetStatus ( const tr_completion_t * cp ) } uint64_t -tr_cpDownloadedValid( const tr_completion_t * cp ) +tr_cpDownloadedValid( const tr_completion * cp ) { uint64_t b = 0; - const tr_torrent_t * tor = cp->tor; + const tr_torrent * tor = cp->tor; const tr_info_t * info = &tor->info; int i; diff --git a/libtransmission/completion.h b/libtransmission/completion.h index 2ec194e42..09a98d945 100644 --- a/libtransmission/completion.h +++ b/libtransmission/completion.h @@ -27,39 +27,34 @@ #include "transmission.h" -typedef struct tr_completion_s tr_completion_t; +typedef struct tr_completion tr_completion; -tr_completion_t * tr_cpInit( tr_torrent_t * ); -void tr_cpClose( tr_completion_t * ); -void tr_cpReset( tr_completion_t * ); +tr_completion * tr_cpInit( tr_torrent * ); +void tr_cpClose( tr_completion * ); +void tr_cpReset( tr_completion * ); /* General */ -cp_status_t tr_cpGetStatus ( const tr_completion_t * ); -uint64_t tr_cpDownloadedValid( const tr_completion_t * ); -uint64_t tr_cpLeftUntilComplete( const tr_completion_t * ); -uint64_t tr_cpLeftUntilDone( const tr_completion_t * ); -float tr_cpPercentComplete( const tr_completion_t * ); -float tr_cpPercentDone( const tr_completion_t * ); -void tr_cpInvalidateDND ( tr_completion_t * ); +cp_status_t tr_cpGetStatus ( const tr_completion * ); +uint64_t tr_cpDownloadedValid( const tr_completion * ); +uint64_t tr_cpLeftUntilComplete( const tr_completion * ); +uint64_t tr_cpLeftUntilDone( const tr_completion * ); +float tr_cpPercentComplete( const tr_completion * ); +float tr_cpPercentDone( const tr_completion * ); +void tr_cpInvalidateDND ( tr_completion * ); /* Pieces */ -int tr_cpPieceIsComplete( const tr_completion_t *, int piece ); -void tr_cpPieceAdd( tr_completion_t *, int piece ); -void tr_cpPieceRem( tr_completion_t *, int piece ); +int tr_cpPieceIsComplete( const tr_completion *, int piece ); +void tr_cpPieceAdd( tr_completion *, int piece ); +void tr_cpPieceRem( tr_completion *, int piece ); /* Blocks */ -void tr_cpDownloaderAdd( tr_completion_t *, int block ); -void tr_cpDownloaderRem( tr_completion_t *, int block ); -int tr_cpBlockIsComplete( const tr_completion_t *, int block ); -void tr_cpBlockAdd( tr_completion_t *, int block ); -void tr_cpBlockBitfieldSet( tr_completion_t *, struct tr_bitfield_s * ); -float tr_cpPercentBlocksInPiece( const tr_completion_t * cp, int piece ); -/* Missing = we don't have it and we are not getting it from any peer yet */ -int tr_cpMissingBlocksForPiece( const tr_completion_t * cp, int piece ); -int tr_cpMissingBlockInPiece( const tr_completion_t *, int piece ); +int tr_cpBlockIsComplete( const tr_completion *, int block ); +void tr_cpBlockAdd( tr_completion *, int block ); +void tr_cpBlockBitfieldSet( tr_completion *, struct tr_bitfield * ); +float tr_cpPercentBlocksInPiece( const tr_completion * cp, int piece ); -const struct tr_bitfield_s * tr_cpPieceBitfield( const tr_completion_t* ); -const struct tr_bitfield_s * tr_cpBlockBitfield( const tr_completion_t * ); +const struct tr_bitfield * tr_cpPieceBitfield( const tr_completion* ); +const struct tr_bitfield * tr_cpBlockBitfield( const tr_completion * ); #endif diff --git a/libtransmission/crypto.c b/libtransmission/crypto.c new file mode 100644 index 000000000..b2856132d --- /dev/null +++ b/libtransmission/crypto.c @@ -0,0 +1,257 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#include +#include /* uint8_t */ +#include /* memcpy */ +#include +#include + +#include +#include +#include +#include + +#include +#include "crypto.h" +#include "utils.h" + +/** +*** +**/ + +void +tr_sha1( uint8_t * setme, + const void * content1, + int content1_len, + ... ) +{ + va_list vl; + SHA_CTX sha; + + SHA1_Init( &sha ); + SHA1_Update( &sha, content1, content1_len ); + + va_start( vl, content1_len ); + for( ;; ) { + const void * content = (const void*) va_arg( vl, const void* ); + const int content_len = content ? (int) va_arg( vl, int ) : -1; + if( content==NULL || content_len<1 ) + break; + SHA1_Update( &sha, content, content_len ); + } + SHA1_Final( setme, &sha ); +} + +/** +*** +**/ + +#define KEY_LEN 96 + +#define PRIME_LEN 96 + +static const uint8_t dh_P[PRIME_LEN] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63, +}; + +static const uint8_t dh_G[] = { 2 }; + +struct tr_crypto +{ + DH * dh; + RC4_KEY dec_key; + RC4_KEY enc_key; + uint8_t torrentHash[SHA_DIGEST_LENGTH]; + unsigned int isIncoming : 1; + unsigned int torrentHashIsSet : 1; + unsigned int mySecretIsSet : 1; + uint8_t myPublicKey[KEY_LEN]; + uint8_t mySecret[KEY_LEN]; + +}; + +/** +*** +**/ + +tr_crypto * +tr_cryptoNew( const uint8_t * torrentHash, + int isIncoming ) +{ + int len, offset; + tr_crypto * crypto; + + crypto = tr_new0( tr_crypto, 1 ); + crypto->isIncoming = isIncoming ? 1 : 0; + tr_cryptoSetTorrentHash( crypto, torrentHash ); + + crypto->dh = DH_new( ); + crypto->dh->p = BN_bin2bn( dh_P, sizeof(dh_P), NULL ); + crypto->dh->g = BN_bin2bn( dh_G, sizeof(dh_G), NULL ); + DH_generate_key( crypto->dh ); + + // DH can generate key sizes that are smaller than the size of + // P with exponentially decreasing probability, in which case + // the msb's of myPublicKey need to be zeroed appropriately. + len = DH_size( crypto->dh ); + offset = KEY_LEN - len; + assert( len <= KEY_LEN ); + memset( crypto->myPublicKey, 0, offset ); + BN_bn2bin( crypto->dh->pub_key, crypto->myPublicKey + offset ); + + return crypto; +} + +void +tr_cryptoFree( tr_crypto * crypto ) +{ + assert( crypto != NULL ); + assert( crypto->dh != NULL ); + + DH_free( crypto->dh ); + tr_free( crypto ); +} + +/** +*** +**/ + +const uint8_t* +tr_cryptoComputeSecret( tr_crypto * crypto, + const uint8_t * peerPublicKey ) +{ + int len, offset; + uint8_t secret[KEY_LEN]; + BIGNUM * bn = BN_bin2bn( peerPublicKey, KEY_LEN, NULL ); + assert( DH_size(crypto->dh) == KEY_LEN ); + + len = DH_compute_key( secret, bn, crypto->dh ); + assert( len <= KEY_LEN ); + offset = KEY_LEN - len; + memset( crypto->mySecret, 0, offset ); + memcpy( crypto->mySecret + offset, secret, len ); + crypto->mySecretIsSet = 1; + + BN_free( bn ); + + return crypto->mySecret; +} + +const uint8_t* +tr_cryptoGetMyPublicKey( const tr_crypto * crypto, int * setme_len ) +{ + *setme_len = KEY_LEN; + return crypto->myPublicKey; +} + +/** +*** +**/ + +static void +initRC4( tr_crypto * crypto, RC4_KEY * setme, const char * key ) +{ + SHA_CTX sha; + uint8_t buf[SHA_DIGEST_LENGTH]; + + assert( crypto->torrentHashIsSet ); + assert( crypto->mySecretIsSet ); + + SHA1_Init( &sha ); + SHA1_Update( &sha, key, 4 ); + SHA1_Update( &sha, crypto->mySecret, KEY_LEN ); + SHA1_Update( &sha, crypto->torrentHash, SHA_DIGEST_LENGTH ); + SHA1_Final( buf, &sha ); + RC4_set_key( setme, SHA_DIGEST_LENGTH, buf ); +} + +void +tr_cryptoDecryptInit( tr_crypto * crypto ) +{ + unsigned char discard[1024]; + const char * txt = crypto->isIncoming ? "keyA" : "keyB"; + initRC4( crypto, &crypto->dec_key, txt ); + RC4( &crypto->dec_key, sizeof(discard), discard, discard ); +} + +void +tr_cryptoDecrypt( tr_crypto * crypto, + size_t buf_len, + const void * buf_in, + void * buf_out ) +{ + RC4( &crypto->dec_key, buf_len, + (const unsigned char*)buf_in, + (unsigned char*)buf_out ); +} + +void +tr_cryptoEncryptInit( tr_crypto * crypto ) +{ + unsigned char discard[1024]; + const char * txt = crypto->isIncoming ? "keyB" : "keyA"; + initRC4( crypto, &crypto->enc_key, txt ); + RC4( &crypto->enc_key, sizeof(discard), discard, discard ); +} + +void +tr_cryptoEncrypt( tr_crypto * crypto, + size_t buf_len, + const void * buf_in, + void * buf_out ) +{ + RC4( &crypto->enc_key, buf_len, + (const unsigned char*)buf_in, + (unsigned char*)buf_out ); +} + +/** +*** +**/ + +void +tr_cryptoSetTorrentHash( tr_crypto * crypto, + const uint8_t * hash ) +{ + crypto->torrentHashIsSet = hash ? 1 : 0; + + if( hash != NULL ) + memcpy( crypto->torrentHash, hash, SHA_DIGEST_LENGTH ); + else + memset( crypto->torrentHash, 0, SHA_DIGEST_LENGTH ); +} + +const uint8_t* +tr_cryptoGetTorrentHash( const tr_crypto * crypto ) +{ + assert( crypto != NULL ); + assert( crypto->torrentHashIsSet ); + + return crypto->torrentHash; +} + +int +tr_cryptoHasTorrentHash( const tr_crypto * crypto ) +{ + assert( crypto != NULL ); + + return crypto->torrentHashIsSet ? 1 : 0; +} diff --git a/libtransmission/crypto.h b/libtransmission/crypto.h new file mode 100644 index 000000000..35e807d38 --- /dev/null +++ b/libtransmission/crypto.h @@ -0,0 +1,78 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_ENCRYPTION_H +#define TR_ENCRYPTION_H + +#include + +/** +*** +**/ + +struct evbuffer; +typedef struct tr_crypto tr_crypto; + +/** +*** +**/ + +tr_crypto* tr_cryptoNew ( const uint8_t * torrentHash, + int isIncoming ); + +void tr_cryptoFree( tr_crypto * crypto ); + +/** +*** +**/ + +void tr_cryptoSetTorrentHash( tr_crypto * crypto, + const uint8_t * torrentHash ); + +const uint8_t* tr_cryptoGetTorrentHash( const tr_crypto * crypto ); + +int tr_cryptoHasTorrentHash( const tr_crypto * crypto ); + +/** +*** +**/ + +const uint8_t* tr_cryptoComputeSecret ( tr_crypto * crypto, + const uint8_t * peerPublicKey ); + +const uint8_t* tr_cryptoGetMyPublicKey ( const tr_crypto * crypto, + int * setme_len ); + +void tr_cryptoDecryptInit( tr_crypto * crypto ); + +void tr_cryptoDecrypt ( tr_crypto * crypto, + size_t buflen, + const void * buf_in, + void * buf_out ); + +/** +*** +**/ + +void tr_cryptoEncryptInit ( tr_crypto * crypto ); + +void tr_cryptoEncrypt ( tr_crypto * crypto, + size_t buflen, + const void * buf_in, + void * buf_out ); + +void tr_sha1 ( uint8_t * setme, + const void * content1, + int content1_len, + ... ); + +#endif diff --git a/libtransmission/fastresume.c b/libtransmission/fastresume.c index 9c891b61b..b617ed9f0 100644 --- a/libtransmission/fastresume.c +++ b/libtransmission/fastresume.c @@ -55,7 +55,7 @@ #include "transmission.h" #include "completion.h" #include "fastresume.h" -#include "peer.h" +#include "peer-mgr.h" #include "platform.h" #include "utils.h" @@ -74,7 +74,7 @@ enum FR_ID_UPLOADED = 3, /* IPs and ports of connectable peers */ - FR_ID_PEERS = 4, + FR_ID_PEERS_OLD = 4, /* progress data: * - 4 bytes * number of files: mtimes of files @@ -88,9 +88,9 @@ enum /* transfer speeds * uint32_t: dl speed rate to use when the mode is single - * uint32_t: dl's tr_speedlimit_t + * uint32_t: dl's tr_speedlimit * uint32_t: ul speed rate to use when the mode is single - * uint32_t: ul's tr_speedlimit_t + * uint32_t: ul's tr_speedlimit */ FR_ID_SPEED = 8, @@ -100,7 +100,10 @@ enum FR_ID_RUN = 9, /* number of corrupt bytes downloaded */ - FR_ID_CORRUPT = 10 + FR_ID_CORRUPT = 10, + + /* IPs and ports of connectable peers */ + FR_ID_PEERS = 11 }; @@ -114,7 +117,7 @@ enum #define FR_SPEED_LEN (2 * (sizeof(uint16_t) + sizeof(uint8_t) ) ) static void -fastResumeFileName( char * buf, size_t buflen, const tr_torrent_t * tor, int tag ) +fastResumeFileName( char * buf, size_t buflen, const tr_torrent * tor, int tag ) { const char * cacheDir = tr_getCacheDirectory (); const char * hash = tor->info.hashString; @@ -132,7 +135,7 @@ fastResumeFileName( char * buf, size_t buflen, const tr_torrent_t * tor, int tag } static tr_time_t* -getMTimes( const tr_torrent_t * tor, int * setme_n ) +getMTimes( const tr_torrent * tor, int * setme_n ) { int i; const int n = tor->info.fileCount; @@ -171,7 +174,7 @@ fastResumeWriteData( uint8_t id, } void -tr_fastResumeSave( const tr_torrent_t * tor ) +tr_fastResumeSave( const tr_torrent * tor ) { char path[MAX_PATH_LENGTH]; FILE * file; @@ -194,7 +197,7 @@ tr_fastResumeSave( const tr_torrent_t * tor ) tr_time_t * mtimes; uint8_t * buf = malloc( FR_PROGRESS_LEN( tor ) ); uint8_t * walk = buf; - const tr_bitfield_t * bitfield; + const tr_bitfield * bitfield; /* mtimes */ mtimes = getMTimes( tor, &n ); @@ -283,23 +286,25 @@ tr_fastResumeSave( const tr_torrent_t * tor ) } /* Write download and upload totals */ + total = tor->downloadedCur + tor->downloadedPrev; fastResumeWriteData( FR_ID_DOWNLOADED, &total, 8, 1, file ); + total = tor->uploadedCur + tor->uploadedPrev; fastResumeWriteData( FR_ID_UPLOADED, &total, 8, 1, file ); + total = tor->corruptCur + tor->corruptPrev; fastResumeWriteData( FR_ID_CORRUPT, &total, 8, 1, file ); if( !( TR_FLAG_PRIVATE & tor->info.flags ) ) { - /* Write IPs and ports of connectable peers, if any */ - int size; - uint8_t * buf = NULL; - if( ( size = tr_peerGetConnectable( tor, &buf ) ) > 0 ) - { - fastResumeWriteData( FR_ID_PEERS, buf, size, 1, file ); - free( buf ); - } + tr_pex * pex; + const int count = tr_peerMgrGetPeers( tor->handle->peerMgr, + tor->info.hash, + &pex ); + if( count > 0 ) + fastResumeWriteData( FR_ID_PEERS, pex, sizeof(tr_pex), count, file ); + tr_free( pex ); } fclose( file ); @@ -308,7 +313,7 @@ tr_fastResumeSave( const tr_torrent_t * tor ) } static int -loadSpeeds( tr_torrent_t * tor, FILE * file ) +loadSpeeds( tr_torrent * tor, FILE * file ) { const size_t len = FR_SPEED_LEN; char * buf = tr_new0( char, len ); @@ -325,11 +330,11 @@ loadSpeeds( tr_torrent_t * tor, FILE * file ) memcpy( &i16, walk, 2 ); walk += 2; tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 ); memcpy( &i8, walk, 1 ); walk += 1; - tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit_t)i8 ); + tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 ); memcpy( &i16, walk, 2 ); walk += 2; tr_torrentSetSpeedLimit( tor, TR_UP, i16 ); memcpy( &i8, walk, 1 ); walk += 1; - tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit_t)i8 ); + tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 ); tr_free( buf ); return TR_OK; @@ -337,8 +342,8 @@ loadSpeeds( tr_torrent_t * tor, FILE * file ) static int -loadPriorities( tr_torrent_t * tor, - FILE * file ) +loadPriorities( tr_torrent * tor, + FILE * file ) { const size_t n = tor->info.fileCount; const size_t len = 2 * n; @@ -387,9 +392,9 @@ loadPriorities( tr_torrent_t * tor, } static int -fastResumeLoadProgress( const tr_torrent_t * tor, - tr_bitfield_t * uncheckedPieces, - FILE * file ) +fastResumeLoadProgress( const tr_torrent * tor, + tr_bitfield * uncheckedPieces, + FILE * file ) { int i; const size_t len = FR_PROGRESS_LEN( tor ); @@ -409,7 +414,7 @@ fastResumeLoadProgress( const tr_torrent_t * tor, const tr_time_t * oldMTimes = (const tr_time_t *) walk; for( i=0; iinfo.files[i]; + const tr_file * file = &tor->info.files[i]; tr_dbg( "File '%s' mtimes differ-- flagging pieces [%d..%d] for recheck", file->name, file->firstPiece, file->lastPiece); tr_bitfieldAddRange( uncheckedPieces, @@ -422,7 +427,7 @@ fastResumeLoadProgress( const tr_torrent_t * tor, /* get the completion bitfield */ if (1) { - tr_bitfield_t bitfield; + tr_bitfield bitfield; memset( &bitfield, 0, sizeof bitfield ); bitfield.len = FR_BLOCK_BITFIELD_LEN( tor ); bitfield.bits = walk; @@ -441,9 +446,9 @@ fastResumeLoadProgress( const tr_torrent_t * tor, } static uint64_t -fastResumeLoadOld( tr_torrent_t * tor, - tr_bitfield_t * uncheckedPieces, - FILE * file ) +fastResumeLoadOld( tr_torrent * tor, + tr_bitfield * uncheckedPieces, + FILE * file ) { uint64_t ret = 0; @@ -475,8 +480,8 @@ fastResumeLoadOld( tr_torrent_t * tor, } static uint64_t -fastResumeLoadImpl ( tr_torrent_t * tor, - tr_bitfield_t * uncheckedPieces ) +fastResumeLoadImpl ( tr_torrent * tor, + tr_bitfield * uncheckedPieces ) { char path[MAX_PATH_LENGTH]; FILE * file; @@ -636,10 +641,9 @@ fastResumeLoadImpl ( tr_torrent_t * tor, } break; - case FR_ID_PEERS: + case FR_ID_PEERS_OLD: if( !( TR_FLAG_PRIVATE & tor->info.flags ) ) { - int used; uint8_t * buf = malloc( len ); if( 1 != fread( buf, len, 1, file ) ) { @@ -647,13 +651,38 @@ fastResumeLoadImpl ( tr_torrent_t * tor, fclose( file ); return ret; } - used = tr_torrentAddCompact( tor, TR_PEER_FROM_CACHE, - buf, len / 6 ); - tr_dbg( "found %i peers in resume file, used %i", - len / 6, used ); + + tr_peerMgrAddPeers( tor->handle->peerMgr, + tor->info.hash, + TR_PEER_FROM_CACHE, + buf, len / 6 ); + + tr_dbg( "found %i peers in resume file", len/6 ); free( buf ); ret |= TR_FR_PEERS; } + + case FR_ID_PEERS: + if( !( TR_FLAG_PRIVATE & tor->info.flags ) ) + { + const int count = len / sizeof(tr_pex); + tr_pex * pex = tr_new0( tr_pex, count ); + if( 1 != fread( pex, sizeof(tr_pex), count, file ) ) + { + free( pex ); + fclose( file ); + return ret; + } + + tr_peerMgrAddPex( tor->handle->peerMgr, + tor->info.hash, + TR_PEER_FROM_CACHE, + pex, count ); + + tr_dbg( "found %i peers in resume file", len/6 ); + free( pex ); + ret |= TR_FR_PEERS; + } continue; default: @@ -670,8 +699,8 @@ fastResumeLoadImpl ( tr_torrent_t * tor, } uint64_t -tr_fastResumeLoad( tr_torrent_t * tor, - tr_bitfield_t * uncheckedPieces ) +tr_fastResumeLoad( tr_torrent * tor, + tr_bitfield * uncheckedPieces ) { const uint64_t ret = fastResumeLoadImpl( tor, uncheckedPieces ); diff --git a/libtransmission/fastresume.h b/libtransmission/fastresume.h index c791d7ac7..176506348 100644 --- a/libtransmission/fastresume.h +++ b/libtransmission/fastresume.h @@ -25,7 +25,7 @@ #ifndef TR_FAST_RESUME_H #define TR_FAST_RESUME_H -void tr_fastResumeSave( const tr_torrent_t * tor ); +void tr_fastResumeSave( const tr_torrent * tor ); enum { @@ -42,7 +42,7 @@ enum /** * Returns a bitwise-or'ed set of the data loaded from fastresume */ -uint64_t tr_fastResumeLoad( tr_torrent_t * tor, - struct tr_bitfield_s * uncheckedPieces ); +uint64_t tr_fastResumeLoad( tr_torrent * tor, + struct tr_bitfield * uncheckedPieces ); #endif diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index 24bec7489..755ec9966 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -63,8 +63,8 @@ tr_openFile_t; typedef struct tr_fd_s { - tr_lock_t * lock; - tr_cond_t * cond; + tr_lock * lock; + tr_cond * cond; int reserved; @@ -390,17 +390,20 @@ int tr_fdSocketAccept( int b, struct in_addr * addr, tr_port_t * port ) **********************************************************************/ void tr_fdSocketClose( int s ) { - tr_lockLock( gFd->lock ); + if( s >= 0 ) + { + tr_lockLock( gFd->lock ); #ifdef BEOS_NETSERVER - closesocket( s ); + closesocket( s ); #else - close( s ); + close( s ); #endif - if( SocketGetPriority( s ) ) - gFd->reserved--; - else - gFd->normal--; - tr_lockUnlock( gFd->lock ); + if( SocketGetPriority( s ) ) + gFd->reserved--; + else + gFd->normal--; + tr_lockUnlock( gFd->lock ); + } } /*********************************************************************** diff --git a/libtransmission/handshake.c b/libtransmission/handshake.c new file mode 100644 index 000000000..1fa9d8e54 --- /dev/null +++ b/libtransmission/handshake.c @@ -0,0 +1,897 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#include +#include +#include +#include /* UCHAR_MAX */ +#include +#include +#include + +#include /* event.h needs this */ +#include + +#include "transmission.h" +#include "bencode.h" +#include "crypto.h" +#include "handshake.h" +#include "peer-io.h" +#include "utils.h" + +/* enable LibTransmission extension protocol */ +#define ENABLE_LTEP + +/* enable Azureus messaging protocol */ +//#define ENABLE_AZMP + +/*** +**** +***/ + +#define HANDSHAKE_NAME "\023BitTorrent protocol" + +enum +{ + /* BitTorrent Handshake Constants */ + HANDSHAKE_NAME_LEN = 20, + HANDSHAKE_FLAGS_LEN = 8, + HANDSHAKE_SIZE = 68, + PEER_ID_LEN = 20, + + /* Extension Negotiation Constants */ + HANDSHAKE_EXTPREF_LTEP_FORCE = 0x0, + HANDSHAKE_EXTPREF_LTEP_PREFER = 0x1, + HANDSHAKE_EXTPREF_AZMP_PREFER = 0x2, + HANDSHAKE_EXTPREF_AZMP_FORCE = 0x3, + + /* Encryption Constants */ + PadA_MAXLEN = 512, + PadB_MAXLEN = 512, + PadC_MAXLEN = 512, + PadD_MAXLEN = 512, + VC_LENGTH = 8, + KEY_LEN = 96 +}; + + +#ifdef ENABLE_LTEP +#define HANDSHAKE_HAS_EXTMSGS( bits ) ( ( (bits)[5] & 0x10 ) ? 1 : 0 ) +#define HANDSHAKE_SET_EXTMSGS( bits ) ( ( (bits)[5] |= 0x10 ) ? 1 : 0 ) +#else +#define HANDSHAKE_HAS_EXTMSGS( bits ) ( 0 ) +#define HANDSHAKE_SET_EXTMSGS( bits ) ( (void)0 ) +#endif + +#ifdef ENABLE_AZMP +#define HANDSHAKE_HAS_AZPROTO( bits ) ( ( (bits)[0] & 0x80 ) ? 1 : 0 ) +#define HANDSHAKE_SET_AZPROTO( bits ) ( ( (bits)[0] |= 0x80 ) ? 1 : 0 ) +#else +#define HANDSHAKE_HAS_AZPROTO( bits ) ( 0 ) +#define HANDSHAKE_SET_AZPROTO( bits ) ( (void)0 ) +#endif + +/* http://www.azureuswiki.com/index.php/Extension_negotiation_protocol + these macros are to be used if both extended messaging and the + azureus protocol is supported, they indicate which protocol is preferred */ +#define HANDSHAKE_GET_EXTPREF( reserved ) ( (reserved)[5] & 0x03 ) +#define HANDSHAKE_SET_EXTPREF( reserved, val ) ( (reserved)[5] |= 0x03 & (val) ) + +extern const char* getPeerId( void ) ; + +struct tr_handshake +{ + unsigned int peerSupportsEncryption : 1; + unsigned int havePeerID : 1; + unsigned int haveSentBitTorrentHandshake : 1; + unsigned int shunUnencryptedPeers : 1; + tr_peerIo * io; + tr_crypto * crypto; + struct tr_handle * handle; + uint8_t myPublicKey[KEY_LEN]; + uint8_t mySecret[KEY_LEN]; + uint8_t state; + uint16_t pad_c_len; + uint16_t pad_d_len; + uint16_t ia_len; + uint32_t crypto_select; + uint8_t myReq1[SHA_DIGEST_LENGTH]; + uint8_t peer_id[PEER_ID_LEN]; + handshakeDoneCB doneCB; + void * doneUserData; +}; + +/** +*** +**/ + +enum +{ + /* incoming */ + AWAITING_HANDSHAKE, + AWAITING_YA, + AWAITING_PAD_A, + AWAITING_CRYPTO_PROVIDE, + AWAITING_PAD_C, + AWAITING_IA, + + /* outgoing */ + AWAITING_YB, + AWAITING_VC, + AWAITING_CRYPTO_SELECT, + AWAITING_PAD_D, +}; + +/** +*** +**/ + +static void +myDebug( const char * file, int line, const tr_handshake * handshake, const char * fmt, ... ) +{ + va_list args; + const char * addr = tr_peerIoGetAddrStr( handshake->io ); + struct evbuffer * buf = evbuffer_new( ); + evbuffer_add_printf( buf, "[%s:%d] %s (%p) ", file, line, addr, handshake ); + va_start( args, fmt ); + evbuffer_add_vprintf( buf, fmt, args ); + va_end( args ); + + fprintf( stderr, "%s\n", EVBUFFER_DATA(buf) ); + + evbuffer_free( buf ); +} + +#define dbgmsg(handshake, fmt...) myDebug(__FILE__, __LINE__, handshake, ##fmt ) + +static const char* getStateName( short state ) +{ + const char * str = "f00!"; + switch( state ) { + case AWAITING_HANDSHAKE: str = "awaiting handshake"; break; + case AWAITING_YA: str = "awaiting ya"; break; + //case SENDING_YB: str = "sending yb"; break; + case AWAITING_PAD_A: str = "awaiting pad a"; break; + case AWAITING_CRYPTO_PROVIDE: str = "awaiting crypto_provide"; break; + case AWAITING_PAD_C: str = "awaiting pad c"; break; + case AWAITING_IA: str = "awaiting ia"; break; + //case SENDING_YA: str = "sending ya"; break; + case AWAITING_YB: str = "awaiting yb"; break; + //case SENDING_CRYPTO_PROVIDE: str = "sending crypto provide"; break; + case AWAITING_VC: str = "awaiting vc"; break; + case AWAITING_CRYPTO_SELECT: str = "awaiting crypto select"; break; + case AWAITING_PAD_D: str = "awaiting pad d"; break; + //case SENDING_NONE: str = "sending plaintext handshake"; break; + } + return str; +} + +static void +setState( tr_handshake * handshake, short state ) +{ + dbgmsg( handshake, "setting to state [%s]", getStateName(state) ); + handshake->state = state; +} + +static void +setReadState( tr_handshake * handshake, int state ) +{ + setState( handshake, state ); + //tr_peerIoSetIOMode( handshake->io, EV_READ, EV_WRITE ); +} + +static uint8_t * +buildHandshakeMessage( tr_handshake * handshake, + int extensionPreference, + int * setme_len ) +{ + uint8_t * buf = tr_new0( uint8_t, HANDSHAKE_SIZE ); + uint8_t * walk = buf; + const uint8_t * torrentHash = tr_cryptoGetTorrentHash( handshake->crypto ); + + memcpy( walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN ); + walk += HANDSHAKE_NAME_LEN; + memset( walk, 0, HANDSHAKE_FLAGS_LEN ); + switch( extensionPreference ) + { + case HANDSHAKE_EXTPREF_LTEP_FORCE: + HANDSHAKE_SET_EXTMSGS( walk ); + HANDSHAKE_SET_EXTPREF( walk, extensionPreference ); + break; + + case HANDSHAKE_EXTPREF_LTEP_PREFER: + HANDSHAKE_SET_EXTMSGS( walk ); + HANDSHAKE_SET_AZPROTO( walk ); + HANDSHAKE_SET_EXTPREF( walk, extensionPreference ); + break; + + case HANDSHAKE_EXTPREF_AZMP_PREFER: + HANDSHAKE_SET_EXTMSGS( walk ); + HANDSHAKE_SET_AZPROTO( walk ); + HANDSHAKE_SET_EXTPREF( walk, extensionPreference ); + break; + + case HANDSHAKE_EXTPREF_AZMP_FORCE: + HANDSHAKE_SET_AZPROTO( walk ); + HANDSHAKE_SET_EXTPREF( walk, extensionPreference ); + break; + } + + walk += HANDSHAKE_FLAGS_LEN; + memcpy( walk, torrentHash, SHA_DIGEST_LENGTH ); + walk += SHA_DIGEST_LENGTH; + memcpy( walk, getPeerId(), TR_ID_LEN ); + walk += TR_ID_LEN; + + assert( walk-buf == HANDSHAKE_SIZE ); + *setme_len = walk - buf; + return buf; +} + +/*** +**** +**** OUTGOING CONNECTIONS +**** +***/ + +/* 1 A->B: Diffie Hellman Ya, PadA */ +static void +sendYa( tr_handshake * handshake ) +{ + int i; + int len; + const uint8_t * public_key; + struct evbuffer * outbuf = evbuffer_new( ); + uint8_t pad_a[PadA_MAXLEN]; + + /* add our public key (Ya) */ + public_key = tr_cryptoGetMyPublicKey( handshake->crypto, &len ); + assert( len == KEY_LEN ); + assert( public_key != NULL ); + evbuffer_add( outbuf, public_key, len ); + + /* add some bullshit padding */ + len = tr_rand( PadA_MAXLEN ); + for( i=0; iio, outbuf ); + + /* cleanup */ + evbuffer_free( outbuf ); +} + +static int +readYb( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + int isEncrypted; + const uint8_t * secret; + uint8_t yb[KEY_LEN]; + struct evbuffer * outbuf; + size_t needlen = HANDSHAKE_NAME_LEN; + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + isEncrypted = memcmp( EVBUFFER_DATA(inbuf), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN ); + if( isEncrypted ) { + needlen = KEY_LEN; + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + } + + dbgmsg( handshake, "got a %s handshake", (isEncrypted ? "encrypted" : "plaintext") ); + + handshake->peerSupportsEncryption = isEncrypted; + tr_peerIoSetEncryption( handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4 + : PEER_ENCRYPTION_NONE ); + if( !isEncrypted ) { + setState( handshake, AWAITING_HANDSHAKE ); + return READ_AGAIN; + } + + /* compute the secret */ + evbuffer_remove( inbuf, yb, KEY_LEN ); + secret = tr_cryptoComputeSecret( handshake->crypto, yb ); + memcpy( handshake->mySecret, secret, KEY_LEN ); + + /* now send these: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), + * ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) */ + outbuf = evbuffer_new( ); + + /* HASH('req1', S) */ + { + uint8_t req1[SHA_DIGEST_LENGTH]; + tr_sha1( req1, "req1", 4, secret, KEY_LEN, NULL ); + evbuffer_add( outbuf, req1, SHA_DIGEST_LENGTH ); + } + + /* HASH('req2', SKEY) xor HASH('req3', S) */ + { + int i; + uint8_t req2[SHA_DIGEST_LENGTH]; + uint8_t req3[SHA_DIGEST_LENGTH]; + uint8_t buf[SHA_DIGEST_LENGTH]; + tr_sha1( req2, "req2", 4, tr_cryptoGetTorrentHash(handshake->crypto), SHA_DIGEST_LENGTH, NULL ); + tr_sha1( req3, "req3", 4, secret, KEY_LEN, NULL ); + for( i=0; icrypto ); + + /* vc */ + tr_cryptoEncrypt( handshake->crypto, VC_LENGTH, vc, vc ); + evbuffer_add( outbuf, vc, VC_LENGTH ); + + /* crypto_provide */ + crypto_provide = 0; + crypto_provide |= (1<<0); /* encryption */ + if( !handshake->shunUnencryptedPeers ) /* plaintext */ + crypto_provide |= (1<<1); + assert( 1<=crypto_provide && crypto_provide<=3 ); + + crypto_provide = htonl( crypto_provide ); + tr_cryptoEncrypt( handshake->crypto, sizeof(crypto_provide), &crypto_provide, &crypto_provide ); + evbuffer_add( outbuf, &crypto_provide, sizeof(crypto_provide) ); + + /* len(padc) */ + i = len = tr_rand( 512 ); + i = htons( i ); + tr_cryptoEncrypt( handshake->crypto, sizeof(i), &i, &i ); + evbuffer_add( outbuf, &i, sizeof(i) ); + + /* padc */ + for( i=0; icrypto, len, pad, pad ); + evbuffer_add( outbuf, pad, len ); + } + + /* ENCRYPT len(IA)), ENCRYPT(IA) */ + { + uint16_t i; + int msgSize; + uint8_t * msg = buildHandshakeMessage( handshake, HANDSHAKE_EXTPREF_LTEP_PREFER, &msgSize ); + + i = htons( msgSize ); + tr_cryptoEncrypt( handshake->crypto, sizeof(uint16_t), &i, &i ); + evbuffer_add( outbuf, &i, sizeof(uint16_t) ); + + tr_cryptoEncrypt( handshake->crypto, msgSize, msg, msg ); + evbuffer_add( outbuf, msg, HANDSHAKE_SIZE ); + handshake->haveSentBitTorrentHandshake = 1; + + tr_free( msg ); + } + + /* send it */ + tr_cryptoDecryptInit( handshake->crypto ); + setReadState( handshake, AWAITING_VC ); + tr_peerIoWriteBuf( handshake->io, outbuf ); + + /* cleanup */ + evbuffer_free( outbuf ); + return READ_DONE; +} + +static int +readVC( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + const uint8_t key[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + const int key_len = VC_LENGTH; + uint8_t tmp[VC_LENGTH]; + + /* note: this works w/o having to `unwind' the buffer if + * we read too much, but it is pretty brute-force. + * it would be nice to make this cleaner. */ + for( ;; ) + { + if( EVBUFFER_LENGTH(inbuf) < VC_LENGTH ) { + dbgmsg( handshake, "not enough bytes... returning read_more" ); + return READ_MORE; + } + + memcpy( tmp, EVBUFFER_DATA(inbuf), key_len ); + tr_cryptoDecryptInit( handshake->crypto ); + tr_cryptoDecrypt( handshake->crypto, key_len, tmp, tmp ); + if( !memcmp( tmp, key, key_len ) ) + break; + + evbuffer_drain( inbuf, 1 ); + } + + dbgmsg( handshake, "got it!" ); + evbuffer_drain( inbuf, key_len ); + setState( handshake, AWAITING_CRYPTO_SELECT ); + return READ_AGAIN; +} + +static int +readCryptoSelect( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + uint32_t crypto_select; + uint16_t pad_d_len; + const size_t needlen = sizeof(uint32_t) + sizeof(uint16_t); + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + tr_peerIoReadUint32( handshake->io, inbuf, &crypto_select ); + assert( crypto_select==1 || crypto_select==2 ); + handshake->crypto_select = crypto_select; + dbgmsg( handshake, "crypto select is %d", (int)crypto_select ); + + tr_peerIoReadUint16( handshake->io, inbuf, &pad_d_len ); + dbgmsg( handshake, "pad_d_len is %d", (int)pad_d_len ); + assert( pad_d_len <= 512 ); + handshake->pad_d_len = pad_d_len; + + setState( handshake, AWAITING_PAD_D ); + return READ_AGAIN; +} + +static int +readPadD( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + const size_t needlen = handshake->pad_d_len; + uint8_t * tmp; + + dbgmsg( handshake, "pad d: need %d, got %d", (int)needlen, (int)EVBUFFER_LENGTH(inbuf) ); + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + tmp = tr_new( uint8_t, needlen ); + tr_peerIoReadBytes( handshake->io, inbuf, tmp, needlen ); + tr_free( tmp ); + + tr_peerIoSetEncryption( handshake->io, + handshake->crypto_select ); + + setState( handshake, AWAITING_HANDSHAKE ); + return READ_AGAIN; +} + +/*** +**** +**** INCOMING CONNECTIONS +**** +***/ + +static void +tr_handshakeDone( tr_handshake * handshake, int isConnected ); + +static int +readHandshake( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + int i; + uint8_t ltep = 0; + uint8_t azmp = 0; + uint8_t pstrlen; + uint8_t * pstr; + uint8_t reserved[HANDSHAKE_FLAGS_LEN]; + uint8_t hash[SHA_DIGEST_LENGTH]; + + dbgmsg( handshake, "payload: need %d, got %d\n", (int)HANDSHAKE_SIZE, (int)EVBUFFER_LENGTH(inbuf) ); + + if( EVBUFFER_LENGTH(inbuf) < HANDSHAKE_SIZE ) + return READ_MORE; + + pstrlen = EVBUFFER_DATA(inbuf)[0]; /* peek, don't read. We may be + handing inbuf to AWAITING_YA */ + + if( pstrlen == 19 ) /* unencrypted */ + { + tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE ); + } + else /* encrypted or corrupt */ + { + tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_RC4 ); + + if( tr_peerIoIsIncoming( handshake->io ) ) + { + dbgmsg( handshake, "I think peer is sending us an encrypted handshake..." ); + setState( handshake, AWAITING_YA ); + return READ_AGAIN; + } + tr_cryptoDecrypt( handshake->crypto, 1, &pstrlen, &pstrlen ); + + if( pstrlen != 19 ) + { + dbgmsg( handshake, "I think peer has sent us a corrupt handshake..." ); + tr_handshakeDone( handshake, FALSE ); + return READ_DONE; + } + } + + evbuffer_drain( inbuf, 1 ); + + /* pstr (BitTorrent) */ + pstr = tr_new( uint8_t, pstrlen+1 ); + tr_peerIoReadBytes( handshake->io, inbuf, pstr, pstrlen ); + pstr[pstrlen] = '\0'; + if( strcmp( (char*)pstr, "BitTorrent protocol" ) ) { + tr_free( pstr ); + tr_handshakeDone( handshake, FALSE ); + return READ_DONE; + } + tr_free( pstr ); + + /* reserved bytes */ + tr_peerIoReadBytes( handshake->io, inbuf, reserved, sizeof(reserved) ); + + /* torrent hash */ + tr_peerIoReadBytes( handshake->io, inbuf, hash, sizeof(hash) ); + if( tr_peerIoIsIncoming( handshake->io ) ) + { + if( !tr_torrentExists( handshake->handle, hash ) ) + { + dbgmsg( handshake, "peer is trying to connect to us for a torrent we don't have." ); + tr_handshakeDone( handshake, FALSE ); + return READ_DONE; + } + else + { + assert( !tr_peerIoHasTorrentHash( handshake->io ) ); + tr_peerIoSetTorrentHash( handshake->io, hash ); + } + } + else /* outgoing */ + { + assert( tr_torrentExists( handshake->handle, hash ) ); + assert( tr_peerIoHasTorrentHash( handshake->io ) ); + if( memcmp( hash, tr_peerIoGetTorrentHash(handshake->io), SHA_DIGEST_LENGTH ) ) + { + dbgmsg( handshake, "peer returned the wrong hash. wtf?" ); + tr_handshakeDone( handshake, FALSE ); + return READ_DONE; + } + } + + /* peer id */ + tr_peerIoReadBytes( handshake->io, inbuf, handshake->peer_id, sizeof(handshake->peer_id) ); + tr_peerIoSetPeersId( handshake->io, handshake->peer_id ); + handshake->havePeerID = TRUE; + dbgmsg( handshake, "peer-id is [%*.*s]", PEER_ID_LEN, PEER_ID_LEN, handshake->peer_id ); + if( !memcmp( handshake->peer_id, getPeerId(), PEER_ID_LEN ) ) { + dbgmsg( handshake, "streuth! we've connected to ourselves." ); + tr_handshakeDone( handshake, FALSE ); + return READ_DONE; + } + + /** + *** Extension negotiation + **/ + + ltep = HANDSHAKE_HAS_EXTMSGS( reserved ); + azmp = HANDSHAKE_HAS_AZPROTO( reserved ); + if( ltep && azmp ) { + switch( HANDSHAKE_GET_EXTPREF( reserved ) ) { + case HANDSHAKE_EXTPREF_LTEP_FORCE: + case HANDSHAKE_EXTPREF_LTEP_PREFER: + azmp = 0; + break; + case HANDSHAKE_EXTPREF_AZMP_FORCE: + case HANDSHAKE_EXTPREF_AZMP_PREFER: + ltep = 0; + break; + } + } + assert( !ltep || !azmp ); + if( ltep ) { i = LT_EXTENSIONS_LTEP; dbgmsg(handshake,"using ltep" ); } + else if( azmp ) { i = LT_EXTENSIONS_AZMP; dbgmsg(handshake,"using azmp" ); } + else { i = LT_EXTENSIONS_NONE; dbgmsg(handshake,"using no extensions" ); } + tr_peerIoSetExtension( handshake->io, i ); + + + /** + *** If this is an incoming message, then we need to send a response handshake + **/ + + if( !handshake->haveSentBitTorrentHandshake ) + { + const int ext = ltep ? HANDSHAKE_EXTPREF_LTEP_FORCE + : HANDSHAKE_EXTPREF_AZMP_FORCE; + int msgSize; + uint8_t * msg = buildHandshakeMessage( handshake, ext, &msgSize ); + tr_peerIoWrite( handshake->io, msg, msgSize ); + tr_free( msg ); + handshake->haveSentBitTorrentHandshake = 1; + } + + /* we've completed the BT handshake... pass the work on to peer-msgs */ + tr_handshakeDone( handshake, TRUE ); + return READ_DONE; +} + +static int +readYa( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + uint8_t ya[KEY_LEN]; + uint8_t *walk, outbuf[KEY_LEN + PadB_MAXLEN]; + const uint8_t *myKey, *secret; + int len; + + if( EVBUFFER_LENGTH( inbuf ) < KEY_LEN ) + return READ_MORE; + + /* read the incoming peer's public key */ + evbuffer_remove( inbuf, ya, KEY_LEN ); + secret = tr_cryptoComputeSecret( handshake->crypto, ya ); + memcpy( handshake->mySecret, secret, KEY_LEN ); + tr_sha1( handshake->myReq1, "req1", 4, secret, KEY_LEN, NULL ); + + /* send our public key to the peer */ + walk = outbuf; + myKey = tr_cryptoGetMyPublicKey( handshake->crypto, &len ); + memcpy( walk, myKey, len ); + len = tr_rand( PadB_MAXLEN ); + while( len-- ) + *walk++ = tr_rand( UCHAR_MAX ); + + setReadState( handshake, AWAITING_PAD_A ); + tr_peerIoWrite( handshake->io, outbuf, walk-outbuf ); + + return READ_DONE; +} + +static int +readPadA( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + uint8_t * pch; + + /** + *** Resynchronizing on HASH('req1',S) + **/ + + pch = memchr( EVBUFFER_DATA(inbuf), + handshake->myReq1[0], + EVBUFFER_LENGTH(inbuf) ); + if( pch == NULL ) { + evbuffer_drain( inbuf, EVBUFFER_LENGTH(inbuf) ); + return READ_MORE; + } + evbuffer_drain( inbuf, pch-EVBUFFER_DATA(inbuf) ); + if( EVBUFFER_LENGTH(inbuf) < SHA_DIGEST_LENGTH ) + return READ_MORE; + if( memcmp( EVBUFFER_DATA(inbuf), handshake->myReq1, SHA_DIGEST_LENGTH ) ) { + evbuffer_drain( inbuf, 1 ); + return READ_AGAIN; + } + + setState( handshake, AWAITING_CRYPTO_PROVIDE ); + return READ_AGAIN; +} + +static int +readCryptoProvide( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + /* HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC)) */ + + int i; + uint8_t vc_in[VC_LENGTH]; + uint8_t req2[SHA_DIGEST_LENGTH]; + uint8_t req3[SHA_DIGEST_LENGTH]; + uint8_t obfuscatedTorrentHash[SHA_DIGEST_LENGTH]; + uint16_t padc_len = 0; + uint32_t crypto_provide = 0; + const size_t needlen = SHA_DIGEST_LENGTH + VC_LENGTH + sizeof(crypto_provide) + sizeof(padc_len); + tr_torrent * tor = NULL; + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + /* TODO: confirm they sent HASH('req1',S) here? */ + evbuffer_drain( inbuf, SHA_DIGEST_LENGTH ); + + /* This next piece is HASH('req2', SKEY) xor HASH('req3', S) ... + * we can get the first half of that (the obufscatedTorrentHash) + * by building the latter and xor'ing it with what the peer sent us */ + dbgmsg( handshake, "reading obfuscated torrent hash...\n" ); + evbuffer_remove( inbuf, req2, SHA_DIGEST_LENGTH ); + tr_sha1( req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL ); + for( i=0; ihandle, obfuscatedTorrentHash ); + assert( tor != NULL ); + dbgmsg( handshake, "found the torrent; it's [%s]\n", tor->info.name ); + tr_peerIoSetTorrentHash( handshake->io, tor->info.hash ); + + /* next part: ENCRYPT(VC, crypto_provide, len(PadC), */ + + tr_cryptoDecryptInit( handshake->crypto ); + + tr_peerIoReadBytes( handshake->io, inbuf, vc_in, VC_LENGTH ); + + tr_peerIoReadUint32( handshake->io, inbuf, &crypto_provide ); + dbgmsg( handshake, "crypto_provide is %d\n", (int)crypto_provide ); + + tr_peerIoReadUint16( handshake->io, inbuf, &padc_len ); + dbgmsg( handshake, "padc is %d\n", (int)padc_len ); + handshake->pad_c_len = padc_len; + setState( handshake, AWAITING_PAD_C ); + return READ_AGAIN; +} + +static int +readPadC( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + uint16_t ia_len; + const size_t needlen = handshake->pad_c_len + sizeof(uint16_t); + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + evbuffer_drain( inbuf, needlen ); + + tr_peerIoReadUint16( handshake->io, inbuf, &ia_len ); + dbgmsg( handshake, "ia_len is %d", (int)ia_len ); + handshake->ia_len = ia_len; + setState( handshake, AWAITING_IA ); + return READ_AGAIN; +} + +static int +readIA( tr_handshake * handshake, struct evbuffer * inbuf ) +{ + const size_t needlen = handshake->ia_len; + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + return readHandshake( handshake, inbuf ); +} + +/*** +**** +**** +**** +***/ + +static ReadState +canRead( struct bufferevent * evin, void * arg ) +{ + tr_handshake * handshake = (tr_handshake *) arg; + struct evbuffer * inbuf = EVBUFFER_INPUT ( evin ); + ReadState ret; + dbgmsg( handshake, "handling canRead; state is [%s]\n", getStateName(handshake->state) ); + + switch( handshake->state ) + { + case AWAITING_HANDSHAKE: ret = readHandshake ( handshake, inbuf ); break; + case AWAITING_YA: ret = readYa ( handshake, inbuf ); break; + case AWAITING_PAD_A: ret = readPadA ( handshake, inbuf ); break; + case AWAITING_CRYPTO_PROVIDE: ret = readCryptoProvide( handshake, inbuf ); break; + case AWAITING_PAD_C: ret = readPadC ( handshake, inbuf ); break; + case AWAITING_IA: ret = readIA ( handshake, inbuf ); break; + + case AWAITING_YB: ret = readYb ( handshake, inbuf ); break; + case AWAITING_VC: ret = readVC ( handshake, inbuf ); break; + case AWAITING_CRYPTO_SELECT: ret = readCryptoSelect ( handshake, inbuf ); break; + case AWAITING_PAD_D: ret = readPadD ( handshake, inbuf ); break; + + default: assert( 0 ); + } + + return ret; +} + +static void +fireDoneFunc( tr_handshake * handshake, int isConnected ) +{ + const uint8_t * peer_id = isConnected && handshake->havePeerID + ? handshake->peer_id + : NULL; + (*handshake->doneCB)( handshake, + handshake->io, + isConnected, + peer_id, + handshake->peerSupportsEncryption, + handshake->doneUserData ); +} + +void +tr_handshakeDone( tr_handshake * handshake, int isOK ) +{ + dbgmsg( handshake, "handshakeDone: %s", isOK ? "connected" : "aborting" ); + tr_peerIoSetIOFuncs( handshake->io, NULL, NULL, NULL, NULL ); + fireDoneFunc( handshake, isOK ); + tr_free( handshake ); +} + +void +tr_handshakeAbort( tr_handshake * handshake ) +{ + tr_handshakeDone( handshake, FALSE ); +} + +static void +gotError( struct bufferevent * evbuf UNUSED, short what UNUSED, void * arg ) +{ + tr_handshake * handshake = (tr_handshake *) arg; + + /* 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->shunUnencryptedPeers ) + && ( !tr_peerIoReconnect( handshake->io ) ) ) + { + int msgSize; + uint8_t * msg = buildHandshakeMessage( handshake, HANDSHAKE_EXTPREF_LTEP_PREFER, &msgSize ); + setReadState( handshake, AWAITING_HANDSHAKE ); + tr_peerIoWrite( handshake->io, msg, msgSize ); + tr_free( msg ); + } + else + { + tr_handshakeDone( handshake, FALSE ); + } +} + +/** +*** +**/ + +tr_handshake* +tr_handshakeNew( tr_peerIo * io, + tr_encryption_mode encryption_mode, + handshakeDoneCB doneCB, + void * doneUserData ) +{ + tr_handshake * handshake; + +// w00t +//static int count = 0; +//if( count++ ) return NULL; + + handshake = tr_new0( tr_handshake, 1 ); + handshake->io = io; + handshake->crypto = tr_peerIoGetCrypto( io ); + handshake->shunUnencryptedPeers = encryption_mode==TR_ENCRYPTION_REQUIRED; + handshake->doneCB = doneCB; + handshake->doneUserData = doneUserData; + handshake->handle = tr_peerIoGetHandle( io ); + + tr_peerIoSetIOMode( io, EV_READ|EV_WRITE, 0 ); + tr_peerIoSetIOFuncs( io, canRead, NULL, gotError, handshake ); + +dbgmsg( handshake, "new handshake for io %p", io ); + + if( tr_peerIoIsIncoming( io ) ) + setReadState( handshake, AWAITING_HANDSHAKE ); + else + sendYa( handshake ); + + return handshake; +} + +const struct in_addr * +tr_handshakeGetAddr( const struct tr_handshake * handshake, uint16_t * port ) +{ + assert( handshake != NULL ); + assert( handshake->io != NULL ); + + return tr_peerIoGetAddress( handshake->io, port ); +} + + diff --git a/libtransmission/handshake.h b/libtransmission/handshake.h new file mode 100644 index 000000000..7231fc8f0 --- /dev/null +++ b/libtransmission/handshake.h @@ -0,0 +1,39 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_HANDSHAKE_H +#define TR_HANDSHAKE_H + +#include "transmission.h" + +struct in_addr; +struct tr_peerIo; +typedef struct tr_handshake tr_handshake; + +typedef void (*handshakeDoneCB)(struct tr_handshake * handshake, + struct tr_peerIo * io, + int isConnected, + const uint8_t * peerId, + int peerSupportsEncryption, + void * userData ); + +tr_handshake * tr_handshakeNew( struct tr_peerIo * io, + tr_encryption_mode encryptionMode, + handshakeDoneCB doneCB, + void * doneUserData ); + +const struct in_addr * tr_handshakeGetAddr( const struct tr_handshake * handshake, + uint16_t * setme_port ); + +void tr_handshakeAbort( tr_handshake * handshake ); + +#endif diff --git a/libtransmission/http.c b/libtransmission/http.c index e9b7f65fd..fb239693c 100644 --- a/libtransmission/http.c +++ b/libtransmission/http.c @@ -31,6 +31,8 @@ #include +#include "evdns.h" + #ifdef __BEOS__ extern int vasprintf( char **, const char *, va_list ); #endif @@ -84,7 +86,6 @@ struct tr_http_s { #define HTTP_LENGTH_FIXED 3 #define HTTP_LENGTH_CHUNKED 4 char lengthtype; - tr_resolve_t * resolve; char * host; int port; int sock; @@ -567,6 +568,20 @@ tr_httpGetBody( tr_http_t * http, const char ** buf, int * len ) *len = http->body.used; } +static void +resolve_cb( int result, char type, int count, int ttl, void *addresses, void *arg) +{ + tr_http_t * http = (tr_http_t *) arg; + + if( (result!=DNS_ERR_NONE) || (type!=DNS_IPv4_A) || (ttl<0) || (count<1) ) + http->state = HTTP_STATE_ERROR; + else { + struct in_addr *in_addrs = addresses; + http->sock = tr_netOpenTCP( &in_addrs[0], htons(http->port), 1 ); + http->state = HTTP_STATE_CONNECT; + } +} + tr_tristate_t tr_httpPulse( tr_http_t * http, const char ** data, int * len ) { @@ -586,8 +601,7 @@ tr_httpPulse( tr_http_t * http, const char ** data, int * len ) http->state = HTTP_STATE_CONNECT; break; } - http->resolve = tr_netResolveInit( http->host ); - if( NULL == http->resolve ) + if( evdns_resolve_ipv4( http->host, 0, resolve_cb, http ) ) { goto err; } @@ -595,19 +609,7 @@ tr_httpPulse( tr_http_t * http, const char ** data, int * len ) /* fallthrough */ case HTTP_STATE_RESOLVE: - switch( tr_netResolvePulse( http->resolve, &addr ) ) - { - case TR_NET_WAIT: - return TR_NET_WAIT; - case TR_NET_ERROR: - goto err; - case TR_NET_OK: - tr_netResolveClose( http->resolve ); - http->resolve = NULL; - http->sock = tr_netOpenTCP( &addr, htons( http->port ), 1 ); - http->state = HTTP_STATE_CONNECT; - } - /* fallthrough */ + return TR_NET_WAIT; case HTTP_STATE_CONNECT: switch( sendrequest( http ) ) @@ -897,15 +899,8 @@ tr_httpWhatsMyAddress( tr_http_t * http ) void tr_httpClose( tr_http_t * http ) { - if( NULL != http->resolve ) - { - tr_netResolveClose( http->resolve ); - } free( http->host ); - if( 0 <= http->sock ) - { - tr_netClose( http->sock ); - } + tr_netClose( http->sock ); free( http->header.buf ); free( http->body.buf ); free( http ); diff --git a/libtransmission/inout.c b/libtransmission/inout.c index e290837af..d4921e31a 100644 --- a/libtransmission/inout.c +++ b/libtransmission/inout.c @@ -20,38 +20,41 @@ #include "transmission.h" #include "completion.h" +#include "crypto.h" +#include "fastresume.h" #include "fdlimit.h" #include "inout.h" +#include "list.h" #include "net.h" -#include "peer.h" -#include "sha1.h" +#include "platform.h" +#include "peer-mgr.h" #include "utils.h" -struct tr_io_s +struct tr_io { - tr_torrent_t * tor; + tr_torrent * tor; }; /**** ***** Low-level IO functions ****/ -enum { TR_IO_READ, TR_IO_WRITE }; - #ifdef WIN32 #define lseek _lseeki64 #endif +enum { TR_IO_READ, TR_IO_WRITE }; + static int -readOrWriteBytes ( const tr_torrent_t * tor, +readOrWriteBytes ( const tr_torrent * tor, int ioMode, int fileIndex, uint64_t fileOffset, void * buf, size_t buflen ) { - const tr_info_t * info = &tor->info; - const tr_file_t * file = &info->files[fileIndex]; + const tr_info * info = &tor->info; + const tr_file * file = &info->files[fileIndex]; typedef size_t (* iofunc) ( int, void *, size_t ); iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write; char path[MAX_PATH_LENGTH]; @@ -85,13 +88,13 @@ readOrWriteBytes ( const tr_torrent_t * tor, } static void -findFileLocation ( const tr_torrent_t * tor, +findFileLocation ( const tr_torrent * tor, int pieceIndex, int pieceOffset, int * fileIndex, uint64_t * fileOffset ) { - const tr_info_t * info = &tor->info; + const tr_info * info = &tor->info; int i; uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset; @@ -112,14 +115,14 @@ findFileLocation ( const tr_torrent_t * tor, } static int -ensureMinimumFileSize ( const tr_torrent_t * tor, +ensureMinimumFileSize ( const tr_torrent * tor, int fileIndex, uint64_t minSize ) /* in bytes */ { int fd; int ret; struct stat sb; - const tr_file_t * file = &tor->info.files[fileIndex]; + const tr_file * file = &tor->info.files[fileIndex]; assert ( 0<=fileIndex && fileIndexinfo.fileCount ); assert ( minSize <= file->length ); @@ -143,17 +146,17 @@ ensureMinimumFileSize ( const tr_torrent_t * tor, } static int -readOrWritePiece ( tr_torrent_t * tor, - int ioMode, - int pieceIndex, - int pieceOffset, - uint8_t * buf, - size_t buflen ) +readOrWritePiece ( tr_torrent * tor, + int ioMode, + int pieceIndex, + int pieceOffset, + uint8_t * buf, + size_t buflen ) { int ret = 0; int fileIndex; uint64_t fileOffset; - const tr_info_t * info = &tor->info; + const tr_info * info = &tor->info; assert( 0<=pieceIndex && pieceIndexinfo.pieceCount ); assert( buflen <= (size_t) tr_torPieceCountBytes( tor, pieceIndex ) ); @@ -162,7 +165,7 @@ readOrWritePiece ( tr_torrent_t * tor, while( buflen && !ret ) { - const tr_file_t * file = &info->files[fileIndex]; + const tr_file * file = &info->files[fileIndex]; const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset ); if( ioMode == TR_IO_WRITE ) @@ -181,15 +184,15 @@ readOrWritePiece ( tr_torrent_t * tor, } int -tr_ioRead( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf ) +tr_ioRead( tr_torrent * tor, int pieceIndex, int begin, int len, uint8_t * buf ) { - return readOrWritePiece ( io->tor, TR_IO_READ, pieceIndex, begin, buf, len ); + return readOrWritePiece ( tor, TR_IO_READ, pieceIndex, begin, buf, len ); } int -tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf ) +tr_ioWrite( tr_torrent * tor, int pieceIndex, int begin, int len, uint8_t * buf ) { - return readOrWritePiece ( io->tor, TR_IO_WRITE, pieceIndex, begin, buf, len ); + return readOrWritePiece ( tor, TR_IO_WRITE, pieceIndex, begin, buf, len ); } /**** @@ -197,14 +200,14 @@ tr_ioWrite( tr_io_t * io, int pieceIndex, int begin, int len, uint8_t * buf ) ****/ static int -tr_ioRecalculateHash ( tr_torrent_t * tor, +tr_ioRecalculateHash ( tr_torrent * tor, int pieceIndex, uint8_t * setme ) { int n; int ret; uint8_t * buf; - const tr_info_t * info; + const tr_info * info; assert( tor != NULL ); assert( setme != NULL ); @@ -215,16 +218,15 @@ tr_ioRecalculateHash ( tr_torrent_t * tor, buf = malloc( n ); ret = readOrWritePiece ( tor, TR_IO_READ, pieceIndex, 0, buf, n ); - if( !ret ) { - SHA1( buf, n, setme ); - } + if( !ret ) + tr_sha1( setme, buf, n, NULL ); free( buf ); return ret; } static int -checkPiece ( tr_torrent_t * tor, int pieceIndex ) +checkPiece ( tr_torrent * tor, int pieceIndex ) { uint8_t hash[SHA_DIGEST_LENGTH]; int ret = tr_ioRecalculateHash( tor, pieceIndex, hash ) @@ -234,81 +236,24 @@ checkPiece ( tr_torrent_t * tor, int pieceIndex ) return ret; } -void -tr_ioCheckFiles( tr_torrent_t * tor ) -{ - assert( tor != NULL ); - assert( tor->completion != NULL ); - assert( tor->info.pieceCount > 0 ); - - if( tor->uncheckedPieces != NULL ) - { - int i; - - /* remove the unchecked pieces from completion... */ - for( i=0; iinfo.pieceCount; ++i ) - if( tr_bitfieldHas( tor->uncheckedPieces, i ) ) - tr_cpPieceRem( tor->completion, i ); - - tr_inf( "Verifying some pieces of \"%s\"", tor->info.name ); - - for( i=0; iinfo.pieceCount; ++i ) - { - if( !tr_bitfieldHas( tor->uncheckedPieces, i ) ) - continue; - - tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) ); - tr_bitfieldRem( tor->uncheckedPieces, i ); - } - - tr_bitfieldFree( tor->uncheckedPieces ); - tor->uncheckedPieces = NULL; - tor->fastResumeDirty = TRUE; - } -} - -/**** -***** Life Cycle -****/ - -tr_io_t* -tr_ioNew ( tr_torrent_t * tor ) -{ - tr_io_t * io = tr_calloc( 1, sizeof( tr_io_t ) ); - io->tor = tor; - return io; -} - +/** +*** +**/ void -tr_ioSync( tr_io_t * io ) +tr_ioClose( const tr_torrent * tor ) { - if( io != NULL ) - { - int i; - const tr_info_t * info = &io->tor->info; + int i; + const tr_info * info = &tor->info; - for( i=0; ifileCount; ++i ) - tr_fdFileClose( io->tor->destination, info->files[i].name ); - } -} - -void -tr_ioClose( tr_io_t * io ) -{ - if( io != NULL ) - { - tr_ioSync( io ); - tr_free( io ); - } + for( i=0; ifileCount; ++i ) + tr_fdFileClose( tor->destination, info->files[i].name ); } int -tr_ioHash( tr_io_t * io, int pieceIndex ) +tr_ioHash( tr_torrent * tor, int pieceIndex ) { - int i; int ret; - tr_torrent_t * tor = io->tor; const int success = !checkPiece( tor, pieceIndex ); if( success ) @@ -324,9 +269,137 @@ tr_ioHash( tr_io_t * io, int pieceIndex ) ret = TR_ERROR; } - /* Assign blame or credit to peers */ - for( i=0; ipeerCount; ++i ) - tr_peerBlame( tor->peers[i], pieceIndex, success ); + tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash, pieceIndex, success ); return ret; } + +/** +*** +**/ + +struct recheck_node +{ + tr_torrent * torrent; + tr_recheck_done_cb recheck_done_cb; + run_status_t status_when_done; +}; + +static void +fireCheckDone( tr_torrent * torrent, + tr_recheck_done_cb recheck_done_cb, + run_status_t status_when_done ) +{ + torrent->runStatus = status_when_done; + (*recheck_done_cb)( torrent ); +} + +struct recheck_node currentNode; + +static tr_list * recheckList = NULL; + +static tr_thread * recheckThread = NULL; + +static int stopCurrent = FALSE; + +static void +recheckThreadFunc( void * unused UNUSED ) +{ + for( ;; ) + { + int i; + tr_torrent * tor; + + struct recheck_node * node = (struct recheck_node*) recheckList ? recheckList->data : NULL; + if( node == NULL ) + break; + + currentNode = *node; + tor = currentNode.torrent; + tr_list_remove_data( &recheckList, node ); + tr_free( node ); + + if( tor->uncheckedPieces == NULL ) { + fireCheckDone( tor, currentNode.recheck_done_cb, currentNode.status_when_done ); + continue; + } + + tor->runStatus = TR_RUN_CHECKING; + + /* remove the unchecked pieces from completion... */ + for( i=0; iinfo.pieceCount; ++i ) + if( tr_bitfieldHas( tor->uncheckedPieces, i ) ) + tr_cpPieceRem( tor->completion, i ); + + tr_inf( "Verifying some pieces of \"%s\"", tor->info.name ); + + for( i=0; iinfo.pieceCount && !stopCurrent; ++i ) + { + if( !tr_bitfieldHas( tor->uncheckedPieces, i ) ) + continue; + + tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) ); + tr_bitfieldRem( tor->uncheckedPieces, i ); + } + + if( !stopCurrent ) + { + tr_bitfieldFree( tor->uncheckedPieces ); + tor->uncheckedPieces = NULL; + } + stopCurrent = FALSE; + tr_fastResumeSave( tor ); + fireCheckDone( tor, currentNode.recheck_done_cb, currentNode.status_when_done ); + } + + recheckThread = NULL; +} + +void +tr_ioRecheckAdd( tr_torrent * tor, + tr_recheck_done_cb recheck_done_cb, + run_status_t status_when_done ) +{ + if( tor->uncheckedPieces == NULL ) + { + fireCheckDone( tor, recheck_done_cb, status_when_done ); + } + else + { + struct recheck_node * node; + node = tr_new( struct recheck_node, 1 ); + node->torrent = tor; + node->recheck_done_cb = recheck_done_cb; + node->status_when_done = status_when_done; + tr_list_append( &recheckList, node ); + + tor->runStatus = TR_RUN_CHECKING_WAIT; + + if( recheckThread == NULL ) + recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" ); + } +} + +static int +compareRecheckByTorrent( const void * va, const void * vb ) +{ + const struct recheck_node * a = ( const struct recheck_node * ) va; + const struct recheck_node * b = ( const struct recheck_node * ) vb; + return a->torrent - b->torrent; +} + +void +tr_ioRecheckRemove( tr_torrent * tor ) +{ + if( tor == currentNode.torrent ) + stopCurrent = TRUE; + else { + struct recheck_node tmp; + tmp.torrent = tor; + struct recheck_node * node = tr_list_remove( &recheckList, &tmp, compareRecheckByTorrent ); + if( node != NULL ) { + fireCheckDone( tor, node->recheck_done_cb, node->status_when_done ); + tr_free( node ); + } + } +} diff --git a/libtransmission/inout.h b/libtransmission/inout.h index 17e594893..1345ce041 100644 --- a/libtransmission/inout.h +++ b/libtransmission/inout.h @@ -25,11 +25,9 @@ #ifndef TR_IO_H #define TR_IO_H 1 -typedef struct tr_io_s tr_io_t; +struct tr_torrent; -void tr_ioCheckFiles ( tr_torrent_t * ); - -tr_io_t * tr_ioNew ( tr_torrent_t * ); +typedef struct tr_io tr_io; /*********************************************************************** * tr_ioRead, tr_ioWrite @@ -39,24 +37,29 @@ tr_io_t * tr_ioNew ( tr_torrent_t * ); * TR_ERROR_ASSERT if the parameters are incorrect, one of the * TR_ERROR_IO_* otherwise. **********************************************************************/ -int tr_ioRead ( tr_io_t *, int index, int begin, int len, uint8_t * ); -int tr_ioWrite ( tr_io_t *, int index, int begin, int len, uint8_t * ); +int tr_ioRead ( struct tr_torrent*, int index, int begin, int len, uint8_t * ); +int tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, uint8_t * ); -/*********************************************************************** - * tr_ioHash - *********************************************************************** - * Hashes the specified piece and updates the completion accordingly. - **********************************************************************/ -int tr_ioHash ( tr_io_t *, int piece ); +/* hashes the specified piece and updates the completion accordingly. */ +int tr_ioHash ( tr_torrent*, int piece ); -/*********************************************************************** - * tr_ioSync - *********************************************************************** - * Flush all data on disc by closing all files, and update the cache - * file. - **********************************************************************/ -void tr_ioSync( tr_io_t * ); +/* close all the files associated with this torrent*/ +void tr_ioClose( const tr_torrent * ); -void tr_ioClose ( tr_io_t * ); +/** +*** +**/ + +typedef void (*tr_recheck_done_cb)( tr_torrent * tor ); + +void tr_ioRecheckAdd( tr_torrent * tor, + tr_recheck_done_cb recheck_done_cb, + run_status_t status_when_done ); + +void tr_ioRecheckRemove( tr_torrent * tor ); + +/** +*** +**/ #endif diff --git a/libtransmission/internal.h b/libtransmission/internal.h index 800369eb0..c254daa63 100644 --- a/libtransmission/internal.h +++ b/libtransmission/internal.h @@ -45,30 +45,26 @@ typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t; #define FALSE 0 #endif -int tr_trackerInfoInit( struct tr_tracker_info_s * info, - const char * address, - int address_len ); +int tr_trackerInfoInit( struct tr_tracker_info * info, + const char * address, + int address_len ); -void tr_trackerInfoClear( struct tr_tracker_info_s * info ); - -struct tr_peer_s; +void tr_trackerInfoClear( struct tr_tracker_info * info ); void tr_peerIdNew ( char* buf, int buflen ); -void tr_torrentResetTransferStats( tr_torrent_t * ); +void tr_torrentResetTransferStats( tr_torrent * ); -int tr_torrentAddCompact( tr_torrent_t * tor, int from, - const uint8_t * buf, int count ); -int tr_torrentAttachPeer( tr_torrent_t * tor, struct tr_peer_s * ); +void tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has ); -void tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has ); +void tr_torrentLock ( const tr_torrent * ); +void tr_torrentUnlock ( const tr_torrent * ); -void tr_torrentReaderLock ( const tr_torrent_t * ); -void tr_torrentReaderUnlock ( const tr_torrent_t * ); -void tr_torrentWriterLock ( tr_torrent_t * ); -void tr_torrentWriterUnlock ( tr_torrent_t * ); +void tr_torrentChangeMyPort ( tr_torrent *, int port ); -void tr_torrentChangeMyPort ( tr_torrent_t *, int port ); +int tr_torrentExists( tr_handle *, const uint8_t * ); +tr_torrent* tr_torrentFindFromHash( tr_handle *, const uint8_t * ); +tr_torrent* tr_torrentFindFromObfuscatedHash( tr_handle *, const uint8_t* ); /* get the index of this piece's first block */ #define tr_torPieceFirstBlock(tor,piece) ( (piece) * (tor)->blockCountInPiece ) @@ -89,37 +85,40 @@ void tr_torrentChangeMyPort ( tr_torrent_t *, int port ); ( ((block)==((tor)->blockCount-1)) ? (tor)->lastBlockSize : (tor)->blockSize ) #define tr_block(a,b) _tr_block(tor,a,b) -int _tr_block( const tr_torrent_t * tor, int index, int begin ); +int _tr_block( const tr_torrent * tor, int index, int begin ); typedef enum { - TR_RUN_CHECKING = (1<<0), /* checking files' checksums */ - TR_RUN_RUNNING = (1<<1), /* seeding or leeching */ - TR_RUN_STOPPING = (1<<2), /* stopping */ - TR_RUN_STOPPING_NET_WAIT = (1<<3), /* waiting on network -- we're - telling tracker we've stopped */ + TR_RUN_CHECKING_WAIT = (1<<0), /* waiting to be checked */ + TR_RUN_CHECKING = (1<<1), /* checking files' checksums */ + TR_RUN_RUNNING = (1<<2), /* seeding or leeching */ + TR_RUN_STOPPING = (1<<3), /* waiting for acknowledgment from tracker */ TR_RUN_STOPPED = (1<<4) /* stopped */ } run_status_t; #define TR_ID_LEN 20 -struct tr_torrent_s +struct tr_torrent { - tr_handle_t * handle; - tr_info_t info; + tr_handle * handle; + tr_info info; - tr_speedlimit_t uploadLimitMode; - tr_speedlimit_t downloadLimitMode; - struct tr_ratecontrol_s * upload; - struct tr_ratecontrol_s * download; - struct tr_ratecontrol_s * swarmspeed; + tr_speedlimit uploadLimitMode; + tr_speedlimit downloadLimitMode; + struct tr_ratecontrol * upload; + struct tr_ratecontrol * download; + struct tr_ratecontrol * swarmspeed; + + struct tr_timer * saveTimer; int error; char errorString[128]; int hasChangedState; + uint8_t obfuscatedHash[SHA_DIGEST_LENGTH]; + uint8_t * azId; int publicPort; @@ -136,27 +135,20 @@ struct tr_torrent_s int blockCountInPiece; int blockCountInLastPiece; - struct tr_completion_s * completion; + struct tr_completion * completion; volatile char dieFlag; - struct tr_bitfield_s * uncheckedPieces; + struct tr_bitfield * uncheckedPieces; run_status_t runStatus; run_status_t runStatusToSave; char runStatusToSaveIsSet; cp_status_t cpStatus; - struct tr_thread_s * thread; - struct tr_rwlock_s * lock; + struct tr_lock * lock; - struct tr_tracker_s * tracker; + struct tr_tracker * tracker; struct tr_publisher_tag * trackerSubscription; - struct tr_io_s * io; uint64_t startDate; uint64_t stopDate; - char ioLoaded; - char fastResumeDirty; - - int peerCount; - struct tr_peer_s * peers[TR_MAX_PEER_COUNT]; uint64_t downloadedCur; uint64_t downloadedPrev; @@ -169,31 +161,36 @@ struct tr_torrent_s uint8_t pexDisabled; int8_t statCur; - tr_stat_t stats[2]; + tr_stat stats[2]; - tr_torrent_t * next; + tr_torrent * next; }; -struct tr_handle_s +struct tr_handle { - struct tr_event_handle_s * events; + tr_encryption_mode encryptionMode; + + struct tr_event_handle * events; int torrentCount; - tr_torrent_t * torrentList; + tr_torrent * torrentList; char * tag; int isPortSet; char useUploadLimit; char useDownloadLimit; - struct tr_ratecontrol_s * upload; - struct tr_ratecontrol_s * download; + struct tr_ratecontrol * upload; + struct tr_ratecontrol * download; - struct tr_shared_s * shared; + struct tr_peerMgr * peerMgr; + struct tr_shared * shared; - tr_handle_status_t stats[2]; + tr_handle_status stats[2]; int statCur; + uint8_t isClosed; + #define TR_AZ_ID_LEN 20 uint8_t azId[TR_AZ_ID_LEN]; }; diff --git a/libtransmission/ipcparse.c b/libtransmission/ipcparse.c index 5a7ebce92..bff417910 100644 --- a/libtransmission/ipcparse.c +++ b/libtransmission/ipcparse.c @@ -585,7 +585,7 @@ ipc_mkgetinfo( struct ipc_info * info, size_t * len, enum ipc_msg id, } int -ipc_addinfo( benc_val_t * list, int tor, const tr_info_t * inf, int types ) +ipc_addinfo( benc_val_t * list, int tor, const tr_info * inf, int types ) { benc_val_t * dict, * item, * file, * tier; int ii, jj, kk; @@ -727,7 +727,7 @@ ipc_addinfo( benc_val_t * list, int tor, const tr_info_t * inf, int types ) int ipc_addstat( benc_val_t * list, int tor, - const tr_stat_t * st, int types ) + const tr_stat * st, int types ) { benc_val_t * dict, * item; int ii, used; diff --git a/libtransmission/ipcparse.h b/libtransmission/ipcparse.h index 5c2bab3dc..eaf828b3a 100644 --- a/libtransmission/ipcparse.h +++ b/libtransmission/ipcparse.h @@ -27,7 +27,7 @@ #include -/* yay for typedefs, we can't forward declare benc_val_t or tr_info_t +/* yay for typedefs, we can't forward declare benc_val_t or tr_info like with structs */ #include "bencode.h" #include "transmission.h" @@ -159,8 +159,8 @@ uint8_t * ipc_mkstr ( struct ipc_info *, size_t *, enum ipc_msg, int64_t, uint8_t * ipc_mkvers ( size_t *, const char * ); uint8_t * ipc_mkgetinfo( struct ipc_info *, size_t *, enum ipc_msg, int64_t, int, const int * ); -int ipc_addinfo ( benc_val_t *, int, const tr_info_t *, int ); -int ipc_addstat ( benc_val_t *, int, const tr_stat_t *, int ); +int ipc_addinfo ( benc_val_t *, int, const tr_info *, int ); +int ipc_addstat ( benc_val_t *, int, const tr_stat *, int ); /* sets errno to EINVAL on parse error or EPERM for unsupported protocol version */ diff --git a/libtransmission/list.c b/libtransmission/list.c index 205caa6d8..f980ee59c 100644 --- a/libtransmission/list.c +++ b/libtransmission/list.c @@ -14,14 +14,14 @@ #include "list.h" #include "utils.h" -static tr_list_t* +static tr_list* node_alloc( void ) { - return tr_new0( tr_list_t, 1 ); + return tr_new0( tr_list, 1 ); } static void -node_free( tr_list_t* node ) +node_free( tr_list* node ) { tr_free( node ); } @@ -31,20 +31,20 @@ node_free( tr_list_t* node ) ***/ void -tr_list_free( tr_list_t** list ) +tr_list_free( tr_list** list ) { while( *list ) { - tr_list_t * node = *list; + tr_list * node = *list; *list = (*list)->next; node_free( node ); } } void -tr_list_prepend( tr_list_t ** list, void * data ) +tr_list_prepend( tr_list ** list, void * data ) { - tr_list_t * node = node_alloc (); + tr_list * node = node_alloc (); node->data = data; node->next = *list; if( *list ) @@ -53,14 +53,14 @@ tr_list_prepend( tr_list_t ** list, void * data ) } void -tr_list_append( tr_list_t ** list, void * data ) +tr_list_append( tr_list ** list, void * data ) { - tr_list_t * node = node_alloc( ); + tr_list * node = node_alloc( ); node->data = data; if( !*list ) *list = node; else { - tr_list_t * l = *list; + tr_list * l = *list; while( l->next ) l = l->next; l->next = node; @@ -69,12 +69,12 @@ tr_list_append( tr_list_t ** list, void * data ) } void -tr_list_insert_sorted( tr_list_t ** list, +tr_list_insert_sorted( tr_list ** list, void * data, int compare(const void*,const void*) ) { /* find l, the node that we'll insert this data before */ - tr_list_t * l; + tr_list * l; for( l=*list; l!=NULL; l=l->next ) { const int c = (compare)( data, l->data ); if( c <= 0 ) @@ -86,7 +86,7 @@ tr_list_insert_sorted( tr_list_t ** list, else if( l == *list ) tr_list_prepend( list, data ); else { - tr_list_t * node = node_alloc( ); + tr_list * node = node_alloc( ); node->data = data; if( l->prev ) { node->prev = l->prev; node->prev->next = node; } node->next = l; @@ -95,8 +95,8 @@ tr_list_insert_sorted( tr_list_t ** list, } -tr_list_t* -tr_list_find_data ( tr_list_t * list, const void * data ) +tr_list* +tr_list_find_data ( tr_list * list, const void * data ) { for(; list; list=list->next ) if( list->data == data ) @@ -105,20 +105,49 @@ tr_list_find_data ( tr_list_t * list, const void * data ) return NULL; } -void -tr_list_remove_data ( tr_list_t ** list, const void * data ) +static void* +tr_list_remove_node ( tr_list ** list, tr_list * node ) { - tr_list_t * node = tr_list_find_data( *list, data ); - tr_list_t * prev = node ? node->prev : NULL; - tr_list_t * next = node ? node->next : NULL; + void * data; + tr_list * prev = node ? node->prev : NULL; + tr_list * next = node ? node->next : NULL; if( prev ) prev->next = next; if( next ) next->prev = prev; if( *list == node ) *list = next; + data = node ? node->data : NULL; node_free( node ); + return data; } -tr_list_t* -tr_list_find ( tr_list_t * list , const void * b, TrListCompareFunc func ) +void* +tr_list_pop_front( tr_list ** list ) +{ + void * ret = NULL; + if( *list != NULL ) + { + ret = (*list)->data; + tr_list_remove_node( list, *list ); + } + return ret; +} + +void* +tr_list_remove_data ( tr_list ** list, const void * data ) +{ + return tr_list_remove_node( list, tr_list_find_data( *list, data ) ); +} + +void* +tr_list_remove( tr_list ** list, + const void * b, + TrListCompareFunc compare_func ) +{ + return tr_list_remove_node( list, tr_list_find( *list, b, compare_func ) ); +} + + +tr_list* +tr_list_find ( tr_list * list , const void * b, TrListCompareFunc func ) { for( ; list; list=list->next ) if( !func( list->data, b ) ) @@ -128,11 +157,21 @@ tr_list_find ( tr_list_t * list , const void * b, TrListCompareFunc func ) } void -tr_list_foreach( tr_list_t * list, TrListForeachFunc func ) +tr_list_foreach( tr_list * list, TrListForeachFunc func ) { - while( list ) - { + while( list != NULL ) { func( list->data ); list = list->next; } } + +int +tr_list_size( const tr_list * list ) +{ + int size = 0; + while( list != NULL ) { + ++size; + list = list->next; + } + return size; +} diff --git a/libtransmission/list.h b/libtransmission/list.h index 6043166a2..46485f812 100644 --- a/libtransmission/list.h +++ b/libtransmission/list.h @@ -13,41 +13,49 @@ #ifndef TR_LIST_H #define TR_LIST_H -typedef struct tr_list_s +typedef struct tr_list { - void * data; - struct tr_list_s * next; - struct tr_list_s * prev; + void * data; + struct tr_list * next; + struct tr_list * prev; } -tr_list_t; +tr_list; typedef int (*TrListCompareFunc)(const void * a, const void * b); typedef void (*TrListForeachFunc)(void *); -void tr_list_free ( tr_list_t ** list ); +int tr_list_size ( const tr_list * list ); -void tr_list_append ( tr_list_t ** list, - void * data ); +void tr_list_free ( tr_list ** list ); -void tr_list_prepend ( tr_list_t ** list, - void * data ); +void tr_list_append ( tr_list ** list, + void * data ); -void tr_list_remove_data ( tr_list_t ** list, - const void * data ); +void tr_list_prepend ( tr_list ** list, + void * data ); -void tr_list_insert_sorted ( tr_list_t ** list, - void * data, - TrListCompareFunc compare_func ); +void* tr_list_pop_front ( tr_list ** list ); -tr_list_t* tr_list_find ( tr_list_t * list, - const void * b, - TrListCompareFunc compare_func ); +void* tr_list_remove_data ( tr_list ** list, + const void * data ); -tr_list_t* tr_list_find_data ( tr_list_t * list, - const void * data ); +void* tr_list_remove ( tr_list ** list, + const void * b, + TrListCompareFunc compare_func ); -void tr_list_foreach ( tr_list_t * list, - TrListForeachFunc foreach_func ); +void tr_list_insert_sorted ( tr_list ** list, + void * data, + TrListCompareFunc compare_func ); + +tr_list* tr_list_find ( tr_list * list, + const void * b, + TrListCompareFunc compare_func ); + +tr_list* tr_list_find_data ( tr_list * list, + const void * data ); + +void tr_list_foreach ( tr_list * list, + TrListForeachFunc foreach_func ); #endif /* TR_LIST_H */ diff --git a/libtransmission/makemeta.c b/libtransmission/makemeta.c index e80d86ddc..b3872750d 100644 --- a/libtransmission/makemeta.c +++ b/libtransmission/makemeta.c @@ -21,14 +21,13 @@ #include #include -#include "trcompat.h" /* for strlcpy */ +#include "crypto.h" /* tr_sha1 */ +#include "trcompat.h" /* strlcpy */ #include "transmission.h" -#include "internal.h" /* for tr_torrent_t */ #include "bencode.h" #include "makemeta.h" #include "platform.h" /* threads, locks */ #include "shared.h" /* shared lock */ -#include "sha1.h" #include "utils.h" /* buildpath */ #include "version.h" @@ -231,7 +230,7 @@ getHashInfo ( tr_metainfo_builder_t * b ) assert( bufptr-buf == (int)thisPieceSize ); assert( pieceRemain == 0 ); - SHA1( buf, thisPieceSize, walk ); + tr_sha1( buf, walk, thisPieceSize, NULL ); walk += SHA_DIGEST_LENGTH; if( b->abortFlag ) { @@ -410,11 +409,11 @@ static void tr_realMakeMetaInfo ( tr_metainfo_builder_t * builder ) static tr_metainfo_builder_t * queue = NULL; -static tr_thread_t * workerThread = NULL; +static tr_thread * workerThread = NULL; -static tr_lock_t* getQueueLock( tr_handle_t * h ) +static tr_lock* getQueueLock( tr_handle_t * h ) { - static tr_lock_t * lock = NULL; + static tr_lock * lock = NULL; tr_sharedLock( h->shared ); if( !lock ) @@ -433,7 +432,7 @@ static void workerFunc( void * user_data ) tr_metainfo_builder_t * builder = NULL; /* find the next builder to process */ - tr_lock_t * lock = getQueueLock ( handle ); + tr_lock * lock = getQueueLock ( handle ); tr_lockLock( lock ); if( queue != NULL ) { builder = queue; @@ -458,7 +457,7 @@ tr_makeMetaInfo( tr_metainfo_builder_t * builder, const char * comment, int isPrivate ) { - tr_lock_t * lock; + tr_lock * lock; builder->abortFlag = 0; builder->isDone = 0; diff --git a/libtransmission/metainfo.c b/libtransmission/metainfo.c index f118a07ce..fe23d3633 100644 --- a/libtransmission/metainfo.c +++ b/libtransmission/metainfo.c @@ -33,10 +33,10 @@ #include "transmission.h" #include "bencode.h" +#include "crypto.h" /* tr_sha1 */ #include "http.h" /* tr_httpParseUrl */ #include "metainfo.h" #include "platform.h" -#include "sha1.h" #include "utils.h" #define TORRENT_MAX_SIZE (5*1024*1024) @@ -44,7 +44,7 @@ /*********************************************************************** * Local prototypes **********************************************************************/ -static int realparse( tr_info_t * inf, const uint8_t * buf, size_t len ); +static int realparse( tr_info * inf, const uint8_t * buf, size_t len ); static void savedname( char * name, size_t len, const char * hash, const char * tag ); static uint8_t * readtorrent( const char * path, size_t * len ); @@ -52,9 +52,9 @@ static int savetorrent( const char * hash, const char * tag, const uint8_t * buf, size_t buflen ); static int getfile( char * buf, int size, const char * prefix, benc_val_t * name ); -static int getannounce( tr_info_t * inf, benc_val_t * meta ); +static int getannounce( tr_info * inf, benc_val_t * meta ); static char * announceToScrape( const char * announce ); -static int parseFiles( tr_info_t * inf, benc_val_t * name, +static int parseFiles( tr_info * inf, benc_val_t * name, benc_val_t * files, benc_val_t * length ); /*********************************************************************** @@ -63,7 +63,7 @@ static int parseFiles( tr_info_t * inf, benc_val_t * name, * **********************************************************************/ int -tr_metainfoParseFile( tr_info_t * inf, const char * tag, +tr_metainfoParseFile( tr_info * inf, const char * tag, const char * path, int save ) { uint8_t * buf; @@ -102,7 +102,7 @@ tr_metainfoParseFile( tr_info_t * inf, const char * tag, } int -tr_metainfoParseData( tr_info_t * inf, const char * tag, +tr_metainfoParseData( tr_info * inf, const char * tag, const uint8_t * data, size_t size, int save ) { if( realparse( inf, data, size ) ) @@ -123,7 +123,7 @@ tr_metainfoParseData( tr_info_t * inf, const char * tag, } int -tr_metainfoParseHash( tr_info_t * inf, const char * tag, const char * hash ) +tr_metainfoParseHash( tr_info * inf, const char * tag, const char * hash ) { struct stat sb; uint8_t * buf; @@ -172,7 +172,7 @@ tr_metainfoParseHash( tr_info_t * inf, const char * tag, const char * hash ) } static int -realparse( tr_info_t * inf, const uint8_t * buf, size_t size ) +realparse( tr_info * inf, const uint8_t * buf, size_t size ) { benc_val_t meta, * beInfo, * val, * val2; int i; @@ -192,8 +192,9 @@ realparse( tr_info_t * inf, const uint8_t * buf, size_t size ) tr_bencFree( &meta ); return TR_EINVALID; } - SHA1( (uint8_t *) beInfo->begin, - (long) beInfo->end - (long) beInfo->begin, inf->hash ); + + tr_sha1( inf->hash, beInfo->begin, beInfo->end - beInfo->begin, NULL ); + for( i = 0; i < SHA_DIGEST_LENGTH; i++ ) { snprintf( inf->hashString + i * 2, sizeof( inf->hashString ) - i * 2, @@ -254,7 +255,7 @@ realparse( tr_info_t * inf, const uint8_t * buf, size_t size ) } inf->pieceCount = val->val.s.i / SHA_DIGEST_LENGTH; - inf->pieces = calloc ( inf->pieceCount, sizeof(tr_piece_t) ); + inf->pieces = calloc ( inf->pieceCount, sizeof(tr_piece) ); for ( i=0; ipieceCount; ++i ) { @@ -307,7 +308,7 @@ realparse( tr_info_t * inf, const uint8_t * buf, size_t size ) return TR_EINVALID; } -void tr_metainfoFree( tr_info_t * inf ) +void tr_metainfoFree( tr_info * inf ) { int i, j; @@ -322,7 +323,7 @@ void tr_metainfoFree( tr_info_t * inf ) } tr_free( inf->trackerList ); - memset( inf, '\0', sizeof(tr_info_t) ); + memset( inf, '\0', sizeof(tr_info) ); } static int getfile( char * buf, int size, @@ -381,12 +382,12 @@ static int getfile( char * buf, int size, return TR_OK; } -static int getannounce( tr_info_t * inf, benc_val_t * meta ) +static int getannounce( tr_info * inf, benc_val_t * meta ) { benc_val_t * val, * subval, * urlval; char * address, * announce; int ii, jj, port, random, subcount; - tr_tracker_info_t * sublist; + tr_tracker_info * sublist; void * swapping; /* Announce-list */ @@ -411,7 +412,7 @@ static int getannounce( tr_info_t * inf, benc_val_t * meta ) /* iterate through the tier's items */ for( jj = 0; jj < subval->val.l.count; jj++ ) { - tr_tracker_info_t tmp; + tr_tracker_info tmp; urlval = &subval->val.l.vals[jj]; if( TYPE_STR != urlval->type || @@ -558,9 +559,9 @@ static char * announceToScrape( const char * announce ) } int -tr_trackerInfoInit( tr_tracker_info_t * info, - const char * address, - int address_len ) +tr_trackerInfoInit( tr_tracker_info * info, + const char * address, + int address_len ) { int ret = tr_httpParseUrl( address, address_len, &info->address, @@ -573,12 +574,12 @@ tr_trackerInfoInit( tr_tracker_info_t * info, } void -tr_trackerInfoClear( tr_tracker_info_t * info ) +tr_trackerInfoClear( tr_tracker_info * info ) { tr_free( info->address ); tr_free( info->announce ); tr_free( info->scrape ); - memset( info, '\0', sizeof(tr_tracker_info_t) ); + memset( info, '\0', sizeof(tr_tracker_info) ); } @@ -690,7 +691,7 @@ savetorrent( const char * hash, const char * tag, } static int -parseFiles( tr_info_t * inf, benc_val_t * name, +parseFiles( tr_info * inf, benc_val_t * name, benc_val_t * files, benc_val_t * length ) { benc_val_t * item, * path; diff --git a/libtransmission/metainfo.h b/libtransmission/metainfo.h index 120d27e51..edcf5f7e6 100644 --- a/libtransmission/metainfo.h +++ b/libtransmission/metainfo.h @@ -27,12 +27,12 @@ #include "transmission.h" -int tr_metainfoParseFile( tr_info_t *, const char * tag, +int tr_metainfoParseFile( tr_info *, const char * tag, const char * path, int save ); -int tr_metainfoParseData( tr_info_t *, const char * tag, +int tr_metainfoParseData( tr_info *, const char * tag, const uint8_t * data, size_t size, int save ); -int tr_metainfoParseHash( tr_info_t *, const char * tag, const char * hash ); -void tr_metainfoFree( tr_info_t * inf ); +int tr_metainfoParseHash( tr_info *, const char * tag, const char * hash ); +void tr_metainfoFree( tr_info * inf ); void tr_metainfoRemoveSaved( const char * hashString, const char * tag ); #endif diff --git a/libtransmission/net.c b/libtransmission/net.c index 2ffa493da..39368d353 100644 --- a/libtransmission/net.c +++ b/libtransmission/net.c @@ -57,11 +57,7 @@ tr_netInit( void ) /*********************************************************************** * DNS resolution - **********************************************************************/ - -/*********************************************************************** - * tr_netResolve - *********************************************************************** + * * Synchronous "resolution": only works with character strings * representing numbers expressed in the Internet standard `.' notation. * Returns a non-zero value if an error occurs. @@ -72,184 +68,6 @@ int tr_netResolve( const char * address, struct in_addr * addr ) return ( addr->s_addr == 0xFFFFFFFF ); } -static tr_thread_t * resolveThread; -static tr_lock_t * resolveLock; -static tr_cond_t * resolveCond; -static volatile int resolveDie; -static tr_resolve_t * resolveQueue; - -static void resolveRelease ( tr_resolve_t * ); -static void resolveFunc ( void * ); - -struct tr_resolve_s -{ - tr_tristate_t status; - char * address; - struct in_addr addr; - - int refcount; - tr_resolve_t * next; -}; - -/*********************************************************************** - * tr_netResolveThreadInit - *********************************************************************** - * Initializes the static variables used for resolution and launch the - * gethostbyname thread. - **********************************************************************/ -void tr_netResolveThreadInit( void ) -{ - resolveDie = 0; - resolveQueue = NULL; - resolveLock = tr_lockNew( ); - resolveCond = tr_condNew( ); - resolveThread = tr_threadNew( resolveFunc, NULL, "resolve" ); -} - -/*********************************************************************** - * tr_netResolveThreadClose - *********************************************************************** - * Notices the gethostbyname thread that is should terminate. Doesn't - * wait until it does, in case it is stuck in a resolution: we let it - * die and clean itself up. - **********************************************************************/ -void tr_netResolveThreadClose( void ) -{ - tr_lockLock( resolveLock ); - resolveDie = 1; - tr_lockUnlock( resolveLock ); - tr_condSignal( resolveCond ); - tr_wait( 200 ); -} - -/*********************************************************************** - * tr_netResolveInit - *********************************************************************** - * Adds an address to the resolution queue. - **********************************************************************/ -tr_resolve_t * tr_netResolveInit( const char * address ) -{ - tr_resolve_t * r = tr_new0( tr_resolve_t, 1 ); - r->status = TR_NET_WAIT; - r->address = strdup( address ); - r->refcount = 2; - r->next = NULL; - - tr_lockLock( resolveLock ); - if( !resolveQueue ) - { - resolveQueue = r; - } - else - { - tr_resolve_t * iter; - for( iter = resolveQueue; iter->next; iter = iter->next ); - iter->next = r; - } - tr_lockUnlock( resolveLock ); - tr_condSignal( resolveCond ); - - return r; -} - -/*********************************************************************** - * tr_netResolvePulse - *********************************************************************** - * Checks the current status of a resolution. - **********************************************************************/ -tr_tristate_t tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr ) -{ - tr_tristate_t ret; - - tr_lockLock( resolveLock ); - ret = r->status; - if( ret == TR_NET_OK ) - { - *addr = r->addr; - } - tr_lockUnlock( resolveLock ); - - return ret; -} - -/*********************************************************************** - * tr_netResolveClose - *********************************************************************** - * - **********************************************************************/ -void tr_netResolveClose( tr_resolve_t * r ) -{ - resolveRelease( r ); -} - -/*********************************************************************** - * resolveRelease - *********************************************************************** - * The allocated tr_resolve_t structures should be freed when - * tr_netResolveClose was called *and* it was removed from the queue. - * This can happen in any order, so we use a refcount to know we can - * take it out. - **********************************************************************/ -static void resolveRelease( tr_resolve_t * r ) -{ - if( --r->refcount < 1 ) - { - free( r->address ); - free( r ); - } -} - -/*********************************************************************** - * resolveFunc - *********************************************************************** - * Keeps waiting for addresses to resolve, and removes them from the - * queue once resolution is done. - **********************************************************************/ -static void resolveFunc( void * arg UNUSED ) -{ - tr_resolve_t * r; - struct hostent * host; - - tr_lockLock( resolveLock ); - - while( !resolveDie ) - { - if( !( r = resolveQueue ) ) - { - tr_condWait( resolveCond, resolveLock ); - continue; - } - - /* Blocking resolution */ - tr_lockUnlock( resolveLock ); - host = gethostbyname( r->address ); - tr_lockLock( resolveLock ); - - if( host ) - { - memcpy( &r->addr, host->h_addr, host->h_length ); - r->status = TR_NET_OK; - } - else - { - r->status = TR_NET_ERROR; - } - - resolveQueue = r->next; - resolveRelease( r ); - } - - /* Clean up */ - tr_lockUnlock( resolveLock ); - tr_lockFree( resolveLock ); - resolveLock = NULL; - while( ( r = resolveQueue ) ) - { - resolveQueue = r->next; - resolveRelease( r ); - } -} - /*********************************************************************** * TCP/UDP sockets @@ -469,12 +287,14 @@ int tr_netRecvFrom( int s, uint8_t * buf, int size, struct sockaddr_in * addr ) return ret; } -void tr_netClose( int s ) +void +tr_netClose( int s ) { tr_fdSocketClose( s ); } -void tr_netNtop( const struct in_addr * addr, char * buf, int len ) +void +tr_netNtop( const struct in_addr * addr, char * buf, int len ) { const uint8_t * cast; diff --git a/libtransmission/net.h b/libtransmission/net.h index 9038f5270..b8ccf8897 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -67,13 +67,6 @@ struct sockaddr_in; **********************************************************************/ int tr_netResolve( const char *, struct in_addr * ); -typedef struct tr_resolve_s tr_resolve_t; -void tr_netResolveThreadInit( void ); -void tr_netResolveThreadClose( void ); -tr_resolve_t * tr_netResolveInit( const char * address ); -tr_tristate_t tr_netResolvePulse( tr_resolve_t *, struct in_addr * ); -void tr_netResolveClose( tr_resolve_t * ); - /*********************************************************************** * TCP and UDP sockets diff --git a/libtransmission/peer-io.c b/libtransmission/peer-io.c new file mode 100644 index 000000000..be0ff7a33 --- /dev/null +++ b/libtransmission/peer-io.c @@ -0,0 +1,519 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include "transmission.h" +#include "crypto.h" +#include "net.h" +#include "peer-io.h" +#include "ratecontrol.h" +#include "trevent.h" +#include "utils.h" + +/** +*** +**/ + +struct tr_peerIo +{ + struct tr_handle * handle; + + struct in_addr in_addr; + int port; + int socket; + int extensions; + int encryptionMode; + struct bufferevent * bufev; + uint8_t peerId[20]; + + unsigned int isEncrypted : 1; + unsigned int isIncoming : 1; + unsigned int peerIdIsSet : 1; + + tr_ratecontrol * rateToPeer; + tr_ratecontrol * rateToClient; + + tr_can_read_cb canRead; + tr_did_write_cb didWrite; + tr_net_error_cb gotError; + void * userData; + + tr_crypto * crypto; +}; + +/** +*** +**/ + +static void +didWriteWrapper( struct bufferevent * e, void * userData ) +{ + tr_peerIo * c = (tr_peerIo *) userData; + if( c->didWrite != NULL ) + (*c->didWrite)( e, c->userData ); +} + +static void +canReadWrapper( struct bufferevent * e, void * userData ) +{ + tr_peerIo * c = (tr_peerIo *) userData; + + if( c->canRead == NULL ) + return; + + for( ;; ) { + const int ret = (*c->canRead)( e, c->userData ); + switch( ret ) { + case READ_AGAIN: if( EVBUFFER_LENGTH( e->input ) ) continue; /* note fall-through */ + case READ_MORE: tr_peerIoSetIOMode( c, EV_READ, 0 ); return; break; + case READ_DONE: return; + } + } +} + +static void +gotErrorWrapper( struct bufferevent * e, short what, void * userData ) +{ + tr_peerIo * c = (tr_peerIo *) userData; + if( c->gotError != NULL ) + (*c->gotError)( e, what, c->userData ); +} + +/** +*** +**/ + +//static int total_io = 0; + +static tr_peerIo* +tr_peerIoNew( struct tr_handle * handle, + struct in_addr * in_addr, + const uint8_t * torrentHash, + int isIncoming, + int socket ) +{ + tr_peerIo * c; + c = tr_new0( tr_peerIo, 1 ); + c->crypto = tr_cryptoNew( torrentHash, isIncoming ); + c->handle = handle; + c->in_addr = *in_addr; + c->socket = socket; + c->rateToPeer = tr_rcInit( ); + c->rateToClient = tr_rcInit( ); + c->isIncoming = isIncoming ? 1 : 0; + c->bufev = bufferevent_new( c->socket, + canReadWrapper, + didWriteWrapper, + gotErrorWrapper, + c ); + bufferevent_enable( c->bufev, EV_READ|EV_WRITE ); + return c; +} + +tr_peerIo* +tr_peerIoNewIncoming( struct tr_handle * handle, + struct in_addr * in_addr, + int socket ) +{ + tr_peerIo * c; + + assert( handle != NULL ); + assert( in_addr != NULL ); + assert( socket >= 0 ); + + c = tr_peerIoNew( handle, in_addr, NULL, 1, socket ); + c->port = -1; + return c; +} + +tr_peerIo* +tr_peerIoNewOutgoing( struct tr_handle * handle, + struct in_addr * in_addr, + int port, + const uint8_t * torrentHash ) +{ + tr_peerIo * c; + + assert( handle != NULL ); + assert( in_addr != NULL ); + assert( port >= 0 ); + assert( torrentHash != NULL ); + + c = tr_peerIoNew( handle, in_addr, torrentHash, 0, + tr_netOpenTCP( in_addr, port, 0 ) ); + c->port = port; + return c; +} + +void +tr_peerIoFree( tr_peerIo * c ) +{ + if( c != NULL ) + { + c->canRead = NULL; + c->didWrite = NULL; + c->gotError = NULL; + tr_bufferevent_free( c->handle, c->bufev ); + tr_netClose( c->socket ); + + tr_rcClose( c->rateToClient ); + tr_rcClose( c->rateToPeer ); + + tr_cryptoFree( c->crypto ); + + tr_free( c ); + } +} + +tr_handle* +tr_peerIoGetHandle( tr_peerIo * io ) +{ + assert( io != NULL ); + assert( io->handle != NULL ); + + return io->handle; +} + +const struct in_addr* +tr_peerIoGetAddress( const tr_peerIo * io, uint16_t * port ) +{ + assert( io != NULL ); + + if( port != NULL ) + *port = io->port; + + return &io->in_addr; +} + +const char* +tr_peerIoGetAddrStr( const tr_peerIo * io ) +{ + static char buf[512]; + assert( io != NULL ); + snprintf( buf, sizeof(buf), "%s:%u", inet_ntoa( io->in_addr ), (unsigned int)io->port ); + return buf; +} + +void +tr_peerIoSetIOFuncs( tr_peerIo * io, + tr_can_read_cb readcb, + tr_did_write_cb writecb, + tr_net_error_cb errcb, + void * userData ) +{ + io->canRead = readcb; + io->didWrite = writecb; + io->gotError = errcb; + io->userData = userData; + + if( EVBUFFER_LENGTH( io->bufev->input ) ) + canReadWrapper( io->bufev, io ); +} + +void +tr_peerIoSetIOMode( tr_peerIo * c, short enable, short disable ) +{ + tr_setBufferEventMode( c->handle, c->bufev, enable, disable ); +} + +int +tr_peerIoIsIncoming( const tr_peerIo * c ) +{ + return c->isIncoming ? 1 : 0; +} + +int +tr_peerIoReconnect( tr_peerIo * io ) +{ + assert( !tr_peerIoIsIncoming( io ) ); + + if( io->socket >= 0 ) + tr_netClose( io->socket ); + + io->socket = tr_netOpenTCP( &io->in_addr, io->port, 0 ); + + if( io->socket >= 0 ) + { + bufferevent_free( io->bufev ); + + io->bufev = bufferevent_new( io->socket, + canReadWrapper, + didWriteWrapper, + gotErrorWrapper, + io ); + bufferevent_enable( io->bufev, EV_READ|EV_WRITE ); + + return 0; + } + + return -1; +} + +/** +*** +**/ + +void +tr_peerIoSetTorrentHash( tr_peerIo * io, + const uint8_t * hash ) +{ + assert( io != NULL ); + + tr_cryptoSetTorrentHash( io->crypto, hash ); +} + +const uint8_t* +tr_peerIoGetTorrentHash( tr_peerIo * io ) +{ + assert( io != NULL ); + assert( io->crypto != NULL ); + + return tr_cryptoGetTorrentHash( io->crypto ); +} + +int +tr_peerIoHasTorrentHash( const tr_peerIo * io ) +{ + assert( io != NULL ); + assert( io->crypto != NULL ); + + return tr_cryptoHasTorrentHash( io->crypto ); +} + +/** +*** +**/ + +void +tr_peerIoSetPeersId( tr_peerIo * io, + const uint8_t * peer_id ) +{ + assert( io != NULL ); + + if(( io->peerIdIsSet = peer_id != NULL )) + memcpy( io->peerId, peer_id, 20 ); + else + memset( io->peerId, 0, 20 ); +} + +const uint8_t* +tr_peerIoGetPeersId( const tr_peerIo * io ) +{ + assert( io != NULL ); + assert( io->peerIdIsSet ); + + return io->peerId; +} + +/** +*** +**/ + +void +tr_peerIoSetExtension( tr_peerIo * io, + int extensions ) +{ + assert( io != NULL ); + assert( ( extensions == LT_EXTENSIONS_NONE ) + || ( extensions == LT_EXTENSIONS_LTEP ) + || ( extensions == LT_EXTENSIONS_AZMP ) ); + + io->extensions = extensions; +} + +int +tr_peerIoGetExtension( const tr_peerIo * io ) +{ + assert( io != NULL ); + + return io->extensions; +} + +/** +*** +**/ + +size_t +tr_peerIoWriteBytesWaiting( const tr_peerIo * io ) +{ + return EVBUFFER_LENGTH( EVBUFFER_OUTPUT( io->bufev ) ); +} + +void +tr_peerIoWrite( tr_peerIo * io, + const void * writeme, + int writeme_len ) +{ + tr_bufferevent_write( io->handle, io->bufev, writeme, writeme_len ); + tr_rcTransferred( io->rateToPeer, writeme_len ); +} + +void +tr_peerIoWriteBuf( tr_peerIo * io, + struct evbuffer * buf ) +{ + tr_peerIoWrite( io, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) ); + evbuffer_drain( buf, ~0 ); +} + +/** +*** +**/ + +tr_crypto* +tr_peerIoGetCrypto( tr_peerIo * c ) +{ + return c->crypto; +} + +void +tr_peerIoSetEncryption( tr_peerIo * io, + int encryptionMode ) +{ + assert( io != NULL ); + assert( encryptionMode==PEER_ENCRYPTION_NONE || encryptionMode==PEER_ENCRYPTION_RC4 ); + + io->encryptionMode = encryptionMode; +} + +int +tr_peerIoIsEncrypted( const tr_peerIo * io ) +{ + return io!=NULL && io->encryptionMode==PEER_ENCRYPTION_RC4; +} + +void +tr_peerIoWriteBytes( tr_peerIo * io, + struct evbuffer * outbuf, + const void * bytes, + int byteCount ) +{ + uint8_t * tmp; + + switch( io->encryptionMode ) + { + case PEER_ENCRYPTION_NONE: + /*fprintf( stderr, "writing %d plaintext bytes to outbuf...\n", byteCount );*/ + evbuffer_add( outbuf, bytes, byteCount ); + break; + + case PEER_ENCRYPTION_RC4: + /*fprintf( stderr, "encrypting and writing %d bytes to outbuf...\n", byteCount );*/ + tmp = tr_new( uint8_t, byteCount ); + tr_cryptoEncrypt( io->crypto, byteCount, bytes, tmp ); + tr_bufferevent_write( io->handle, io->bufev, tmp, byteCount ); + tr_free( tmp ); + break; + + default: + assert( 0 ); + } +} + +void +tr_peerIoWriteUint16( tr_peerIo * io, + struct evbuffer * outbuf, + uint16_t writeme ) +{ + uint16_t tmp = htons( writeme ); + tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof(uint16_t) ); +} + +void +tr_peerIoWriteUint32( tr_peerIo * io, + struct evbuffer * outbuf, + uint32_t writeme ) +{ + uint32_t tmp = htonl( writeme ); + tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof(uint32_t) ); +} + +void +tr_peerIoReadBytes( tr_peerIo * io, + struct evbuffer * inbuf, + void * bytes, + int byteCount ) +{ + assert( (int)EVBUFFER_LENGTH( inbuf ) >= byteCount ); + + switch( io->encryptionMode ) + { + case PEER_ENCRYPTION_NONE: + /*fprintf( stderr, "reading %d plaintext bytes from inbuf...\n", byteCount );*/ + evbuffer_remove( inbuf, bytes, byteCount ); + tr_rcTransferred( io->rateToClient, byteCount ); + break; + + case PEER_ENCRYPTION_RC4: + /*fprintf( stderr, "reading AND DECRYPTING %d bytes from inbuf...\n", byteCount );*/ + evbuffer_remove( inbuf, bytes, byteCount ); + tr_cryptoDecrypt( io->crypto, byteCount, bytes, bytes ); + tr_rcTransferred( io->rateToClient, byteCount ); + break; + + default: + assert( 0 ); + } +} + +void +tr_peerIoReadUint16( tr_peerIo * io, + struct evbuffer * inbuf, + uint16_t * setme ) +{ + uint16_t tmp; + tr_peerIoReadBytes( io, inbuf, &tmp, sizeof(uint16_t) ); + *setme = ntohs( tmp ); +} + +void +tr_peerIoReadUint32( tr_peerIo * io, + struct evbuffer * inbuf, + uint32_t * setme ) +{ + uint32_t tmp; + tr_peerIoReadBytes( io, inbuf, &tmp, sizeof(uint32_t) ); + *setme = ntohl( tmp ); +} + +void +tr_peerIoDrain( tr_peerIo * io, + struct evbuffer * inbuf, + int byteCount ) +{ + uint8_t * tmp = tr_new( uint8_t, byteCount ); + tr_peerIoReadBytes( io, inbuf, tmp, byteCount ); + tr_free( tmp ); +} + +/** +*** +**/ + +float +tr_peerIoGetRateToClient( const tr_peerIo * io ) +{ + return io==NULL ? 0.0f : tr_rcRate( io->rateToClient ); + +} + +float +tr_peerIoGetRateToPeer( const tr_peerIo * io ) +{ + return io==NULL ? 0.0f : tr_rcRate( io->rateToPeer ); +} + diff --git a/libtransmission/peer-io.h b/libtransmission/peer-io.h new file mode 100644 index 000000000..c2e664d42 --- /dev/null +++ b/libtransmission/peer-io.h @@ -0,0 +1,178 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_PEER_IO_H +#define TR_PEER_IO_H + +/** +*** +**/ + +struct in_addr; +struct evbuffer; +struct bufferevent; +struct tr_handle; +struct tr_crypto; +typedef struct tr_peerIo tr_peerIo; + + +/** +*** +**/ + +tr_peerIo* + tr_peerIoNewOutgoing( struct tr_handle * handle, + struct in_addr * addr, + int port, + const uint8_t * torrentHash ); + +tr_peerIo* + tr_peerIoNewIncoming( struct tr_handle * handle, + struct in_addr * addr, + int socket ); + +void tr_peerIoFree ( tr_peerIo * io ); + +tr_handle* tr_peerIoGetHandle( tr_peerIo * io ); + +/** +*** +**/ + +enum +{ + LT_EXTENSIONS_NONE, + LT_EXTENSIONS_LTEP, + LT_EXTENSIONS_AZMP +}; + +void tr_peerIoSetExtension( tr_peerIo * io, + int lt_extensions ); + +int tr_peerIoGetExtension( const tr_peerIo * io ); + +/** +*** +**/ + +const char* + tr_peerIoGetAddrStr( const tr_peerIo * io ); + +const struct in_addr* + tr_peerIoGetAddress( const tr_peerIo * io, uint16_t * port ); + +const uint8_t* + tr_peerIoGetTorrentHash( tr_peerIo * io ); + +int tr_peerIoHasTorrentHash( const tr_peerIo * io ); + +void tr_peerIoSetTorrentHash( tr_peerIo * io, + const uint8_t * hash ); + +int tr_peerIoReconnect( tr_peerIo * io ); + +int tr_peerIoIsIncoming( const tr_peerIo * io ); + +/** +*** +**/ + +void tr_peerIoSetPeersId( tr_peerIo * io, + const uint8_t * peer_id ); + +const uint8_t* + tr_peerIoGetPeersId( const tr_peerIo * io ); + +/** +*** +**/ + +typedef enum { READ_MORE, READ_AGAIN, READ_DONE } ReadState; +typedef ReadState (*tr_can_read_cb)(struct bufferevent*, void* user_data); +typedef void (*tr_did_write_cb)(struct bufferevent *, void *); +typedef void (*tr_net_error_cb)(struct bufferevent *, short what, void *); + +void tr_peerIoSetIOFuncs( tr_peerIo * io, + tr_can_read_cb readcb, + tr_did_write_cb writecb, + tr_net_error_cb errcb, + void * user_data ); + +void tr_peerIoSetIOMode ( tr_peerIo * io, + short enable_mode, + short disable_mode ); + +size_t tr_peerIoWriteBytesWaiting( const tr_peerIo * io ); + +void tr_peerIoWrite( tr_peerIo * io, + const void * writeme, + int writeme_len ); + +void tr_peerIoWriteBuf( tr_peerIo * io, + struct evbuffer * buf ); + + +float tr_peerIoGetRateToClient( const tr_peerIo * io ); + +float tr_peerIoGetRateToPeer( const tr_peerIo * io ); + +/** +*** +**/ + +struct tr_crypto* tr_peerIoGetCrypto( tr_peerIo * io ); + +typedef enum +{ + /* these match the values in MSE's crypto_select */ + PEER_ENCRYPTION_NONE = (1<<0), + PEER_ENCRYPTION_RC4 = (1<<1) +} +EncryptionMode; + +void tr_peerIoSetEncryption( tr_peerIo * io, + int encryptionMode ); + +int tr_peerIoIsEncrypted( const tr_peerIo * io ); + +void tr_peerIoWriteBytes ( tr_peerIo * io, + struct evbuffer * outbuf, + const void * bytes, + int byteCount ); + +void tr_peerIoWriteUint16 ( tr_peerIo * io, + struct evbuffer * outbuf, + uint16_t writeme ); + +void tr_peerIoWriteUint32 ( tr_peerIo * io, + struct evbuffer * outbuf, + uint32_t writeme ); + +void tr_peerIoReadBytes ( tr_peerIo * io, + struct evbuffer * inbuf, + void * bytes, + int byteCount ); + +void tr_peerIoReadUint16 ( tr_peerIo * io, + struct evbuffer * inbuf, + uint16_t * setme ); + +void tr_peerIoReadUint32 ( tr_peerIo * io, + struct evbuffer * inbuf, + uint32_t * setme ); + +void tr_peerIoDrain ( tr_peerIo * io, + struct evbuffer * inbuf, + int byteCount ); + + +#endif diff --git a/libtransmission/peer-mgr-private.h b/libtransmission/peer-mgr-private.h new file mode 100644 index 000000000..f7bb0bfda --- /dev/null +++ b/libtransmission/peer-mgr-private.h @@ -0,0 +1,52 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_PEER_MGR_PRIVATE_H +#define TR_PEER_MGR_PRIVATE_H + +#include /* uint16_t */ +#include /* struct in_addr */ +#include "publish.h" /* tr_publisher_tag */ + +struct tr_bitfield; +struct tr_peerIo; +struct tr_peermsgs; + +typedef struct tr_peer +{ + unsigned int peerIsChoked : 1; + unsigned int peerIsInterested : 1; + unsigned int clientIsChoked : 1; + unsigned int clientIsInterested : 1; + unsigned int peerSupportsEncryption : 1; + + struct in_addr in_addr; + uint16_t port; + struct tr_peerIo * io; + uint8_t from; + + struct tr_bitfield * banned; + struct tr_bitfield * blame; + struct tr_bitfield * have; + float progress; + + /* the client name from the `v' string in LTEP's handshake dictionary */ + char * client; + + time_t peerSentDataAt; + + struct tr_peermsgs * msgs; + tr_publisher_tag msgsTag; +} +tr_peer; + +#endif diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c new file mode 100644 index 000000000..755cf107c --- /dev/null +++ b/libtransmission/peer-mgr.c @@ -0,0 +1,1064 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#include +#include /* memcpy, memcmp */ +#include /* qsort */ +#include /* printf */ +#include /* INT_MAX */ + +#include "transmission.h" +#include "clients.h" +#include "completion.h" +#include "handshake.h" +#include "net.h" +#include "peer-io.h" +#include "peer-mgr.h" +#include "peer-mgr-private.h" +#include "peer-msgs.h" +#include "ptrarray.h" +#include "trevent.h" +#include "utils.h" + +/* how frequently to change which peers are choked */ +#define RECHOKE_PERIOD_SECONDS (15 * 1000) + +#define REFILL_PERIOD_MSEC 1000 + +/* how many peers to unchoke per-torrent. */ +/* FIXME: make this user-configurable? */ +#define NUM_UNCHOKED_PEERS_PER_TORRENT 8 + +/** +*** +**/ + +typedef struct +{ + uint8_t hash[SHA_DIGEST_LENGTH]; + tr_ptrArray * peers; /* tr_peer */ + tr_timer * chokeTimer; + tr_timer * refillTimer; + tr_torrent * tor; + tr_bitfield * requested; + + unsigned int isRunning : 1; + + struct tr_peerMgr * manager; +} +Torrent; + +struct tr_peerMgr +{ + tr_handle * handle; + tr_ptrArray * torrents; /* Torrent */ + int connectionCount; + tr_ptrArray * handshakes; /* in-process */ +}; + +/** +*** +**/ + +static int +handshakeCompareToAddr( const void * va, const void * vb ) +{ + const tr_handshake * a = va; + const struct in_addr * b = vb; + return memcmp( tr_handshakeGetAddr( a, NULL ), b, sizeof( struct in_addr ) ); +} + +static int +handshakeCompare( const void * va, const void * vb ) +{ + const tr_handshake * a = va; + const tr_handshake * b = vb; + return memcmp( tr_handshakeGetAddr( a, NULL ), + tr_handshakeGetAddr( b, NULL ), + sizeof( struct in_addr ) ); +} + +static tr_handshake* +getExistingHandshake( tr_peerMgr * mgr, const struct in_addr * in_addr ) +{ + return tr_ptrArrayFindSorted( mgr->handshakes, + in_addr, + handshakeCompareToAddr ); +} + +/** +*** +**/ + +static int +torrentCompare( const void * va, const void * vb ) +{ + const Torrent * a = va; + const Torrent * b = vb; + return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH ); +} + +static int +torrentCompareToHash( const void * va, const void * vb ) +{ + const Torrent * a = (const Torrent*) va; + const uint8_t * b_hash = (const uint8_t*) vb; + return memcmp( a->hash, b_hash, SHA_DIGEST_LENGTH ); +} + +static Torrent* +getExistingTorrent( tr_peerMgr * manager, const uint8_t * hash ) +{ + return (Torrent*) tr_ptrArrayFindSorted( manager->torrents, + hash, + torrentCompareToHash ); +} + +static int chokePulse( void * vtorrent ); + +static int +peerCompare( const void * va, const void * vb ) +{ + const tr_peer * a = (const tr_peer *) va; + const tr_peer * b = (const tr_peer *) vb; + return memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) ); +} + +static int +peerCompareToAddr( const void * va, const void * vb ) +{ + const tr_peer * a = (const tr_peer *) va; + const struct in_addr * b = (const struct in_addr *) vb; + return memcmp( &a->in_addr, b, sizeof(struct in_addr) ); +} + +static tr_peer* +getExistingPeer( Torrent * torrent, const struct in_addr * in_addr ) +{ + assert( torrent != NULL ); + assert( torrent->peers != NULL ); + assert( in_addr != NULL ); + + return (tr_peer*) tr_ptrArrayFindSorted( torrent->peers, + in_addr, + peerCompareToAddr ); +} + +static tr_peer* +getPeer( Torrent * torrent, const struct in_addr * in_addr, int * isNew ) +{ + tr_peer * peer = getExistingPeer( torrent, in_addr ); + + if( isNew ) + *isNew = peer == NULL; + + if( peer == NULL ) + { + peer = tr_new0( tr_peer, 1 ); + memcpy( &peer->in_addr, in_addr, sizeof(struct in_addr) ); + tr_ptrArrayInsertSorted( torrent->peers, peer, peerCompare ); +fprintf( stderr, "getPeer: torrent %p now has %d peers\n", torrent, tr_ptrArraySize(torrent->peers) ); + } + + return peer; +} + +static void +disconnectPeer( tr_peer * peer ) +{ + assert( peer != NULL ); + + tr_peerIoFree( peer->io ); + peer->io = NULL; + + if( peer->msgs != NULL ) + { +fprintf( stderr, "PUB unsub peer %p from msgs %p\n", peer, peer->msgs ); + tr_peerMsgsUnsubscribe( peer->msgs, peer->msgsTag ); + tr_peerMsgsFree( peer->msgs ); + peer->msgs = NULL; + } + + tr_bitfieldFree( peer->have ); + peer->have = NULL; + + tr_bitfieldFree( peer->blame ); + peer->blame = NULL; + + tr_bitfieldFree( peer->banned ); + peer->banned = NULL; +} + +static void +freePeer( tr_peer * peer ) +{ + disconnectPeer( peer ); + tr_free( peer->client ); + tr_free( peer ); +} + +static void +freeTorrent( tr_peerMgr * manager, Torrent * t ) +{ + int i, size; + tr_peer ** peers; + uint8_t hash[SHA_DIGEST_LENGTH]; + +fprintf( stderr, "timer freeTorrent %p\n", t ); + + assert( manager != NULL ); + assert( t != NULL ); + assert( t->peers != NULL ); + assert( getExistingTorrent( manager, t->hash ) != NULL ); + + memcpy( hash, t->hash, SHA_DIGEST_LENGTH ); + + tr_timerFree( &t->chokeTimer ); + tr_timerFree( &t->refillTimer ); + + peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size ); + for( i=0; irequested ); + tr_ptrArrayFree( t->peers ); + tr_ptrArrayRemoveSorted( manager->torrents, t, torrentCompare ); + tr_free( t ); + + assert( getExistingTorrent( manager, hash ) == NULL ); +} + +/** +*** +**/ + +tr_peerMgr* +tr_peerMgrNew( tr_handle * handle ) +{ + tr_peerMgr * m = tr_new0( tr_peerMgr, 1 ); + m->handle = handle; + m->torrents = tr_ptrArrayNew( ); + m->handshakes = tr_ptrArrayNew( ); + return m; +} + +void +tr_peerMgrFree( tr_peerMgr * manager ) +{ +fprintf( stderr, "timer peerMgrFree\n" ); + + while( !tr_ptrArrayEmpty( manager->handshakes ) ) + tr_handshakeAbort( (tr_handshake*)tr_ptrArrayNth( manager->handshakes, 0) ); + tr_ptrArrayFree( manager->handshakes ); + + while( !tr_ptrArrayEmpty( manager->torrents ) ) + freeTorrent( manager, (Torrent*)tr_ptrArrayNth( manager->torrents, 0) ); + tr_ptrArrayFree( manager->torrents ); + + tr_free( manager ); +} + +static tr_peer** +getConnectedPeers( Torrent * t, int * setmeCount ) +{ + int i, peerCount, connectionCount; + tr_peer **peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount ); + tr_peer **ret = tr_new( tr_peer*, peerCount ); + + for( i=connectionCount=0; imsgs != NULL ) + ret[connectionCount++] = peers[i]; + + *setmeCount = connectionCount; + return ret; +} + +/*** +**** Refill +***/ + +struct tr_refill_piece +{ + tr_priority_t priority; + uint32_t piece; + uint32_t peerCount; +}; + +static int +compareRefillPiece (const void * aIn, const void * bIn) +{ + const struct tr_refill_piece * a = aIn; + const struct tr_refill_piece * b = bIn; + + /* if one piece has a higher priority, it goes first */ + if (a->priority != b->priority) + return a->priority > b->priority ? -1 : 1; + + /* otherwise if one has fewer peers, it goes first */ + if (a->peerCount != b->peerCount) + return a->peerCount < b->peerCount ? -1 : 1; + + /* otherwise go with the earlier piece */ + return a->piece - b->piece; +} + +static int +isPieceInteresting( const tr_torrent * tor, + int piece ) +{ + if( tor->info.pieces[piece].dnd ) /* we don't want it */ + return 0; + + if( tr_cpPieceIsComplete( tor->completion, piece ) ) /* we already have it */ + return 0; + + return 1; +} + +static uint32_t* +getPreferredPieces( Torrent * t, + uint32_t * pieceCount ) +{ + const tr_torrent * tor = t->tor; + const tr_info * inf = &tor->info; + + int i; + uint32_t poolSize = 0; + uint32_t * pool = tr_new( uint32_t, inf->pieceCount ); + + int peerCount; + tr_peer** peers = getConnectedPeers( t, &peerCount ); + + for( i=0; ipieceCount; ++i ) + if( isPieceInteresting( tor, i ) ) + pool[poolSize++] = i; + + /* sort the pool from most interesting to least... */ + if( poolSize > 1 ) + { + uint32_t j; + struct tr_refill_piece * p = tr_new( struct tr_refill_piece, poolSize ); + + for( j=0; jpiece = piece; + setme->priority = inf->pieces[piece].priority; + setme->peerCount = 0; + + for( k=0; kpeerIsInterested && !peer->clientIsChoked && tr_bitfieldHas( peer->have, piece ) ) + ++setme->peerCount; + } + } + + qsort (p, poolSize, sizeof(struct tr_refill_piece), compareRefillPiece); + + for( j=0; jtor; + + pieces = getPreferredPieces( t, &pieceCount ); +fprintf( stderr, "REFILL refillPulse for {%s} got %d of %d pieces\n", tor->info.name, (int)pieceCount, t->tor->info.pieceCount ); + + req = tr_new( uint64_t, pieceCount * tor->blockCountInPiece ); + reqCount = 0; + unreq = tr_new( uint64_t, pieceCount * tor->blockCountInPiece ); + unreqCount = 0; + + for( i=0; icompletion, block ) ) + continue; + else if( tr_bitfieldHas( t->requested, block ) ) + req[reqCount++] = block; + else + unreq[unreqCount++] = block; + } + +fprintf( stderr, "REFILL refillPulse for {%s} reqCount is %d, unreqCount is %d\n", tor->info.name, (int)reqCount, (int)unreqCount ); + ret = walk = tr_new( uint64_t, unreqCount + reqCount ); + memcpy( walk, unreq, sizeof(uint64_t) * unreqCount ); + walk += unreqCount; + memcpy( walk, req, sizeof(uint64_t) * reqCount ); + walk += reqCount; + assert( ( walk - ret ) == ( unreqCount + reqCount ) ); + *setmeCount = walk - ret; + + tr_free( req ); + tr_free( unreq ); + tr_free( pieces ); + + return ret; +} + +static int +refillPulse( void * vtorrent ) +{ + Torrent * t = vtorrent; + tr_torrent * tor = t->tor; + uint32_t i; + int peerCount; + tr_peer ** peers; + uint64_t blockCount; + uint64_t * blocks; + + if( !t->isRunning ) + return TRUE; + if( tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE ) + return TRUE; + + blocks = getPreferredBlocks( t, &blockCount ); + peers = getConnectedPeers( t, &peerCount ); + +fprintf( stderr, "REFILL refillPulse for {%s} got %d blocks\n", tor->info.name, (int)blockCount ); + + for( i=0; peerCount && iblockSize) - (index * tor->info.pieceSize); + const uint32_t length = tr_torBlockCountBytes( tor, block ); + int j; + assert( _tr_block( tor, index, begin ) == block ); + assert( begin < (uint32_t)tr_torPieceCountBytes( tor, (int)index ) ); + assert( (begin + length) <= (uint32_t)tr_torPieceCountBytes( tor, (int)index ) ); + + + /* find a peer who can ask for this block */ + for( j=0; jmsgs, index, begin, length ); + switch( val ) + { + case TR_ADDREQ_FULL: + case TR_ADDREQ_CLIENT_CHOKED: + memmove( peers+j, peers+j+1, sizeof(tr_peer*)*(--peerCount-j) ); + break; + + case TR_ADDREQ_MISSING: + ++j; + break; + + case TR_ADDREQ_OK: + fprintf( stderr, "REFILL peer %p took the request for block %d\n", peers[j]->msgs, block ); + tr_bitfieldAdd( t->requested, block ); + j = peerCount; + break; + + default: + assert( 0 && "unhandled value" ); + break; + } + } + } + + /* cleanup */ + tr_free( peers ); + tr_free( blocks ); + + t->refillTimer = NULL; + return FALSE; +} + +static void +broadcastClientHave( Torrent * t, uint32_t index ) +{ + int i, size; + tr_peer ** peers = getConnectedPeers( t, &size ); + for( i=0; imsgs, index ); + tr_free( peers ); +} + +static void +broadcastGotBlock( Torrent * t, uint32_t index, uint32_t offset, uint32_t length ) +{ + int i, size; + tr_peer ** peers = getConnectedPeers( t, &size ); + for( i=0; imsgs, index, offset, length ); + tr_free( peers ); +} + +static void +msgsCallbackFunc( void * source UNUSED, void * vevent, void * vt ) +{ + Torrent * t = (Torrent *) vt; + const tr_peermsgs_event * e = (const tr_peermsgs_event *) vevent; + + switch( e->eventType ) + { + case TR_PEERMSG_NEED_REQ: + if( t->refillTimer == NULL ) + t->refillTimer = tr_timerNew( t->manager->handle, + refillPulse, t, + REFILL_PERIOD_MSEC ); + break; + + case TR_PEERMSG_CLIENT_HAVE: + broadcastClientHave( t, e->pieceIndex ); + break; + + case TR_PEERMSG_CLIENT_BLOCK: + broadcastGotBlock( t, e->pieceIndex, e->offset, e->length ); + break; + + case TR_PEERMSG_GOT_ERROR: + /* FIXME */ + break; + + default: + assert(0); + } +} + +static void +myHandshakeDoneCB( tr_handshake * handshake, + tr_peerIo * io, + int isConnected, + const uint8_t * peer_id, + int peerSupportsEncryption, + void * vmanager ) +{ + int ok = isConnected; + uint16_t port; + const struct in_addr * in_addr; + tr_peerMgr * manager = (tr_peerMgr*) vmanager; + const uint8_t * hash = NULL; + Torrent * t; + tr_handshake * ours; + + assert( io != NULL ); + assert( isConnected==0 || isConnected==1 ); + assert( peerSupportsEncryption==0 || peerSupportsEncryption==1 ); + + ours = tr_ptrArrayRemoveSorted( manager->handshakes, + handshake, + handshakeCompare ); + assert( handshake == ours ); + + in_addr = tr_peerIoGetAddress( io, &port ); + + if( !tr_peerIoHasTorrentHash( io ) ) /* incoming connection gone wrong? */ + { + tr_peerIoFree( io ); + --manager->connectionCount; + return; + } + + hash = tr_peerIoGetTorrentHash( io ); + t = getExistingTorrent( manager, hash ); + if( !t || !t->isRunning ) + { + tr_peerIoFree( io ); + --manager->connectionCount; + return; + } + + fprintf( stderr, "peer-mgr: torrent [%s] finished a handshake; isConnected is %d\n", t->tor->info.name, isConnected ); + + /* if we couldn't connect or were snubbed, + * the peer's probably not worth remembering. */ + if( !ok ) { + tr_peer * peer = getExistingPeer( t, in_addr ); + fprintf( stderr, "peer-mgr: torrent [%s] got a bad one, and you know what? fuck them.\n", t->tor->info.name ); + tr_peerIoFree( io ); + --manager->connectionCount; + if( peer ) { + tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare ); + freePeer( peer ); + } + return; + } + + if( 1 ) { + tr_peer * peer = getPeer( t, in_addr, NULL ); + if( peer->msgs != NULL ) { /* we already have this peer */ + tr_peerIoFree( io ); + --manager->connectionCount; + } else { + peer->port = port; + peer->io = io; + peer->msgs = tr_peerMsgsNew( t->tor, peer ); + peer->client = peer_id ? tr_clientForId( peer_id ) : NULL; + peer->peerSupportsEncryption = peerSupportsEncryption ? 1 : 0; + fprintf( stderr, "PUB sub peer %p to msgs %p\n", peer, peer->msgs ); + peer->msgsTag = tr_peerMsgsSubscribe( peer->msgs, msgsCallbackFunc, t ); + } + } +} + +static void +initiateHandshake( tr_peerMgr * manager, tr_peerIo * io ) +{ + tr_handshake * handshake = tr_handshakeNew( io, + manager->handle->encryptionMode, + myHandshakeDoneCB, + manager ); + ++manager->connectionCount; + + tr_ptrArrayInsertSorted( manager->handshakes, handshake, handshakeCompare ); +} + +void +tr_peerMgrAddIncoming( tr_peerMgr * manager, + struct in_addr * addr, + int socket ) +{ + if( getExistingHandshake( manager, addr ) == NULL ) + { + tr_peerIo * io = tr_peerIoNewIncoming( manager->handle, addr, socket ); + initiateHandshake( manager, io ); + } +} + +static void +maybeConnect( tr_peerMgr * manager, Torrent * t, tr_peer * peer ) +{ + tr_peerIo * io; + + assert( manager != NULL ); + assert( t != NULL ); + assert( peer != NULL ); + + if( peer->io != NULL ) { /* already connected */ + fprintf( stderr, "not connecting because we already have an IO for that address\n" ); + return; + } + if( !t->isRunning ) { /* torrent's not running */ + fprintf( stderr, "OUTGOING connection not being made because t [%s] is not running\n", t->tor->info.name ); + return; + } + if( getExistingHandshake( manager, &peer->in_addr ) != NULL ) { /* already have a handshake pending */ + fprintf( stderr, "already have a handshake for that address\n" ); + return; + } + + io = tr_peerIoNewOutgoing( manager->handle, + &peer->in_addr, + peer->port, + t->hash ); + initiateHandshake( manager, io ); +} + +void +tr_peerMgrAddPex( tr_peerMgr * manager, + const uint8_t * torrentHash, + int from, + const tr_pex * pex, + int pexCount ) +{ + Torrent * t = getExistingTorrent( manager, torrentHash ); + const tr_pex * end = pex + pexCount; + while( pex != end ) + { + int isNew; + tr_peer * peer = getPeer( t, &pex->in_addr, &isNew ); + if( isNew ) { + peer->port = pex->port; + peer->from = from; + maybeConnect( manager, t, peer ); + } + ++pex; + } +} + +void +tr_peerMgrAddPeers( tr_peerMgr * manager, + const uint8_t * torrentHash, + int from, + const uint8_t * peerCompact, + int peerCount ) +{ + int i; + const uint8_t * walk = peerCompact; + Torrent * t = getExistingTorrent( manager, torrentHash ); + for( i=0; t!=NULL && iport = port; + peer->from = from; + maybeConnect( manager, t, peer ); + } + } +} + +/** +*** +**/ + +int +tr_peerMgrIsAcceptingConnections( const tr_peerMgr * manager UNUSED ) +{ + return TRUE; /* manager->connectionCount < MAX_CONNECTED_PEERS; */ +} + +void +tr_peerMgrSetBlame( tr_peerMgr * manager UNUSED, + const uint8_t * torrentHash UNUSED, + int pieceIndex UNUSED, + int success UNUSED ) +{ + fprintf( stderr, "FIXME: tr_peerMgrSetBlame\n" ); +} + +int +tr_pexCompare( const void * va, const void * vb ) +{ + const tr_pex * a = (const tr_pex *) va; + const tr_pex * b = (const tr_pex *) vb; + int i = memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) ); + if( i ) return i; + if( a->port < b->port ) return -1; + if( a->port > b->port ) return 1; + return 0; +} + +int tr_pexCompare( const void * a, const void * b ); + + +int +tr_peerMgrGetPeers( tr_peerMgr * manager, + const uint8_t * torrentHash, + tr_pex ** setme_pex ) +{ + const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash ); + int i, peerCount; + const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount ); + tr_pex * pex = tr_new( tr_pex, peerCount ); + tr_pex * walk = pex; + + for( i=0; iin_addr = peer->in_addr; + + walk->port = peer->port; + + walk->flags = 0; + if( peer->peerSupportsEncryption ) walk->flags |= 1; + if( peer->progress >= 1.0 ) walk->flags |= 2; + } + + assert( ( walk - pex ) == peerCount ); + qsort( pex, peerCount, sizeof(tr_pex), tr_pexCompare ); + *setme_pex = pex; + return peerCount; +} + +void +tr_peerMgrStartTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash ) +{ + int i, peerCount; + Torrent * t = getExistingTorrent( manager, torrentHash ); + + t->isRunning = 1; + + peerCount = tr_ptrArraySize( t->peers ); + for( i=0; ipeers, i ) ); +} + +void +tr_peerMgrStopTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash) +{ + int i, peerCount; + Torrent * t = getExistingTorrent( manager, torrentHash ); + + t->isRunning = 0; + + peerCount = tr_ptrArraySize( t->peers ); + for( i=0; ipeers, i ) ); +} + +void +tr_peerMgrAddTorrent( tr_peerMgr * manager, + tr_torrent * tor ) +{ + Torrent * t; + + assert( tor != NULL ); + assert( getExistingTorrent( manager, tor->info.hash ) == NULL ); + + t = tr_new0( Torrent, 1 ); + t->manager = manager; + t->tor = tor; + t->peers = tr_ptrArrayNew( ); + t->requested = tr_bitfieldNew( tor->blockCount ); + t->chokeTimer = tr_timerNew( manager->handle, chokePulse, t, RECHOKE_PERIOD_SECONDS ); + + memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH ); + tr_ptrArrayInsertSorted( manager->torrents, t, torrentCompare ); +} + +void +tr_peerMgrRemoveTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash ) +{ + Torrent * t = getExistingTorrent( manager, torrentHash ); + assert( t != NULL ); + tr_peerMgrStopTorrent( manager, torrentHash ); + freeTorrent( manager, t ); +} + +void +tr_peerMgrTorrentAvailability( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int8_t * tab, + int tabCount ) +{ + int i; + const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash ); + const tr_torrent * tor = t->tor; + const float interval = tor->info.pieceCount / (float)tabCount; + + memset( tab, 0, tabCount ); + + for( i=0; icompletion, piece ) ) + tab[i] = -1; + else { + int j, peerCount; + const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount ); + for( j=0; jhave, i ) ) + ++tab[i]; + } + } +} + + +void +tr_peerMgrTorrentStats( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int * setmePeersTotal, + int * setmePeersConnected, + int * setmePeersSendingToUs, + int * setmePeersGettingFromUs, + int * setmePeersFrom ) +{ + int i, size; + const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash ); + const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size ); + + *setmePeersTotal = size; + *setmePeersConnected = 0; + *setmePeersSendingToUs = 0; + *setmePeersGettingFromUs = 0; + + for( i=0; iio == NULL ) /* not connected */ + continue; + + ++*setmePeersConnected; + + ++setmePeersFrom[peer->from]; + + if( tr_peerIoGetRateToPeer( peer->io ) > 0.01 ) + ++*setmePeersGettingFromUs; + + if( tr_peerIoGetRateToClient( peer->io ) > 0.01 ) + ++*setmePeersSendingToUs; + } +} + +struct tr_peer_stat * +tr_peerMgrPeerStats( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int * setmeCount UNUSED ) +{ + int i, size; + const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash ); + const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size ); + tr_peer_stat * ret; + + ret = tr_new0( tr_peer_stat, size ); + + for( i=0; iio != NULL; + tr_peer_stat * stat = ret + i; + + tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) ); + stat->port = peer->port; + stat->from = peer->from; + stat->client = peer->client; + stat->progress = peer->progress; + stat->isConnected = live ? 1 : 0; + stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0; + stat->uploadToRate = tr_peerIoGetRateToPeer( peer->io ); + stat->downloadFromRate = tr_peerIoGetRateToClient( peer->io ); + stat->isDownloading = stat->uploadToRate > 0.01; + stat->isUploading = stat->downloadFromRate > 0.01; + } + + *setmeCount = size; + return ret; +} + +/** +*** +**/ + +typedef struct +{ + tr_peer * peer; + float rate; + int randomKey; + int preferred; + int doUnchoke; +} +ChokeData; + +static int +compareChoke( const void * va, const void * vb ) +{ + const ChokeData * a = ( const ChokeData * ) va; + const ChokeData * b = ( const ChokeData * ) vb; + + if( a->preferred != b->preferred ) + return a->preferred ? -1 : 1; + + if( a->preferred ) + { + if( a->rate > b->rate ) return -1; + if( a->rate < b->rate ) return 1; + return 0; + } + else + { + return a->randomKey - b->randomKey; + } +} + +static int +clientIsSnubbedBy( const tr_peer * peer ) +{ + assert( peer != NULL ); + + return peer->peerSentDataAt < (time(NULL) - 30); +} + +static void +rechokeLeech( Torrent * t ) +{ + int i, size, unchoked=0; + tr_peer ** peers = getConnectedPeers( t, &size ); + ChokeData * choke = tr_new0( ChokeData, size ); + + /* sort the peers by preference and rate */ + for( i=0; ipeer = peer; + node->preferred = peer->peerIsInterested && !clientIsSnubbedBy(peer); + node->randomKey = tr_rand( INT_MAX ); + node->rate = tr_peerIoGetRateToClient( peer->io ); + } + qsort( choke, size, sizeof(ChokeData), compareChoke ); + + for( i=0; ipeerIsInterested ) + break; + } + + for( i=0; imsgs, !choke[i].doUnchoke ); + + /* cleanup */ + tr_free( choke ); + tr_free( peers ); +} + +static void +rechokeSeed( Torrent * t ) +{ + int i, size; + tr_peer ** peers = getConnectedPeers( t, &size ); + + /* FIXME */ + for( i=0; imsgs, FALSE ); + + tr_free( peers ); +} + +static int +chokePulse( void * vtorrent ) +{ + Torrent * t = vtorrent; + const int done = tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE; + if( done ) + rechokeLeech( vtorrent ); + else + rechokeSeed( vtorrent ); + return TRUE; +} diff --git a/libtransmission/peer-mgr.h b/libtransmission/peer-mgr.h new file mode 100644 index 000000000..657ae4acf --- /dev/null +++ b/libtransmission/peer-mgr.h @@ -0,0 +1,99 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_PEER_MGR_H +#define TR_PEER_MGR_H + +#include /* uint16_t */ +#include /* struct in_addr */ + +struct in_addr; +struct tr_handle; +struct tr_peer_stat; +struct tr_torrent; +typedef struct tr_peerMgr tr_peerMgr; + +typedef struct tr_pex +{ + struct in_addr in_addr; + uint16_t port; + uint8_t flags; +} +tr_pex; + +int tr_pexCompare( const void * a, const void * b ); + +tr_peerMgr* tr_peerMgrNew( struct tr_handle * ); + +void tr_peerMgrFree( tr_peerMgr * manager ); + +int tr_peerMgrIsAcceptingConnections( const tr_peerMgr * manager ); + +void tr_peerMgrAddIncoming( tr_peerMgr * manager, + struct in_addr * addr, + int socket ); + +void tr_peerMgrAddPeers( tr_peerMgr * manager, + const uint8_t * torrentHash, + int from, + const uint8_t * peerCompact, + int peerCount ); + +void tr_peerMgrAddPex( tr_peerMgr * manager, + const uint8_t * torrentHash, + int from, + const tr_pex * pex, + int pexCount ); + +void tr_peerMgrSetBlame( tr_peerMgr * manager, + const uint8_t * torrentHash, + int pieceIndex, + int success ); + +int tr_peerMgrGetPeers( tr_peerMgr * manager, + const uint8_t * torrentHash, + tr_pex ** setme_pex ); + +void tr_peerMgrStartTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash ); + +void tr_peerMgrStopTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash ); + +void tr_peerMgrAddTorrent( tr_peerMgr * manager, + struct tr_torrent * tor ); + +void tr_peerMgrRemoveTorrent( tr_peerMgr * manager, + const uint8_t * torrentHash ); + +void tr_peerMgrTorrentAvailability( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int8_t * tab, + int tabCount ); + +void tr_peerMgrTorrentStats( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int * setmePeersTotal, + int * setmePeersConnected, + int * setmePeersSendingToUs, + int * setmePeersGettingFromUs, + int * setmePeersFrom ); /* <-- array of TR_PEER_FROM__MAX */ + +struct tr_peer_stat * tr_peerMgrPeerStats( const tr_peerMgr * manager, + const uint8_t * torrentHash, + int * setmeCount ); + + + + + +#endif diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c new file mode 100644 index 000000000..cbc51ae24 --- /dev/null +++ b/libtransmission/peer-msgs.c @@ -0,0 +1,1294 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#include +#include +#include +#include +#include + +#include + +#include /* event.h needs this */ +#include + +#include "transmission.h" +#include "bencode.h" +#include "completion.h" +#include "inout.h" +#include "list.h" +#include "peer-io.h" +#include "peer-mgr.h" +#include "peer-mgr-private.h" +#include "peer-msgs.h" +#include "ratecontrol.h" +#include "trevent.h" +#include "utils.h" + +/** +*** +**/ + +#define PEX_INTERVAL (60 * 1000) + +#define PEER_PULSE_INTERVAL (100) + +enum +{ + BT_CHOKE = 0, + BT_UNCHOKE = 1, + BT_INTERESTED = 2, + BT_NOT_INTERESTED = 3, + BT_HAVE = 4, + BT_BITFIELD = 5, + BT_REQUEST = 6, + BT_PIECE = 7, + BT_CANCEL = 8, + BT_PORT = 9, + BT_LTEP = 20, + + LTEP_HANDSHAKE = 0, + + OUR_LTEP_PEX = 1 +}; + +enum +{ + AWAITING_BT_LENGTH, + AWAITING_BT_MESSAGE, + READING_BT_PIECE +}; + +struct peer_request +{ + uint32_t index; + uint32_t offset; + uint32_t length; + time_t time_requested; +}; + +static int +peer_request_compare( const void * va, const void * vb ) +{ + struct peer_request * a = (struct peer_request*) va; + struct peer_request * b = (struct peer_request*) vb; + if( a->index != b->index ) return a->index - b->index; + if( a->offset != b->offset ) return a->offset - b->offset; + if( a->length != b->length ) return a->length - b->length; + return 0; +} + +struct tr_peermsgs +{ + tr_peer * info; + + tr_handle * handle; + tr_torrent * torrent; + tr_peerIo * io; + + tr_publisher_t * publisher; + + struct evbuffer * outMessages; /* buffer of all the non-piece messages */ + struct evbuffer * outBlock; /* the block we're currently sending */ + struct evbuffer * inBlock; /* the block we're currently receiving */ + tr_list * peerAskedFor; + tr_list * clientAskedFor; + + tr_timer * pulseTimer; + tr_timer * pexTimer; + + struct peer_request blockToUs; /* the block currntly being sent to us */ + + time_t lastReqAddedAt; + time_t gotKeepAliveTime; + time_t clientSentPexAt; + + unsigned int notListening : 1; + unsigned int peerSupportsPex : 1; + unsigned int hasSentLtepHandshake : 1; + + uint8_t state; + + uint8_t ut_pex_id; + uint16_t listeningPort; + + uint16_t pexCount; + + uint32_t incomingMessageLength; + + tr_pex * pex; +}; + +/** +*** +**/ + +static void +myDebug( const char * file, int line, const struct tr_peermsgs * msgs, const char * fmt, ... ) +{ + va_list args; + const char * addr = tr_peerIoGetAddrStr( msgs->io ); + struct evbuffer * buf = evbuffer_new( ); + evbuffer_add_printf( buf, "[%s:%d] %s (%p) ", file, line, addr, msgs ); + va_start( args, fmt ); + evbuffer_add_vprintf( buf, fmt, args ); + va_end( args ); + + fprintf( stderr, "%s\n", EVBUFFER_DATA(buf) ); + + evbuffer_free( buf ); +} + +#define dbgmsg(handshake, fmt...) myDebug(__FILE__, __LINE__, handshake, ##fmt ) + +/** +*** EVENTS +**/ + +static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0 }; + +static void +publishEvent( tr_peermsgs * peer, int eventType ) +{ + tr_peermsgs_event e = blankEvent; + e.eventType = eventType; + tr_publisherPublish( peer->publisher, peer, &e ); +} + +static void +fireNeedReq( tr_peermsgs * msgs ) +{ + tr_peermsgs_event e = blankEvent; + e.eventType = TR_PEERMSG_NEED_REQ; + tr_publisherPublish( msgs->publisher, msgs, &e ); +} + +static void +fireClientHave( tr_peermsgs * msgs, uint32_t pieceIndex ) +{ + tr_peermsgs_event e = blankEvent; + e.eventType = TR_PEERMSG_CLIENT_HAVE; + e.pieceIndex = pieceIndex; + tr_publisherPublish( msgs->publisher, msgs, &e ); +} + +static void +fireGotBlock( tr_peermsgs * peer, uint32_t pieceIndex, uint32_t offset, uint32_t length ) +{ + tr_peermsgs_event e = blankEvent; + e.eventType = TR_PEERMSG_CLIENT_BLOCK; + e.pieceIndex = pieceIndex; + e.offset = offset; + e.length = length; + tr_publisherPublish( peer->publisher, peer, &e ); +} + +static void +fireGotError( tr_peermsgs * peer ) +{ + publishEvent( peer, TR_PEERMSG_GOT_ERROR ); +} + +/** +*** INTEREST +**/ + +static int +isPieceInteresting( const tr_peermsgs * peer, + int piece ) +{ + const tr_torrent * torrent = peer->torrent; + if( torrent->info.pieces[piece].dnd ) /* we don't want it */ + return FALSE; + if( tr_cpPieceIsComplete( torrent->completion, piece ) ) /* we already have it */ + return FALSE; + if( !tr_bitfieldHas( peer->info->have, piece ) ) /* peer doesn't have it */ + return FALSE; + if( tr_bitfieldHas( peer->info->banned, piece ) ) /* peer is banned for it */ + return FALSE; + return TRUE; +} + +static int +isPeerInteresting( const tr_peermsgs * msgs ) +{ + const int clientIsSeed = tr_cpGetStatus( msgs->torrent->completion ) != TR_CP_INCOMPLETE; + const int peerIsSeed = msgs->info->progress >= 1.0; + + if( peerIsSeed ) + { + return !clientIsSeed; + } + else if( clientIsSeed ) + { + return !peerIsSeed; + } + else /* we're both leeches... */ + { + int i; + const tr_torrent * torrent = msgs->torrent; + const tr_bitfield * bitfield = tr_cpPieceBitfield( torrent->completion ); + + if( !msgs->info->have ) /* We don't know what this peer has... what should this be? */ + return TRUE; + + assert( bitfield->len == msgs->info->have->len ); + for( i=0; iinfo.pieceCount; ++i ) + if( isPieceInteresting( msgs, i ) ) + return TRUE; + + return FALSE; + } +} + +static void +sendInterest( tr_peermsgs * msgs, int weAreInterested ) +{ + const uint32_t len = sizeof(uint8_t); + const uint8_t bt_msgid = weAreInterested ? BT_INTERESTED : BT_NOT_INTERESTED; + + assert( msgs != NULL ); + assert( weAreInterested==0 || weAreInterested==1 ); + + msgs->info->clientIsInterested = weAreInterested; + dbgmsg( msgs, ": sending an %s message", (weAreInterested ? "INTERESTED" : "NOT_INTERESTED") ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); +} + +static void +updateInterest( tr_peermsgs * msgs ) +{ + const int i = isPeerInteresting( msgs ); + if( i != msgs->info->clientIsInterested ) + sendInterest( msgs, i ); + if( i ) + fireNeedReq( msgs ); +} + +void +tr_peerMsgsSetChoke( tr_peermsgs * msgs, int choke ) +{ + assert( msgs != NULL ); + assert( msgs->info != NULL ); + assert( choke==0 || choke==1 ); + + if( msgs->info->peerIsChoked != choke ) + { + const uint32_t len = sizeof(uint8_t); + const uint8_t bt_msgid = choke ? BT_CHOKE : BT_UNCHOKE; + + msgs->info->peerIsChoked = choke ? 1 : 0; + if( msgs->info ) + { + tr_list_foreach( msgs->peerAskedFor, tr_free ); + tr_list_free( &msgs->peerAskedFor ); + } + + dbgmsg( msgs, "sending a %s message", (choke ? "CHOKE" : "UNCHOKE") ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + } +} + +/** +*** +**/ + +void +tr_peerMsgsCancel( tr_peermsgs * msgs, + uint32_t pieceIndex, + uint32_t offset, + uint32_t length ) +{ + tr_list * node; + struct peer_request tmp; + + assert( msgs != NULL ); + assert( length > 0 ); + + tmp.index = pieceIndex; + tmp.offset = offset; + tmp.length = length; + + node = tr_list_find( msgs->clientAskedFor, &tmp, peer_request_compare ); + if( node != NULL ) + { + /* cancel the request */ + const uint8_t bt_msgid = BT_CANCEL; + const uint32_t len = sizeof(uint8_t) + 3 * sizeof(uint32_t); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, pieceIndex ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, offset ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, length ); + + /* remove it from our "requested" list */ + tr_list_remove_data( &msgs->peerAskedFor, node->data ); + } +} + +/** +*** +**/ + +void +tr_peerMsgsHave( tr_peermsgs * msgs, + uint32_t pieceIndex ) +{ + const uint8_t bt_msgid = BT_HAVE; + const uint32_t len = sizeof(uint8_t) + sizeof(uint32_t); + dbgmsg( msgs, "w00t telling them we HAVE piece #%d", pieceIndex ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, pieceIndex ); + + updateInterest( msgs ); +} + +/** +*** +**/ + +static int +pulse( void * vmsgs ); + +int +tr_peerMsgsAddRequest( tr_peermsgs * msgs, + uint32_t index, + uint32_t offset, + uint32_t length ) +{ + const uint8_t bt_msgid = BT_REQUEST; + const uint32_t len = sizeof(uint8_t) + 3 * sizeof(uint32_t); + struct peer_request * req; + int maxSize; + + assert( msgs != NULL ); + assert( msgs->torrent != NULL ); + assert( index < ((uint32_t)msgs->torrent->info.pieceCount) ); + assert( offset < (uint32_t)tr_torPieceCountBytes( msgs->torrent, (int)index ) ); + assert( (offset + length) <= (uint32_t)tr_torPieceCountBytes( msgs->torrent, (int)index ) ); + + if( msgs->info->clientIsChoked ) + return TR_ADDREQ_CLIENT_CHOKED; + + if( !tr_bitfieldHas( msgs->info->have, index ) ) + return TR_ADDREQ_MISSING; + + maxSize = MIN( 2 + (int)(tr_peerIoGetRateToClient(msgs->io)/10), 100 ); + //if( ( time(NULL) - msgs->lastReqAddedAt <= 5 ) && ( tr_list_size( msgs->clientAskedFor) >= maxSize ) ) + if( tr_list_size( msgs->clientAskedFor) >= maxSize ) + return TR_ADDREQ_FULL; + + dbgmsg( msgs, "w00t peer has a max request queue size of %d... adding request for piece %d, offset %d", maxSize, (int)index, (int)offset ); + + /* queue the request */ + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, index ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, offset ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, length ); + + /* add it to our `requests sent' list */ + req = tr_new( struct peer_request, 1 ); + req->index = index; + req->offset = offset; + req->length = length; + req->time_requested = msgs->lastReqAddedAt = time( NULL ); + tr_list_append( &msgs->clientAskedFor, req ); + pulse( msgs ); + + return TR_ADDREQ_OK; +} + +/** +*** +**/ + +static void +sendLtepHandshake( tr_peermsgs * msgs ) +{ + benc_val_t val, *m; + char * buf; + int len; + const char * v = TR_NAME " " USERAGENT_PREFIX; + const int port = tr_getPublicPort( msgs->handle ); + struct evbuffer * outbuf = evbuffer_new( ); + uint32_t msglen; + const uint8_t tr_msgid = 20; /* ltep extension id */ + const uint8_t ltep_msgid = 0; /* handshake id */ + + dbgmsg( msgs, "sending an ltep handshake" ); + tr_bencInit( &val, TYPE_DICT ); + tr_bencDictReserve( &val, 3 ); + m = tr_bencDictAdd( &val, "m" ); + tr_bencInit( m, TYPE_DICT ); + tr_bencDictReserve( m, 1 ); + tr_bencInitInt( tr_bencDictAdd( m, "ut_pex" ), OUR_LTEP_PEX ); + if( port > 0 ) + tr_bencInitInt( tr_bencDictAdd( &val, "p" ), port ); + tr_bencInitStr( tr_bencDictAdd( &val, "v" ), v, 0, 1 ); + buf = tr_bencSaveMalloc( &val, &len ); + tr_bencPrint( &val ); + + msglen = sizeof(tr_msgid) + sizeof(ltep_msgid) + len; + tr_peerIoWriteUint32( msgs->io, outbuf, msglen ); + tr_peerIoWriteBytes ( msgs->io, outbuf, &tr_msgid, 1 ); + tr_peerIoWriteBytes ( msgs->io, outbuf, <ep_msgid, 1 ); + tr_peerIoWriteBytes ( msgs->io, outbuf, buf, len ); + + tr_peerIoWriteBuf( msgs->io, outbuf ); + + msgs->hasSentLtepHandshake = 1; + + /* cleanup */ + tr_bencFree( &val ); + tr_free( buf ); + evbuffer_free( outbuf ); + +} + +static void +parseLtepHandshake( tr_peermsgs * msgs, int len, struct evbuffer * inbuf ) +{ + benc_val_t val, * sub; + uint8_t * tmp = tr_new( uint8_t, len ); + evbuffer_remove( inbuf, tmp, len ); + + if( tr_bencLoad( tmp, len, &val, NULL ) || val.type!=TYPE_DICT ) { + dbgmsg( msgs, "GET extended-handshake, couldn't get dictionary" ); + tr_free( tmp ); + return; + } + + tr_bencPrint( &val ); + + /* check supported messages for utorrent pex */ + sub = tr_bencDictFind( &val, "m" ); + if( tr_bencIsDict( sub ) ) { + sub = tr_bencDictFind( sub, "ut_pex" ); + if( tr_bencIsInt( sub ) ) { + msgs->peerSupportsPex = 1; + msgs->ut_pex_id = (uint8_t) sub->val.i; + dbgmsg( msgs, "msgs->ut_pex is %d", (int)msgs->ut_pex_id ); + } + } + +#if 0 + /* get peer's client name */ + sub = tr_bencDictFind( &val, "v" ); + if( tr_bencIsStr( sub ) ) { +int i; + tr_free( msgs->info->client ); + fprintf( stderr, "dictionary says client is [%s]\n", sub->val.s.s ); + msgs->info->client = tr_strndup( sub->val.s.s, sub->val.s.i ); +for( i=0; ival.s.i; ++i ) { fprintf( stderr, "[%c] (%d)\n", sub->val.s.s[i], (int)sub->val.s.s[i] ); + if( (int)msgs->info->client[i]==-75 ) msgs->info->client[i]='u'; } + fprintf( stderr, "msgs->client is now [%s]\n", msgs->info->client ); + } +#endif + + /* get peer's listening port */ + sub = tr_bencDictFind( &val, "p" ); + if( tr_bencIsInt( sub ) ) { + msgs->listeningPort = htons( (uint16_t)sub->val.i ); + dbgmsg( msgs, "msgs->port is now %hu", msgs->listeningPort ); + } + + tr_bencFree( &val ); + tr_free( tmp ); +} + +static void +parseUtPex( tr_peermsgs * msgs, int msglen, struct evbuffer * inbuf ) +{ + benc_val_t val, * sub; + uint8_t * tmp; + + if( msgs->torrent->pexDisabled ) /* no sharing! */ + return; + + tmp = tr_new( uint8_t, msglen ); + evbuffer_remove( inbuf, tmp, msglen ); + + if( tr_bencLoad( tmp, msglen, &val, NULL ) || !tr_bencIsDict( &val ) ) { + dbgmsg( msgs, "GET can't read extended-pex dictionary" ); + tr_free( tmp ); + return; + } + + sub = tr_bencDictFind( &val, "added" ); + if( tr_bencIsStr(sub) && ((sub->val.s.i % 6) == 0)) { + const int n = sub->val.s.i / 6 ; + dbgmsg( msgs, "got %d peers from uT pex", n ); + tr_peerMgrAddPeers( msgs->handle->peerMgr, + msgs->torrent->info.hash, + TR_PEER_FROM_PEX, + (uint8_t*)sub->val.s.s, n ); + } + + tr_bencFree( &val ); + tr_free( tmp ); +} + +static void +sendPex( tr_peermsgs * msgs ); + +static void +parseLtep( tr_peermsgs * msgs, int msglen, struct evbuffer * inbuf ) +{ + uint8_t ltep_msgid; + + tr_peerIoReadBytes( msgs->io, inbuf, <ep_msgid, 1 ); + msglen--; + + if( ltep_msgid == LTEP_HANDSHAKE ) + { + dbgmsg( msgs, "got ltep handshake" ); + parseLtepHandshake( msgs, msglen, inbuf ); + if( !msgs->hasSentLtepHandshake ) + sendLtepHandshake( msgs ); + } + else if( ltep_msgid == msgs->ut_pex_id ) + { + dbgmsg( msgs, "got ut pex" ); + msgs->peerSupportsPex = 1; + parseUtPex( msgs, msglen, inbuf ); + } + else + { + dbgmsg( msgs, "skipping unknown ltep message (%d)", (int)ltep_msgid ); + evbuffer_drain( inbuf, msglen ); + } +} + +static int +readBtLength( tr_peermsgs * msgs, struct evbuffer * inbuf ) +{ + uint32_t len; + const size_t needlen = sizeof(uint32_t); + + if( EVBUFFER_LENGTH(inbuf) < needlen ) + return READ_MORE; + + tr_peerIoReadUint32( msgs->io, inbuf, &len ); + + if( len == 0 ) { /* peer sent us a keepalive message */ + dbgmsg( msgs, "peer sent us a keepalive message..." ); + msgs->gotKeepAliveTime = time( NULL ); + } else { + dbgmsg( msgs, "peer is sending us a message with %"PRIu64" bytes...\n", (uint64_t)len ); + msgs->incomingMessageLength = len; + msgs->state = AWAITING_BT_MESSAGE; + } return READ_AGAIN; +} + +static int +readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf ) +{ + uint8_t id; + uint32_t ui32; + uint32_t msglen = msgs->incomingMessageLength; + + if( EVBUFFER_LENGTH(inbuf) < msglen ) + return READ_MORE; + + tr_peerIoReadBytes( msgs->io, inbuf, &id, 1 ); + msglen--; + dbgmsg( msgs, "peer sent us a message... " + "bt id number is %d, and remaining len is %d", (int)id, (int)msglen ); + + switch( id ) + { + case BT_CHOKE: + assert( msglen == 0 ); + dbgmsg( msgs, "w00t peer sent us a BT_CHOKE" ); + msgs->info->clientIsChoked = 1; + tr_list_foreach( msgs->peerAskedFor, tr_free ); + tr_list_free( &msgs->peerAskedFor ); + tr_list_foreach( msgs->clientAskedFor, tr_free ); + tr_list_free( &msgs->clientAskedFor ); + break; + + case BT_UNCHOKE: + assert( msglen == 0 ); + dbgmsg( msgs, "w00t peer sent us a BT_UNCHOKE" ); + msgs->info->clientIsChoked = 0; + fireNeedReq( msgs ); + break; + + case BT_INTERESTED: + assert( msglen == 0 ); + dbgmsg( msgs, "w00t peer sent us a BT_INTERESTED" ); + msgs->info->peerIsInterested = 1; + break; + + case BT_NOT_INTERESTED: + assert( msglen == 0 ); + dbgmsg( msgs, "w00t peer sent us a BT_NOT_INTERESTED" ); + msgs->info->peerIsInterested = 0; + break; + + case BT_HAVE: + assert( msglen == 4 ); + dbgmsg( msgs, "w00t peer sent us a BT_HAVE" ); + tr_peerIoReadUint32( msgs->io, inbuf, &ui32 ); + tr_bitfieldAdd( msgs->info->have, ui32 ); + msgs->info->progress = tr_bitfieldCountTrueBits( msgs->info->have ) / (float)msgs->torrent->info.pieceCount; + dbgmsg( msgs, "after the HAVE message, peer progress is %f", msgs->info->progress ); + updateInterest( msgs ); + break; + + case BT_BITFIELD: + assert( msglen == msgs->info->have->len ); + dbgmsg( msgs, "w00t peer sent us a BT_BITFIELD" ); + tr_peerIoReadBytes( msgs->io, inbuf, msgs->info->have->bits, msglen ); + msgs->info->progress = tr_bitfieldCountTrueBits( msgs->info->have ) / (float)msgs->torrent->info.pieceCount; + dbgmsg( msgs, "after the HAVE message, peer progress is %f", msgs->info->progress ); + updateInterest( msgs ); + fireNeedReq( msgs ); + /* FIXME: maybe unchoke */ + break; + + case BT_REQUEST: { + struct peer_request * req; + assert( msglen == 12 ); + dbgmsg( msgs, "peer sent us a BT_REQUEST" ); + req = tr_new( struct peer_request, 1 ); + tr_peerIoReadUint32( msgs->io, inbuf, &req->index ); + tr_peerIoReadUint32( msgs->io, inbuf, &req->offset ); + tr_peerIoReadUint32( msgs->io, inbuf, &req->length ); + if( !msgs->info->peerIsChoked ) + tr_list_append( &msgs->peerAskedFor, req ); + break; + } + + case BT_CANCEL: { + struct peer_request req; + tr_list * node; + assert( msglen == 12 ); + dbgmsg( msgs, "peer sent us a BT_CANCEL" ); + tr_peerIoReadUint32( msgs->io, inbuf, &req.index ); + tr_peerIoReadUint32( msgs->io, inbuf, &req.offset ); + tr_peerIoReadUint32( msgs->io, inbuf, &req.length ); + node = tr_list_find( msgs->peerAskedFor, &req, peer_request_compare ); + if( node != NULL ) { + void * data = node->data; + tr_list_remove_data( &msgs->peerAskedFor, data ); + tr_free( data ); + dbgmsg( msgs, "found the req that peer is cancelling... cancelled." ); + } + break; + } + + case BT_PIECE: { + dbgmsg( msgs, "peer sent us a BT_PIECE" ); + assert( msgs->blockToUs.length == 0 ); + msgs->state = READING_BT_PIECE; + tr_peerIoReadUint32( msgs->io, inbuf, &msgs->blockToUs.index ); + tr_peerIoReadUint32( msgs->io, inbuf, &msgs->blockToUs.offset ); + msgs->blockToUs.length = msglen - 8; + assert( msgs->blockToUs.length > 0 ); + assert( EVBUFFER_LENGTH(msgs->inBlock) == 0 ); + //evbuffer_drain( msgs->inBlock, ~0 ); + return READ_AGAIN; + break; + } + + case BT_PORT: { + assert( msglen == 2 ); + dbgmsg( msgs, "peer sent us a BT_PORT" ); + tr_peerIoReadUint16( msgs->io, inbuf, &msgs->listeningPort ); + break; + } + + case BT_LTEP: + dbgmsg( msgs, "peer sent us a BT_LTEP" ); + parseLtep( msgs, msglen, inbuf ); + break; + + default: + dbgmsg( msgs, "peer sent us an UNKNOWN: %d", (int)id ); + tr_peerIoDrain( msgs->io, inbuf, msglen ); + assert( 0 ); + } + + msgs->incomingMessageLength = -1; + msgs->state = AWAITING_BT_LENGTH; + return READ_AGAIN; +} + +static void +clientGotBytes( tr_peermsgs * msgs, uint32_t byteCount ) +{ + tr_torrent * tor = msgs->torrent; + tor->downloadedCur += byteCount; + tr_rcTransferred( tor->download, byteCount ); + tr_rcTransferred( tor->handle->download, byteCount ); +} + +static void +peerGotBytes( tr_peermsgs * msgs, uint32_t byteCount ) +{ + tr_torrent * tor = msgs->torrent; + tor->uploadedCur += byteCount; + tr_rcTransferred( tor->upload, byteCount ); + tr_rcTransferred( tor->handle->upload, byteCount ); +} + +static int +canDownload( const tr_peermsgs * msgs UNUSED ) +{ +#if 0 + tr_torrent * tor = msgs->torrent; + + if( tor->downloadLimitMode == TR_SPEEDLIMIT_GLOBAL ) + return !tor->handle->useDownloadLimit || tr_rcCanTransfer( tor->handle->download ); + + if( tor->downloadLimitMode == TR_SPEEDLIMIT_SINGLE ) + return tr_rcCanTransfer( tor->download ); +#endif + + return TRUE; +} + +static void +reassignBytesToCorrupt( tr_peermsgs * msgs, uint32_t byteCount ) +{ + tr_torrent * tor = msgs->torrent; + + /* increment the `corrupt' field */ + tor->corruptCur += byteCount; + + /* decrement the `downloaded' field */ + if( tor->downloadedCur >= byteCount ) + tor->downloadedCur -= byteCount; + else + tor->downloadedCur = 0; +} + + +static void +gotBadPiece( tr_peermsgs * msgs, uint32_t pieceIndex ) +{ + const uint32_t byteCount = tr_torPieceCountBytes( msgs->torrent, (int)pieceIndex ); + reassignBytesToCorrupt( msgs, byteCount ); +} + +static void +gotUnwantedBlock( tr_peermsgs * msgs, uint32_t index UNUSED, uint32_t offset UNUSED, uint32_t length ) +{ + reassignBytesToCorrupt( msgs, length ); +} + +static void +addUsToBlamefield( tr_peermsgs * msgs, uint32_t index ) +{ + if( !msgs->info->blame ) + msgs->info->blame = tr_bitfieldNew( msgs->torrent->info.pieceCount ); + tr_bitfieldAdd( msgs->info->blame, index ); +} + +static void +gotBlock( tr_peermsgs * msgs, + struct evbuffer * inbuf, + uint32_t index, + uint32_t offset, + uint32_t length ) +{ + tr_torrent * tor = msgs->torrent; + const int block = _tr_block( tor, index, offset ); + struct peer_request key, *req; + + /** + *** Remove the block from our `we asked for this' list + **/ + + key.index = index; + key.offset = offset; + key.length = length; + req = (struct peer_request*) tr_list_remove( &msgs->clientAskedFor, &key, + peer_request_compare ); + dbgmsg( msgs, "w00t peer sent us a block. turnaround time was %d seconds", + (int)(time(NULL) - req->time_requested) ); + if( req == NULL ) { + gotUnwantedBlock( msgs, index, offset, length ); + dbgmsg( msgs, "we didn't ask for this message..." ); + return; + } + tr_free( req ); + dbgmsg( msgs, "peer has %d more blocks we've asked for", + tr_list_size(msgs->clientAskedFor)); + + /** + *** Error checks + **/ + + if( tr_cpBlockIsComplete( tor->completion, block ) ) { + dbgmsg( msgs, "have this block already..." ); + tr_dbg( "have this block already..." ); + gotUnwantedBlock( msgs, index, offset, length ); + return; + } + + if( (int)length != tr_torBlockCountBytes( tor, block ) ) { + dbgmsg( msgs, "block is the wrong length..." ); + tr_dbg( "block is the wrong length..." ); + gotUnwantedBlock( msgs, index, offset, length ); + return; + } + + /** + *** Write the block + **/ + + if( tr_ioWrite( tor, index, offset, length, EVBUFFER_DATA( inbuf ))) { + return; + } + + tr_cpBlockAdd( tor->completion, block ); + + addUsToBlamefield( msgs, index ); + + fireGotBlock( msgs, index, offset, length ); + fireNeedReq( msgs ); + + /** + *** Handle if this was the last block in the piece + **/ + + if( tr_cpPieceIsComplete( tor->completion, index ) ) + { + if( !tr_ioHash( tor, index ) ) + { + gotBadPiece( msgs, index ); + return; + } + + fireClientHave( msgs, index ); + } +} + + +static ReadState +readBtPiece( tr_peermsgs * msgs, struct evbuffer * inbuf ) +{ + uint32_t inlen; + uint8_t * tmp; + + assert( msgs != NULL ); + assert( msgs->blockToUs.length > 0 ); + assert( inbuf != NULL ); + assert( EVBUFFER_LENGTH( inbuf ) > 0 ); + + /* read from the inbuf into our block buffer */ + inlen = MIN( EVBUFFER_LENGTH(inbuf), msgs->blockToUs.length ); + tmp = tr_new( uint8_t, inlen ); + tr_peerIoReadBytes( msgs->io, inbuf, tmp, inlen ); + evbuffer_add( msgs->inBlock, tmp, inlen ); + + /* update our tables accordingly */ + assert( inlen >= msgs->blockToUs.length ); + msgs->blockToUs.length -= inlen; + msgs->info->peerSentDataAt = time( NULL ); + clientGotBytes( msgs, inlen ); + + /* if this was the entire block, save it */ + if( !msgs->blockToUs.length ) + { + dbgmsg( msgs, "w00t -- got block index %u, offset %u", msgs->blockToUs.index, msgs->blockToUs.offset ); + assert( (int)EVBUFFER_LENGTH( msgs->inBlock ) == tr_torBlockCountBytes( msgs->torrent, _tr_block(msgs->torrent,msgs->blockToUs.index, msgs->blockToUs.offset) ) ); + gotBlock( msgs, msgs->inBlock, + msgs->blockToUs.index, + msgs->blockToUs.offset, + EVBUFFER_LENGTH( msgs->inBlock ) ); + evbuffer_drain( msgs->inBlock, ~0 ); + msgs->state = AWAITING_BT_LENGTH; + } + + /* cleanup */ + tr_free( tmp ); + return READ_AGAIN; +} + +static ReadState +canRead( struct bufferevent * evin, void * vpeer ) +{ + ReadState ret; + tr_peermsgs * peer = (tr_peermsgs *) vpeer; + struct evbuffer * inbuf = EVBUFFER_INPUT ( evin ); + + switch( peer->state ) + { + case AWAITING_BT_LENGTH: ret = readBtLength ( peer, inbuf ); break; + case AWAITING_BT_MESSAGE: ret = readBtMessage ( peer, inbuf ); break; + case READING_BT_PIECE: ret = readBtPiece ( peer, inbuf ); break; + default: assert( 0 ); + } + return ret; +} + +/** +*** +**/ + +static int +canWrite( const tr_peermsgs * msgs ) +{ + /* don't let our outbuffer get too large */ + if( tr_peerIoWriteBytesWaiting( msgs->io ) > 1024 ) + return FALSE; + + return TRUE; +} + +static int +canUpload( const tr_peermsgs * msgs ) +{ + const tr_torrent * tor = msgs->torrent; + + if( !canWrite( msgs ) ) + return FALSE; + + if( tor->uploadLimitMode == TR_SPEEDLIMIT_GLOBAL ) + return !tor->handle->useUploadLimit || tr_rcCanTransfer( tor->handle->upload ); + + if( tor->uploadLimitMode == TR_SPEEDLIMIT_SINGLE ) + return tr_rcCanTransfer( tor->upload ); + + return TRUE; +} + +static int +pulse( void * vmsgs ) +{ + tr_peermsgs * msgs = (tr_peermsgs *) vmsgs; + size_t len; + + /* if we froze out a downloaded block because of speed limits, + start listening to the peer again */ +#if 0 + if( msgs->notListening ) + { + fprintf( stderr, "msgs %p thawing out...\n", msgs ); + msgs->notListening = 0; + tr_peerIoSetIOMode ( msgs->io, EV_READ, 0 ); + } +#endif + + if( !canWrite( msgs ) ) + { + } + else if(( len = EVBUFFER_LENGTH( msgs->outBlock ) )) + { + while ( len && canUpload( msgs ) ) + { + const size_t outlen = MIN( len, 1024 ); + tr_peerIoWrite( msgs->io, EVBUFFER_DATA(msgs->outBlock), outlen ); + evbuffer_drain( msgs->outBlock, outlen ); + peerGotBytes( msgs, outlen ); + len -= outlen; + dbgmsg( msgs, "wrote %d bytes; %d left in block", (int)outlen, (int)len ); + } + } + else if(( len = EVBUFFER_LENGTH( msgs->outMessages ) )) + { + tr_peerIoWriteBuf( msgs->io, msgs->outMessages ); + } + else if(( msgs->peerAskedFor )) + { + struct peer_request * req = tr_list_pop_front( &msgs->peerAskedFor ); + uint8_t * tmp = tr_new( uint8_t, req->length ); + const uint8_t msgid = BT_PIECE; + const uint32_t msglen = sizeof(uint8_t) + sizeof(uint32_t)*2 + req->length; + tr_ioRead( msgs->torrent, req->index, req->offset, req->length, tmp ); + tr_peerIoWriteUint32( msgs->io, msgs->outBlock, msglen ); + tr_peerIoWriteBytes ( msgs->io, msgs->outBlock, &msgid, 1 ); + tr_peerIoWriteUint32( msgs->io, msgs->outBlock, req->index ); + tr_peerIoWriteUint32( msgs->io, msgs->outBlock, req->offset ); + tr_peerIoWriteBytes ( msgs->io, msgs->outBlock, tmp, req->length ); + tr_free( tmp ); + dbgmsg( msgs, "putting req into out queue: index %d, offset %d, length %d ... %d blocks left in our queue", (int)req->index, (int)req->offset, (int)req->length, tr_list_size(msgs->peerAskedFor) ); + tr_free( req ); + } + + return TRUE; /* loop forever */ +} + +static void +didWrite( struct bufferevent * evin UNUSED, void * vpeer ) +{ + pulse( (tr_peermsgs *) vpeer ); +} + +static void +gotError( struct bufferevent * evbuf UNUSED, short what UNUSED, void * vpeer ) +{ + fireGotError( (tr_peermsgs*)vpeer ); +} + +static void +sendBitfield( tr_peermsgs * msgs ) +{ + const tr_bitfield * bitfield = tr_cpPieceBitfield( msgs->torrent->completion ); + const uint32_t len = sizeof(uint8_t) + bitfield->len; + const uint8_t bt_msgid = BT_BITFIELD; + + dbgmsg( msgs, "sending peer a bitfield message\n", msgs ); + tr_peerIoWriteUint32( msgs->io, msgs->outMessages, len ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + tr_peerIoWriteBytes( msgs->io, msgs->outMessages, bitfield->bits, bitfield->len ); +} + +/** +*** +**/ + +#define MAX_DIFFS 50 + +typedef struct +{ + tr_pex * added; + tr_pex * dropped; + tr_pex * elements; + int addedCount; + int droppedCount; + int elementCount; + int diffCount; +} +PexDiffs; + +static void +pexAddedCb( void * vpex, void * userData ) +{ + PexDiffs * diffs = (PexDiffs *) userData; + tr_pex * pex = (tr_pex *) vpex; + if( diffs->diffCount < MAX_DIFFS ) + { + diffs->diffCount++; + diffs->added[diffs->addedCount++] = *pex; + diffs->elements[diffs->elementCount++] = *pex; + } +} + +static void +pexRemovedCb( void * vpex, void * userData ) +{ + PexDiffs * diffs = (PexDiffs *) userData; + tr_pex * pex = (tr_pex *) vpex; + if( diffs->diffCount < MAX_DIFFS ) + { + diffs->diffCount++; + diffs->dropped[diffs->droppedCount++] = *pex; + } +} + +static void +pexElementCb( void * vpex, void * userData ) +{ + PexDiffs * diffs = (PexDiffs *) userData; + tr_pex * pex = (tr_pex *) vpex; + if( diffs->diffCount < MAX_DIFFS ) + { + diffs->diffCount++; + diffs->elements[diffs->elementCount++] = *pex; + } +} + +static void +sendPex( tr_peermsgs * msgs ) +{ + if( msgs->peerSupportsPex && !msgs->torrent->pexDisabled ) + { + int i; + tr_pex * newPex = NULL; + const int newCount = tr_peerMgrGetPeers( msgs->handle->peerMgr, msgs->torrent->info.hash, &newPex ); + PexDiffs diffs; + benc_val_t val, *added, *dropped, *flags; + uint8_t *tmp, *walk; + char * benc; + int bencLen; + const uint8_t bt_msgid = BT_LTEP; + const uint8_t ltep_msgid = OUR_LTEP_PEX; + + /* build the diffs */ + diffs.added = tr_new( tr_pex, newCount ); + diffs.addedCount = 0; + diffs.dropped = tr_new( tr_pex, msgs->pexCount ); + diffs.droppedCount = 0; + diffs.elements = tr_new( tr_pex, newCount + msgs->pexCount ); + diffs.elementCount = 0; + diffs.diffCount = 0; + tr_set_compare( msgs->pex, msgs->pexCount, + newPex, newCount, + tr_pexCompare, sizeof(tr_pex), + pexRemovedCb, pexAddedCb, pexElementCb, &diffs ); + dbgmsg( msgs, "pex: old peer count %d, new peer count %d, added %d, removed %d", msgs->pexCount, newCount, diffs.addedCount, diffs.droppedCount ); + + /* update peer */ + tr_free( msgs->pex ); + msgs->pex = diffs.elements; + msgs->pexCount = diffs.elementCount; + + /* build the pex payload */ + tr_bencInit( &val, TYPE_DICT ); + tr_bencDictReserve( &val, 3 ); + + /* "added" */ + added = tr_bencDictAdd( &val, "added" ); + tmp = walk = tr_new( uint8_t, diffs.addedCount * 6 ); + for( i=0; i> -->> flag is %d", (int)diffs.added[i].flags ); + *walk++ = diffs.added[i].flags; + } + assert( ( walk - tmp ) == diffs.addedCount ); + tr_bencInitStr( flags, tmp, walk-tmp, FALSE ); + + /* "dropped" */ + dropped = tr_bencDictAdd( &val, "dropped" ); + tmp = walk = tr_new( uint8_t, diffs.droppedCount * 6 ); + for( i=0; iio, msgs->outMessages, 1 + 1 + bencLen ); + tr_peerIoWriteBytes ( msgs->io, msgs->outMessages, &bt_msgid, 1 ); + tr_peerIoWriteBytes ( msgs->io, msgs->outMessages, <ep_msgid, 1 ); + tr_peerIoWriteBytes ( msgs->io, msgs->outMessages, benc, bencLen ); + + /* cleanup */ + tr_free( benc ); + tr_bencFree( &val ); + tr_free( diffs.added ); + tr_free( diffs.dropped ); + tr_free( newPex ); + + msgs->clientSentPexAt = time( NULL ); + } +} + +static int +pexPulse( void * vpeer ) +{ + sendPex( vpeer ); + return TRUE; +} + +/** +*** +**/ + +tr_peermsgs* +tr_peerMsgsNew( struct tr_torrent * torrent, struct tr_peer * info ) +{ + tr_peermsgs * msgs; + + assert( info != NULL ); + assert( info->io != NULL ); + + msgs = tr_new0( tr_peermsgs, 1 ); + msgs->publisher = tr_publisherNew( ); + msgs->info = info; + msgs->handle = torrent->handle; + msgs->torrent = torrent; + msgs->io = info->io; + msgs->info->clientIsChoked = 1; + msgs->info->peerIsChoked = 1; + msgs->info->clientIsInterested = 0; + msgs->info->peerIsInterested = 0; + msgs->info->have = tr_bitfieldNew( torrent->info.pieceCount ); + msgs->pulseTimer = tr_timerNew( msgs->handle, pulse, msgs, PEER_PULSE_INTERVAL ); + msgs->pexTimer = tr_timerNew( msgs->handle, pexPulse, msgs, PEX_INTERVAL ); + msgs->outMessages = evbuffer_new( ); + msgs->outBlock = evbuffer_new( ); + msgs->inBlock = evbuffer_new( ); + + tr_peerIoSetIOFuncs( msgs->io, canRead, didWrite, gotError, msgs ); + tr_peerIoSetIOMode( msgs->io, EV_READ|EV_WRITE, 0 ); + + /** + *** If we initiated this connection, + *** we may need to send LTEP/AZMP handshakes. + *** Otherwise we'll wait for the peer to send theirs first. + **/ + if( !tr_peerIoIsIncoming( msgs->io ) ) + { + const int extensions = tr_peerIoGetExtension( msgs->io ); + switch( extensions ) + { + case LT_EXTENSIONS_NONE: + /* no-op */ + break; + + case LT_EXTENSIONS_LTEP: + sendLtepHandshake( msgs ); + break; + + case LT_EXTENSIONS_AZMP: + dbgmsg( msgs, "FIXME: need to send AZMP handshake" ); + break; + } + } + + sendBitfield( msgs ); + return msgs; +} + +void +tr_peerMsgsFree( tr_peermsgs* p ) +{ + if( p != NULL ) + { + tr_timerFree( &p->pulseTimer ); + tr_timerFree( &p->pexTimer ); + tr_publisherFree( &p->publisher ); + tr_list_foreach( p->clientAskedFor, tr_free ); + tr_list_free( &p->clientAskedFor ); + tr_list_foreach( p->peerAskedFor, tr_free ); + tr_list_free( &p->peerAskedFor ); + evbuffer_free( p->outMessages ); + evbuffer_free( p->outBlock ); + evbuffer_free( p->inBlock ); + tr_free( p ); + } +} + +tr_publisher_tag +tr_peerMsgsSubscribe( tr_peermsgs * peer, + tr_delivery_func func, + void * userData ) +{ + return tr_publisherSubscribe( peer->publisher, func, userData ); +} + +void +tr_peerMsgsUnsubscribe( tr_peermsgs * peer, + tr_publisher_tag tag ) +{ + tr_publisherUnsubscribe( peer->publisher, tag ); +} diff --git a/libtransmission/peer-msgs.h b/libtransmission/peer-msgs.h new file mode 100644 index 000000000..97d14b05c --- /dev/null +++ b/libtransmission/peer-msgs.h @@ -0,0 +1,84 @@ +/* + * This file Copyright (C) 2007 Charles Kerr + * + * This file is licensed by the GPL version 2. Works owned by the + * Transmission project are granted a special exemption to clause 2(b) + * so that the bulk of its code can remain under the MIT license. + * This exemption does not extend to derived works not owned by + * the Transmission project. + * + * $Id$ + */ + +#ifndef TR_P_H +#define TR_P_H + +#include +#include "publish.h" + +struct tr_torrent; +struct tr_peer; +struct tr_bitfield; + +typedef struct tr_peermsgs tr_peermsgs; + +tr_peermsgs* tr_peerMsgsNew( struct tr_torrent * torrent, + struct tr_peer * peer ); + +void tr_peerMsgsSetChoke( tr_peermsgs *, int doChoke ); + +void tr_peerMsgsHave( tr_peermsgs * msgs, + uint32_t pieceIndex ); + +void tr_peerMsgsCancel( tr_peermsgs * msgs, + uint32_t pieceIndex, + uint32_t offset, + uint32_t length ); + +void tr_peerMsgsFree( tr_peermsgs* ); + + +enum { + TR_ADDREQ_OK=0, + TR_ADDREQ_FULL, + TR_ADDREQ_MISSING, + TR_ADDREQ_CLIENT_CHOKED +}; + +int tr_peerMsgsAddRequest( tr_peermsgs * peer, + uint32_t index, + uint32_t begin, + uint32_t length ); + +/** +*** PeerMsgs Publish / Subscribe +**/ + +typedef enum +{ + TR_PEERMSG_CLIENT_HAVE, + TR_PEERMSG_CLIENT_BLOCK, + TR_PEERMSG_GOT_ERROR, + TR_PEERMSG_NEED_REQ +} +PeerMsgsEventType; + +typedef struct +{ + PeerMsgsEventType eventType; + uint32_t pieceIndex; /* for TR_PEERMSG_GOT_BLOCK, TR_PEERMSG_GOT_HAVE */ + uint32_t offset; /* for TR_PEERMSG_GOT_BLOCK */ + uint32_t length; /* for TR_PEERMSG_GOT_BLOCK */ +} +tr_peermsgs_event; + +tr_publisher_tag tr_peerMsgsSubscribe ( tr_peermsgs * peer, + tr_delivery_func func, + void * user ); + +void tr_peerMsgsUnsubscribe ( tr_peermsgs * peer, + tr_publisher_tag tag ); + + + +#endif diff --git a/libtransmission/peer.c b/libtransmission/peer.c index 051a55607..e68379b9f 100644 --- a/libtransmission/peer.c +++ b/libtransmission/peer.c @@ -174,7 +174,7 @@ typedef struct tr_request_s struct tr_peer_s { - tr_torrent_t * tor; + tr_torrent * tor; struct in_addr addr; tr_port_t port; /* peer's listening port, 0 if not known */ @@ -214,10 +214,10 @@ struct tr_peer_s uint8_t id[TR_ID_LEN]; /* The pieces that the peer has */ - tr_bitfield_t * bitfield; + tr_bitfield * bitfield; /* blocks we've requested from this peer */ - tr_bitfield_t * reqfield; + tr_bitfield * reqfield; int pieceCount; float progress; @@ -225,9 +225,9 @@ struct tr_peer_s int badPcs; int banned; /* The pieces that the peer is contributing to */ - tr_bitfield_t * blamefield; + tr_bitfield * blamefield; /* The bad pieces that the peer has contributed to */ - tr_bitfield_t * banfield; + tr_bitfield * banfield; uint8_t * buf; int size; @@ -249,11 +249,11 @@ struct tr_peer_s int inBegin; int inLength; - tr_list_t * outRequests; + tr_list * outRequests; uint64_t outDate; - tr_ratecontrol_t * download; - tr_ratecontrol_t * upload; + tr_ratecontrol * download; + tr_ratecontrol * upload; char * client; int extclient; @@ -291,16 +291,6 @@ static void tr_htonl( uint32_t a, void * p ) memcpy ( p, &u, sizeof( uint32_t ) ); } -static const char* getPeerId( void ) -{ - static char * peerId = NULL; - if( !peerId ) { - peerId = tr_new0( char, TR_ID_LEN + 1 ); - tr_peerIdNew( peerId, TR_ID_LEN + 1 ); - } - return peerId; -} - static void tr_peerPieceIsCorrupt ( tr_peer_t *, int pieceIndex ); #include "peerext.h" @@ -353,7 +343,7 @@ tr_peer_t * tr_peerInit( const struct in_addr * addr, tr_port_t port, void tr_peerDestroy( tr_peer_t * peer ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; tr_request_t * r; int i, block; @@ -423,7 +413,7 @@ void tr_peerSetPrivate( tr_peer_t * peer, int private ) } } -void tr_peerSetTorrent( tr_peer_t * peer, tr_torrent_t * tor ) +void tr_peerSetTorrent( tr_peer_t * peer, tr_torrent * tor ) { peer->tor = tor; } @@ -435,7 +425,7 @@ void tr_peerSetTorrent( tr_peer_t * peer, tr_torrent_t * tor ) **********************************************************************/ int tr_peerRead( tr_peer_t * peer ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; int ret; uint64_t date; @@ -542,7 +532,7 @@ const uint8_t * tr_peerHash( const tr_peer_t * peer ) **********************************************************************/ int tr_peerPulse( tr_peer_t * peer ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; int ret, size; uint8_t * p; uint64_t date; @@ -580,7 +570,7 @@ int tr_peerPulse( tr_peer_t * peer ) if( PEER_STATUS_CONNECTING == peer->status ) { uint8_t buf[HANDSHAKE_SIZE]; - const tr_info_t * inf; + const tr_info * inf; inf = tr_torrentInfo( tor ); assert( 68 == HANDSHAKE_SIZE ); @@ -793,7 +783,7 @@ int tr_peerIsConnected( const tr_peer_t * peer ) return peer && (peer->status == PEER_STATUS_CONNECTED); } -int tr_peerIsFrom( const tr_peer_t * peer ) +int tr_peerGetFrom( const tr_peer_t * peer ) { return peer->from; } @@ -885,7 +875,7 @@ static int peerIsGood( const tr_peer_t * peer ) void tr_peerBlame( tr_peer_t * peer, int piece, int success ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; if( !peer->blamefield || !tr_bitfieldHas( peer->blamefield, piece ) ) { @@ -926,7 +916,7 @@ void tr_peerBlame( tr_peer_t * peer, int piece, int success ) tr_bitfieldRem( peer->blamefield, piece ); } -int tr_peerGetConnectable( const tr_torrent_t * tor, uint8_t ** _buf ) +int tr_peerGetConnectable( const tr_torrent * tor, uint8_t ** _buf ) { int count = 0; uint8_t * buf; @@ -969,7 +959,7 @@ int tr_peerGetConnectable( const tr_torrent_t * tor, uint8_t ** _buf ) void tr_peerPieceIsCorrupt( tr_peer_t * peer, int pieceIndex ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; const uint64_t byteCount = tr_torPieceCountBytes( tor, pieceIndex ); @@ -986,7 +976,7 @@ tr_peerPieceIsCorrupt( tr_peer_t * peer, int pieceIndex ) void tr_peerSentBlockToUs( tr_peer_t * peer, int byteCount ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; assert( byteCount >= 0 ); assert( byteCount <= tor->info.pieceSize ); @@ -1002,7 +992,7 @@ tr_peerSentBlockToUs( tr_peer_t * peer, int byteCount ) void tr_peerGotBlockFromUs ( tr_peer_t * peer, int byteCount ) { - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; assert( byteCount >= 0 ); assert( byteCount <= tor->info.pieceSize ); @@ -1016,7 +1006,7 @@ tr_peerGotBlockFromUs ( tr_peer_t * peer, int byteCount ) } static void -tr_torrentSwiftPulse ( tr_torrent_t * tor ) +tr_torrentSwiftPulse ( tr_torrent * tor ) { /* Preferred # of seconds for the request queue's turnaround time. This is just an arbitrary number. */ @@ -1091,7 +1081,7 @@ tr_swiftPulse( tr_handle_t * h ) if( lastPulseTime + SWIFT_REFRESH_INTERVAL_SEC <= time( NULL ) ) { - tr_torrent_t * tor; + tr_torrent * tor; for( tor=h->torrentList; tor; tor=tor->next ) tr_torrentSwiftPulse( tor ); diff --git a/libtransmission/peer.h b/libtransmission/peer.h index bda18a178..eb63ae334 100644 --- a/libtransmission/peer.h +++ b/libtransmission/peer.h @@ -34,7 +34,7 @@ tr_peer_t * tr_peerInit ( const struct in_addr *, tr_port_t, int sock void tr_peerDestroy ( tr_peer_t * ); const char *tr_peerClient ( tr_peer_t * ); void tr_peerSetPrivate ( tr_peer_t *, int ); -void tr_peerSetTorrent ( tr_peer_t *, struct tr_torrent_s * ); +void tr_peerSetTorrent ( tr_peer_t *, struct tr_torrent * ); void tr_peerSentBlockToUs ( tr_peer_t *, int byteCount ); void tr_peerGotBlockFromUs ( tr_peer_t *, int byteCount ); int tr_peerRead ( tr_peer_t * ); @@ -42,7 +42,7 @@ uint64_t tr_peerDate ( const tr_peer_t * ); const uint8_t * tr_peerHash ( const tr_peer_t * ); int tr_peerPulse ( tr_peer_t * ); int tr_peerIsConnected ( const tr_peer_t * ); -int tr_peerIsFrom ( const tr_peer_t * ); +int tr_peerGetFrom ( const tr_peer_t * ); int tr_peerTimesChoked ( const tr_peer_t * ); int tr_peerIsChokingUs ( const tr_peer_t * ); int tr_peerIsChokedByUs ( const tr_peer_t * ); @@ -60,7 +60,7 @@ void tr_peerSetOptimistic ( tr_peer_t *, int ); int tr_peerIsOptimistic ( const tr_peer_t * ); void tr_peerBlame ( tr_peer_t *, int piece, int success ); struct in_addr * tr_peerAddress ( tr_peer_t * ); -int tr_peerGetConnectable ( const struct tr_torrent_s *, uint8_t ** ); +int tr_peerGetConnectable ( const struct tr_torrent *, uint8_t ** ); void tr_swiftPulse ( tr_handle_t * ); diff --git a/libtransmission/peeraz.h b/libtransmission/peeraz.h index 67d0e69fb..9dc9b94e4 100644 --- a/libtransmission/peeraz.h +++ b/libtransmission/peeraz.h @@ -97,7 +97,7 @@ azmsgNameIndex( const char * name, int len ) } static uint8_t * -makeAZHandshake( tr_torrent_t * tor, tr_peer_t * peer, int * buflen ) +makeAZHandshake( tr_torrent * tor, tr_peer_t * peer, int * buflen ) { char * buf; benc_val_t val, * msgsval, * msgdictval; @@ -224,7 +224,7 @@ peertreeToBencAZ( tr_peertree_t * tree, benc_val_t * val ) } static int -makeAZPex( tr_torrent_t * tor, tr_peer_t * peer, char ** buf, int * len ) +makeAZPex( tr_torrent * tor, tr_peer_t * peer, char ** buf, int * len ) { benc_val_t val; @@ -235,7 +235,7 @@ makeAZPex( tr_torrent_t * tor, tr_peer_t * peer, char ** buf, int * len ) } static int -sendAZHandshake( tr_torrent_t * tor, tr_peer_t * peer ) +sendAZHandshake( tr_torrent * tor, tr_peer_t * peer ) { uint8_t * buf; int len; @@ -372,7 +372,7 @@ static int parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len ) { benc_val_t val, * sub, * sub2, * dict, * subsub; - tr_bitfield_t * msgs; + tr_bitfield * msgs; int ii, idx, newclient; char * client; @@ -492,9 +492,9 @@ parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len ) } static int -parseAZPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len ) +parseAZPex( tr_torrent * tor, tr_peer_t * peer, uint8_t * buf, int len ) { - tr_info_t * info = &tor->info; + tr_info * info = &tor->info; benc_val_t val, * list, * pair; int ii, used; diff --git a/libtransmission/peerext.h b/libtransmission/peerext.h index 16251db39..746636f4c 100644 --- a/libtransmission/peerext.h +++ b/libtransmission/peerext.h @@ -26,7 +26,7 @@ #define EXTENDED_PEX_ID 1 static int -makeCommonPex( tr_torrent_t * tor, tr_peer_t * peer, +makeCommonPex( tr_torrent * tor, tr_peer_t * peer, int ( *peerfunc )( tr_peertree_t *, benc_val_t * ), const char * extrakey, benc_val_t * extraval, char ** retbuf, int * retlen ) @@ -122,7 +122,7 @@ makeCommonPex( tr_torrent_t * tor, tr_peer_t * peer, } static char * -makeExtendedHandshake( tr_torrent_t * tor, tr_peer_t * peer, int * len ) +makeExtendedHandshake( tr_torrent * tor, tr_peer_t * peer, int * len ) { benc_val_t val, * msgsval; char * buf; @@ -205,7 +205,7 @@ peertreeToBencUT( tr_peertree_t * tree, benc_val_t * val ) } static int -makeUTPex( tr_torrent_t * tor, tr_peer_t * peer, char ** buf, int * len ) +makeUTPex( tr_torrent * tor, tr_peer_t * peer, char ** buf, int * len ) { benc_val_t val; @@ -299,7 +299,7 @@ parseExtendedHandshake( tr_peer_t * peer, uint8_t * buf, int len ) } static int -parseUTPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len ) +parseUTPex( tr_torrent * tor, tr_peer_t * peer, uint8_t * buf, int len ) { benc_val_t val, * sub; int used; @@ -326,11 +326,13 @@ parseUTPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len ) sub = tr_bencDictFind( &val, "added" ); if( NULL != sub && TYPE_STR == sub->type && 0 == sub->val.s.i % 6 ) { +#if 0 used = tr_torrentAddCompact( tor, TR_PEER_FROM_PEX, ( uint8_t * )sub->val.s.s, sub->val.s.i / 6 ); peer_dbg( "GET extended-pex, got %i peers, used %i", sub->val.s.i / 6, used ); +#endif } else { diff --git a/libtransmission/peermessages.h b/libtransmission/peermessages.h index 8f37bb3c4..71705f632 100644 --- a/libtransmission/peermessages.h +++ b/libtransmission/peermessages.h @@ -105,9 +105,9 @@ fillHeader( tr_peer_t * peer, int size, int id, uint8_t * buf ) } static uint8_t * -blockPending( tr_torrent_t * tor, - tr_peer_t * peer, - int * size ) +blockPending( tr_torrent * tor, + tr_peer_t * peer, + int * size ) { if( !peer->outBlockLoaded ) /* we need to load the block for the next request */ { @@ -143,7 +143,7 @@ blockPending( tr_torrent_t * tor, tr_htonl( r->begin, buf ); buf += 4; - tr_ioRead( tor->io, r->index, r->begin, r->length, buf ); + tr_ioRead( tor, r->index, r->begin, r->length, buf ); peer_dbg( "SEND piece %d/%d (%d bytes)", r->index, r->begin, r->length ); @@ -274,10 +274,10 @@ static void sendHave( tr_peer_t * peer, int piece ) * - id = 5 (1 byte) * - bitfield (X bytes) **********************************************************************/ -static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer ) +static void sendBitfield( tr_torrent * tor, tr_peer_t * peer ) { uint8_t * p; - const tr_bitfield_t * bitfield; + const tr_bitfield * bitfield; bitfield = tr_cpPieceBitfield( tor->completion ); p = getMessagePointer( peer, bitfield->len, PEER_MSG_BITFIELD ); @@ -292,9 +292,9 @@ static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer ) *********************************************************************** * **********************************************************************/ -static void sendRequest( tr_torrent_t * tor, tr_peer_t * peer, int block ) +static void sendRequest( tr_torrent * tor, tr_peer_t * peer, int block ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; tr_request_t * r; uint8_t * p; @@ -343,7 +343,7 @@ static void sendCancel( tr_peer_t * peer, int index, int begin, *********************************************************************** * **********************************************************************/ -static void broadcastCancel( tr_torrent_t * tor, int index, int begin, +static void broadcastCancel( tr_torrent * tor, int index, int begin, int length ) { int i, j; @@ -385,7 +385,7 @@ static void broadcastCancel( tr_torrent_t * tor, int index, int begin, * - eid = Y (1 byte) * - data (X bytes) **********************************************************************/ -static int sendExtended( tr_torrent_t * tor, tr_peer_t * peer, int id ) +static int sendExtended( tr_torrent * tor, tr_peer_t * peer, int id ) { uint8_t * p; char * buf; @@ -433,7 +433,7 @@ static int sendExtended( tr_torrent_t * tor, tr_peer_t * peer, int id ) *********************************************************************** * **********************************************************************/ -static int sendAZPex( tr_torrent_t * tor, tr_peer_t * peer ) +static int sendAZPex( tr_torrent * tor, tr_peer_t * peer ) { uint8_t * p; char * buf; diff --git a/libtransmission/peerparse.h b/libtransmission/peerparse.h index 9a5ec91e4..46862b5c9 100644 --- a/libtransmission/peerparse.h +++ b/libtransmission/peerparse.h @@ -32,10 +32,10 @@ * **********************************************************************/ -static int parseChoke( tr_torrent_t * tor, - tr_peer_t * peer, - int len, - int choking ) +static int parseChoke( tr_torrent * tor, + tr_peer_t * peer, + int len, + int choking ) { tr_request_t * r; int i; @@ -101,10 +101,10 @@ static int parseInterested( tr_peer_t * peer, int len, *********************************************************************** * **********************************************************************/ -static int parseHave( tr_torrent_t * tor, tr_peer_t * peer, - uint8_t * p, int len ) +static int parseHave( tr_torrent * tor, tr_peer_t * peer, + uint8_t * p, int len ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; uint32_t piece; if( len != 4 ) @@ -139,10 +139,10 @@ static int parseHave( tr_torrent_t * tor, tr_peer_t * peer, return TR_OK; } -static int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer, - uint8_t * p, int len ) +static int parseBitfield( tr_torrent * tor, tr_peer_t * peer, + uint8_t * p, int len ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; int bitfieldSize; int i; @@ -194,10 +194,10 @@ static int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer, return TR_OK; } -static int parseRequest( tr_torrent_t * tor, tr_peer_t * peer, - uint8_t * p, int len ) +static int parseRequest( tr_torrent * tor, tr_peer_t * peer, + uint8_t * p, int len ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; int index, begin, length; tr_request_t * r; @@ -281,10 +281,10 @@ static void updateRequests( tr_peer_t * peer, int index, int begin ) } } -static int parsePiece( tr_torrent_t * tor, tr_peer_t * peer, - uint8_t * p, int len ) +static int parsePiece( tr_torrent * tor, tr_peer_t * peer, + uint8_t * p, int len ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; int index, begin, block, i, ret; if( 8 > len ) @@ -334,7 +334,7 @@ static int parsePiece( tr_torrent_t * tor, tr_peer_t * peer, tr_bitfieldAdd( peer->blamefield, index ); /* Write to disk */ - if( ( ret = tr_ioWrite( tor->io, index, begin, len - 8, &p[8] ) ) ) + if( ( ret = tr_ioWrite( tor, index, begin, len - 8, &p[8] ) ) ) { return ret; } @@ -379,13 +379,13 @@ static int reqCompare( const void * va, const void * vb ) return a->length - b->length; } -static int parseCancel( tr_torrent_t * tor, tr_peer_t * peer, - uint8_t * p, int len ) +static int parseCancel( tr_torrent * tor, tr_peer_t * peer, + uint8_t * p, int len ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; int index, begin, length; tr_request_t req; - tr_list_t * l; + tr_list * l; if( len != 12 ) { @@ -474,8 +474,8 @@ parseMessageHeader( tr_peer_t * peer, uint8_t * buf, int buflen, } } -static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer, - int id, uint8_t * p, int len ) +static int parseMessage( tr_torrent * tor, tr_peer_t * peer, + int id, uint8_t * p, int len ) { int extid; @@ -597,9 +597,9 @@ static const uint8_t * parseBufHash( const tr_peer_t * peer ) } } -static int parseHandshake( tr_torrent_t * tor, tr_peer_t * peer ) +static int parseHandshake( tr_torrent * tor, tr_peer_t * peer ) { - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; int ii, extmsgs, azproto; char * dbgsup, * dbgwant; @@ -694,7 +694,7 @@ static int parseHandshake( tr_torrent_t * tor, tr_peer_t * peer ) return TR_OK; } -static int sendInitial( tr_torrent_t * tor, tr_peer_t * peer ) +static int sendInitial( tr_torrent * tor, tr_peer_t * peer ) { if( PEER_STATUS_CONNECTED != peer->status ) { @@ -715,7 +715,7 @@ static int sendInitial( tr_torrent_t * tor, tr_peer_t * peer ) return TR_OK; } -static int parseBuf( tr_torrent_t * tor, tr_peer_t * peer ) +static int parseBuf( tr_torrent * tor, tr_peer_t * peer ) { int len, ret, msgid; uint8_t * buf; diff --git a/libtransmission/peerutils.h b/libtransmission/peerutils.h index f707ff93e..bfa6ae56d 100644 --- a/libtransmission/peerutils.h +++ b/libtransmission/peerutils.h @@ -38,7 +38,7 @@ static int checkPeer( tr_peer_t * peer ) { int ret; - tr_torrent_t * tor = peer->tor; + tr_torrent * tor = peer->tor; const uint64_t now = tr_date( ); const uint64_t idleTime = now - peer->date; const uint64_t idleSecs = idleTime / 1000u; @@ -137,9 +137,9 @@ checkPeer( tr_peer_t * peer ) return TR_OK; } -static int isPieceInteresting( const tr_torrent_t * tor, - const tr_peer_t * peer, - int piece ) +static int isPieceInteresting( const tr_torrent * tor, + const tr_peer_t * peer, + int piece ) { if( tor->info.pieces[piece].dnd ) /* we don't want it */ return 0; @@ -162,10 +162,10 @@ static int isPieceInteresting( const tr_torrent_t * tor, * Returns 1 if 'peer' has at least one piece that we want but * haven't completed, or 0 otherwise. **********************************************************************/ -static int isInteresting( const tr_torrent_t * tor, const tr_peer_t * peer ) +static int isInteresting( const tr_torrent * tor, const tr_peer_t * peer ) { int i; - const tr_bitfield_t * bitfield = tr_cpPieceBitfield( tor->completion ); + const tr_bitfield * bitfield = tr_cpPieceBitfield( tor->completion ); if( !peer->bitfield ) { @@ -183,7 +183,7 @@ static int isInteresting( const tr_torrent_t * tor, const tr_peer_t * peer ) } static void -updateInterest( tr_torrent_t * tor, tr_peer_t * peer ) +updateInterest( tr_torrent * tor, tr_peer_t * peer ) { const int i = !!isInteresting( tor, peer ); @@ -223,12 +223,12 @@ int comparePieces (const void * aIn, const void * bIn) return a->piece - b->piece; } -static int* getPreferredPieces( const tr_torrent_t * tor, - const tr_peer_t * peer, - int * pieceCount, - int * isEndgame ) +static int* getPreferredPieces( const tr_torrent * tor, + const tr_peer_t * peer, + int * pieceCount, + int * isEndgame ) { - const tr_info_t * inf = &tor->info; + const tr_info * inf = &tor->info; int i; int poolSize = 0; diff --git a/libtransmission/platform.c b/libtransmission/platform.c index 794b1f20b..0b059ca8e 100644 --- a/libtransmission/platform.c +++ b/libtransmission/platform.c @@ -56,11 +56,11 @@ **** THREADS ***/ -struct tr_thread_s +struct tr_thread { void (* func ) ( void * ); void * arg; - char * name; + const char * name; #ifdef __BEOS__ thread_id thread; @@ -82,8 +82,8 @@ struct tr_thread_s static ThreadFuncReturnType ThreadFunc( void * _t ) { - tr_thread_t * t = _t; - char* name = tr_strdup( t->name ); + tr_thread * t = _t; + const char * name = t->name; #ifdef __BEOS__ /* This is required because on BeOS, SIGINT is sent to each thread, @@ -95,23 +95,21 @@ ThreadFunc( void * _t ) t->func( t->arg ); tr_dbg( "Thread '%s' exited", name ); - tr_free( name ); - #ifdef WIN32 _endthreadex( 0 ); return 0; #endif } -tr_thread_t * +tr_thread * tr_threadNew( void (*func)(void *), void * arg, const char * name ) { - tr_thread_t * t = tr_new0( tr_thread_t, 1 ); + tr_thread * t = tr_new0( tr_thread, 1 ); t->func = func; t->arg = arg; - t->name = tr_strdup( name ); + t->name = name; #ifdef __BEOS__ t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t ); @@ -126,21 +124,19 @@ tr_threadNew( void (*func)(void *), } int -tr_amInThread ( const tr_thread_t * t ) +tr_amInThread ( const tr_thread * t ) { - int ret; #ifdef __BEOS__ - ret = find_thread(NULL) == t->thread; + return find_thread(NULL) == t->thread; #elif defined(WIN32) - ret = GetCurrentThreadId() == t->thread_id; + return GetCurrentThreadId() == t->thread_id; #else - ret = pthread_equal( t->thread, pthread_self( ) ); + return pthread_equal( t->thread, pthread_self( ) ); #endif - return ret; } void -tr_threadJoin( tr_thread_t * t ) +tr_threadJoin( tr_thread * t ) { if( t != NULL ) { @@ -155,7 +151,6 @@ tr_threadJoin( tr_thread_t * t ) #endif tr_dbg( "Thread '%s' joined", t->name ); - tr_free( t->name ); t->name = NULL; t->func = NULL; tr_free( t ); @@ -166,7 +161,7 @@ tr_threadJoin( tr_thread_t * t ) **** LOCKS ***/ -struct tr_lock_s +struct tr_lock { #ifdef __BEOS__ sem_id lock; @@ -177,10 +172,10 @@ struct tr_lock_s #endif }; -tr_lock_t* +tr_lock* tr_lockNew( void ) { - tr_lock_t * l = tr_new0( tr_lock_t, 1 ); + tr_lock * l = tr_new0( tr_lock, 1 ); #ifdef __BEOS__ l->lock = create_sem( 1, "" ); @@ -194,7 +189,7 @@ tr_lockNew( void ) } void -tr_lockFree( tr_lock_t * l ) +tr_lockFree( tr_lock * l ) { #ifdef __BEOS__ delete_sem( l->lock ); @@ -207,7 +202,7 @@ tr_lockFree( tr_lock_t * l ) } int -tr_lockTryLock( tr_lock_t * l ) /* success on zero! */ +tr_lockTryLock( tr_lock * l ) /* success on zero! */ { #ifdef __BEOS__ return acquire_sem_etc( l->lock, 1, B_RELATIVE_TIMEOUT, 0 ); @@ -219,7 +214,7 @@ tr_lockTryLock( tr_lock_t * l ) /* success on zero! */ } void -tr_lockLock( tr_lock_t * l ) +tr_lockLock( tr_lock * l ) { #ifdef __BEOS__ acquire_sem( l->lock ); @@ -231,7 +226,7 @@ tr_lockLock( tr_lock_t * l ) } void -tr_lockUnlock( tr_lock_t * l ) +tr_lockUnlock( tr_lock * l ) { #ifdef __BEOS__ release_sem( l->lock ); @@ -242,121 +237,11 @@ tr_lockUnlock( tr_lock_t * l ) #endif } -/*** -**** RW LOCK -***/ - -struct tr_rwlock_s -{ - tr_lock_t * lock; - tr_cond_t * readCond; - tr_cond_t * writeCond; - size_t readCount; - size_t wantToRead; - size_t wantToWrite; - int haveWriter; -}; - -static void -tr_rwSignal( tr_rwlock_t * rw ) -{ - if ( rw->wantToWrite ) - tr_condSignal( rw->writeCond ); - else if ( rw->wantToRead ) - tr_condBroadcast( rw->readCond ); -} - -tr_rwlock_t* -tr_rwNew ( void ) -{ - tr_rwlock_t * rw = tr_new0( tr_rwlock_t, 1 ); - rw->lock = tr_lockNew( ); - rw->readCond = tr_condNew( ); - rw->writeCond = tr_condNew( ); - return rw; -} - -void -tr_rwReaderLock( tr_rwlock_t * rw ) -{ - tr_lockLock( rw->lock ); - rw->wantToRead++; - while( rw->haveWriter || rw->wantToWrite ) - tr_condWait( rw->readCond, rw->lock ); - rw->wantToRead--; - rw->readCount++; - tr_lockUnlock( rw->lock ); -} - -int -tr_rwReaderTrylock( tr_rwlock_t * rw ) -{ - int ret = FALSE; - tr_lockLock( rw->lock ); - if ( !rw->haveWriter && !rw->wantToWrite ) { - rw->readCount++; - ret = TRUE; - } - tr_lockUnlock( rw->lock ); - return ret; - -} - -void -tr_rwReaderUnlock( tr_rwlock_t * rw ) -{ - tr_lockLock( rw->lock ); - --rw->readCount; - if( !rw->readCount ) - tr_rwSignal( rw ); - tr_lockUnlock( rw->lock ); -} - -void -tr_rwWriterLock( tr_rwlock_t * rw ) -{ - tr_lockLock( rw->lock ); - rw->wantToWrite++; - while( rw->haveWriter || rw->readCount ) - tr_condWait( rw->writeCond, rw->lock ); - rw->wantToWrite--; - rw->haveWriter = TRUE; - tr_lockUnlock( rw->lock ); -} - -int -tr_rwWriterTrylock( tr_rwlock_t * rw ) -{ - int ret = FALSE; - tr_lockLock( rw->lock ); - if( !rw->haveWriter && !rw->readCount ) - ret = rw->haveWriter = TRUE; - tr_lockUnlock( rw->lock ); - return ret; -} -void -tr_rwWriterUnlock( tr_rwlock_t * rw ) -{ - tr_lockLock( rw->lock ); - rw->haveWriter = FALSE; - tr_rwSignal( rw ); - tr_lockUnlock( rw->lock ); -} - -void -tr_rwFree( tr_rwlock_t * rw ) -{ - tr_condFree( rw->writeCond ); - tr_condFree( rw->readCond ); - tr_lockFree( rw->lock ); - tr_free( rw ); -} - /*** **** COND ***/ -struct tr_cond_s +struct tr_cond { #ifdef __BEOS__ sem_id sem; @@ -364,7 +249,7 @@ struct tr_cond_s int start, end; #elif defined(WIN32) tr_list * events; - tr_lock_t * lock; + tr_lock * lock; #else pthread_cond_t cond; #endif @@ -383,10 +268,10 @@ static DWORD getContEventTLS( void ) } #endif -tr_cond_t* +tr_cond* tr_condNew( void ) { - tr_cond_t * c = tr_new0( tr_cond_t, 1 ); + tr_cond * c = tr_new0( tr_cond, 1 ); #ifdef __BEOS__ c->sem = create_sem( 1, "" ); c->start = 0; @@ -401,7 +286,7 @@ tr_condNew( void ) } void -tr_condWait( tr_cond_t * c, tr_lock_t * l ) +tr_condWait( tr_cond * c, tr_lock * l ) { #ifdef __BEOS__ @@ -449,7 +334,7 @@ tr_condWait( tr_cond_t * c, tr_lock_t * l ) } #ifdef __BEOS__ -static int condTrySignal( tr_cond_t * c ) +static int condTrySignal( tr_cond * c ) { if( c->start == c->end ) return 1; @@ -473,7 +358,7 @@ static int condTrySignal( tr_cond_t * c ) } #endif void -tr_condSignal( tr_cond_t * c ) +tr_condSignal( tr_cond * c ) { #ifdef __BEOS__ @@ -496,7 +381,7 @@ tr_condSignal( tr_cond_t * c ) } void -tr_condBroadcast( tr_cond_t * c ) +tr_condBroadcast( tr_cond * c ) { #ifdef __BEOS__ @@ -520,7 +405,7 @@ tr_condBroadcast( tr_cond_t * c ) } void -tr_condFree( tr_cond_t * c ) +tr_condFree( tr_cond * c ) { #ifdef __BEOS__ delete_sem( c->sem ); diff --git a/libtransmission/platform.h b/libtransmission/platform.h index b0dea59ed..8c692fc3a 100644 --- a/libtransmission/platform.h +++ b/libtransmission/platform.h @@ -24,46 +24,30 @@ #ifndef TR_PLATFORM_H #define TR_PLATFORM_H -typedef struct tr_lock_s tr_lock_t; -typedef struct tr_cond_s tr_cond_t; -typedef struct tr_thread_s tr_thread_t; +typedef struct tr_lock tr_lock; +typedef struct tr_cond tr_cond; +typedef struct tr_thread tr_thread; const char * tr_getHomeDirectory( void ); const char * tr_getCacheDirectory( void ); const char * tr_getTorrentsDirectory( void ); -tr_thread_t* tr_threadNew ( void (*func)(void *), void * arg, const char * name ); -void tr_threadJoin ( tr_thread_t * ); +tr_thread* tr_threadNew ( void (*func)(void *), void * arg, const char * name ); +void tr_threadJoin ( tr_thread * ); +int tr_amInThread ( const tr_thread * ); int tr_amInThread ( const tr_thread_t * ); -tr_lock_t * tr_lockNew ( void ); -void tr_lockFree ( tr_lock_t * ); -int tr_lockTryLock ( tr_lock_t * ); -void tr_lockLock ( tr_lock_t * ); -void tr_lockUnlock ( tr_lock_t * ); - -tr_cond_t * tr_condNew ( void ); -void tr_condFree ( tr_cond_t * ); -void tr_condSignal ( tr_cond_t * ); -void tr_condBroadcast ( tr_cond_t * ); -void tr_condWait ( tr_cond_t *, tr_lock_t * ); - -/*** -**** RW lock: -**** The lock can be had by one writer or any number of readers. -***/ - -typedef struct tr_rwlock_s tr_rwlock_t; - -tr_rwlock_t* tr_rwNew ( void ); -void tr_rwFree ( tr_rwlock_t * ); -void tr_rwReaderLock ( tr_rwlock_t * ); -int tr_rwReaderTrylock ( tr_rwlock_t * ); -void tr_rwReaderUnlock ( tr_rwlock_t * ); -void tr_rwWriterLock ( tr_rwlock_t * ); -int tr_rwWriterTrylock ( tr_rwlock_t * ); -void tr_rwWriterUnlock ( tr_rwlock_t * ); +tr_lock * tr_lockNew ( void ); +void tr_lockFree ( tr_lock * ); +int tr_lockTryLock ( tr_lock * ); +void tr_lockLock ( tr_lock * ); +void tr_lockUnlock ( tr_lock * ); +tr_cond * tr_condNew ( void ); +void tr_condFree ( tr_cond * ); +void tr_condSignal ( tr_cond * ); +void tr_condBroadcast ( tr_cond * ); +void tr_condWait ( tr_cond *, tr_lock * ); struct in_addr; /* forward declaration to calm gcc down */ int diff --git a/libtransmission/ptrarray.c b/libtransmission/ptrarray.c index aedaa22be..b7c05e037 100644 --- a/libtransmission/ptrarray.c +++ b/libtransmission/ptrarray.c @@ -19,19 +19,19 @@ #define GROW 32 -struct tr_ptrArray_s +struct tr_ptrArray { void ** items; int n_items; int n_alloc; }; -tr_ptrArray_t* +tr_ptrArray* tr_ptrArrayNew( void ) { - tr_ptrArray_t * p; + tr_ptrArray * p; - p = tr_new( tr_ptrArray_t, 1 ); + p = tr_new( tr_ptrArray, 1 ); p->n_items = 0; p->n_alloc = GROW; p->items = tr_new( void*, p->n_alloc ); @@ -40,7 +40,7 @@ tr_ptrArrayNew( void ) } void -tr_ptrArrayFree( tr_ptrArray_t * t ) +tr_ptrArrayFree( tr_ptrArray * t ) { assert( t != NULL ); assert( t->items != NULL ); @@ -50,32 +50,42 @@ tr_ptrArrayFree( tr_ptrArray_t * t ) } void** -tr_ptrArrayPeek( tr_ptrArray_t * t, int * size ) +tr_ptrArrayPeek( tr_ptrArray * t, int * size ) { *size = t->n_items; return t->items; } +void* +tr_ptrArrayNth( tr_ptrArray* t, int i ) +{ + assert( t != NULL ); + assert( i >= 0 ); + assert( i < t->n_items ); + + return t->items[i]; +} + int -tr_ptrArraySize( const tr_ptrArray_t * t ) +tr_ptrArraySize( const tr_ptrArray * t ) { return t->n_items; } int -tr_ptrArrayEmpty( const tr_ptrArray_t * t ) +tr_ptrArrayEmpty( const tr_ptrArray * t ) { return t->n_items == 0; } void -tr_ptrArrayClear( tr_ptrArray_t * t ) +tr_ptrArrayClear( tr_ptrArray * t ) { t->n_items = 0; } int -tr_ptrArrayInsert( tr_ptrArray_t * t, void * ptr, int pos ) +tr_ptrArrayInsert( tr_ptrArray * t, void * ptr, int pos ) { if( pos<0 || pos>t->n_items ) pos = t->n_items; @@ -95,13 +105,13 @@ tr_ptrArrayInsert( tr_ptrArray_t * t, void * ptr, int pos ) } int -tr_ptrArrayAppend( tr_ptrArray_t * t, void * ptr ) +tr_ptrArrayAppend( tr_ptrArray * t, void * ptr ) { return tr_ptrArrayInsert( t, ptr, -1 ); } void -tr_ptrArrayErase( tr_ptrArray_t * t, int begin, int end ) +tr_ptrArrayErase( tr_ptrArray * t, int begin, int end ) { assert( begin >= 0 ); if( end < 0 ) end = t->n_items; @@ -120,12 +130,11 @@ tr_ptrArrayErase( tr_ptrArray_t * t, int begin, int end ) **/ int -tr_ptrArrayLowerBound( const tr_ptrArray_t * t, - void * ptr, - int compare( const void *,const void * ), - int * exact_match ) +tr_ptrArrayLowerBound( const tr_ptrArray * t, + const void * ptr, + int compare( const void *,const void * ), + int * exact_match ) { - int c = -1; int len = t->n_items; int first = 0; @@ -133,7 +142,7 @@ tr_ptrArrayLowerBound( const tr_ptrArray_t * t, { int half = len / 2; int middle = first + half; - c = compare( t->items[middle], ptr ); + const int c = compare( t->items[middle], ptr ); if( c < 0 ) { first = middle + 1; len = len - half - 1; @@ -154,18 +163,18 @@ tr_ptrArrayLowerBound( const tr_ptrArray_t * t, } int -tr_ptrArrayInsertSorted( tr_ptrArray_t * t, - void * ptr, - int compare(const void*,const void*) ) +tr_ptrArrayInsertSorted( tr_ptrArray * t, + void * ptr, + int compare(const void*,const void*) ) { const int pos = tr_ptrArrayLowerBound( t, ptr, compare, NULL ); return tr_ptrArrayInsert( t, ptr, pos ); } void* -tr_ptrArrayFindSorted( tr_ptrArray_t * t, - void * ptr, - int compare(const void*,const void*) ) +tr_ptrArrayFindSorted( tr_ptrArray * t, + const void * ptr, + int compare(const void*,const void*) ) { int match; const int pos = tr_ptrArrayLowerBound( t, ptr, compare, &match ); @@ -173,9 +182,9 @@ tr_ptrArrayFindSorted( tr_ptrArray_t * t, } void* -tr_ptrArrayRemoveSorted( tr_ptrArray_t * t, - void * ptr, - int compare(const void*,const void*) ) +tr_ptrArrayRemoveSorted( tr_ptrArray * t, + void * ptr, + int compare(const void*,const void*) ) { void * ret = NULL; int match; diff --git a/libtransmission/ptrarray.h b/libtransmission/ptrarray.h index 62d0545d7..5819c3c64 100644 --- a/libtransmission/ptrarray.h +++ b/libtransmission/ptrarray.h @@ -16,24 +16,25 @@ /** * A simple pointer array that resizes itself dynamically. */ -typedef struct tr_ptrArray_s tr_ptrArray_t; +typedef struct tr_ptrArray tr_ptrArray; -tr_ptrArray_t * tr_ptrArrayNew ( void ); -void tr_ptrArrayFree ( tr_ptrArray_t* ); -void** tr_ptrArrayPeek ( tr_ptrArray_t*, int * size ); -void** tr_ptrArrayBase ( tr_ptrArray_t* ); -void tr_ptrArrayClear ( tr_ptrArray_t* ); -int tr_ptrArrayInsert ( tr_ptrArray_t*, void*, int pos ); -int tr_ptrArrayAppend ( tr_ptrArray_t*, void* ); -void tr_ptrArrayErase ( tr_ptrArray_t*, int begin, int end ); -int tr_ptrArraySize ( const tr_ptrArray_t* ); -int tr_ptrArrayEmpty ( const tr_ptrArray_t* ); +tr_ptrArray * tr_ptrArrayNew ( void ); -int tr_ptrArrayInsertSorted( tr_ptrArray_t*, void*, - int compare(const void*,const void*) ); -void* tr_ptrArrayRemoveSorted( tr_ptrArray_t*, void*, - int compare(const void*,const void*) ); -void* tr_ptrArrayFindSorted ( tr_ptrArray_t*, void*, - int compare(const void*,const void*) ); +void tr_ptrArrayFree ( tr_ptrArray* ); +void* tr_ptrArrayNth ( tr_ptrArray*, int n ); +void** tr_ptrArrayPeek ( tr_ptrArray*, int * size ); +void** tr_ptrArrayBase ( tr_ptrArray* ); +void tr_ptrArrayClear ( tr_ptrArray* ); +int tr_ptrArrayInsert ( tr_ptrArray*, void*, int pos ); +int tr_ptrArrayAppend ( tr_ptrArray*, void* ); +void tr_ptrArrayErase ( tr_ptrArray*, int begin, int end ); +int tr_ptrArraySize ( const tr_ptrArray* ); +int tr_ptrArrayEmpty ( const tr_ptrArray* ); +int tr_ptrArrayInsertSorted ( tr_ptrArray*, void*, + int compare(const void*,const void*) ); +void* tr_ptrArrayRemoveSorted ( tr_ptrArray*, void*, + int compare(const void*,const void*) ); +void* tr_ptrArrayFindSorted ( tr_ptrArray*, const void*, + int compare(const void*,const void*) ); #endif diff --git a/libtransmission/publish.c b/libtransmission/publish.c index 78f4d788c..98bf49151 100644 --- a/libtransmission/publish.c +++ b/libtransmission/publish.c @@ -10,6 +10,7 @@ * $Id$ */ +#include #include "list.h" #include "publish.h" #include "utils.h" @@ -22,7 +23,7 @@ struct tr_publisher_node struct tr_publisher_s { - tr_list_t * list; + tr_list * list; }; tr_publisher_t* @@ -32,10 +33,14 @@ tr_publisherNew( void ) } void -tr_publisherFree( tr_publisher_t * p ) +tr_publisherFree( tr_publisher_t ** p ) { - tr_list_free( &p->list ); - tr_free( p ); + assert( p != NULL ); + assert( *p != NULL ); + + tr_list_free( &(*p)->list ); + tr_free( *p ); + *p = NULL; } tr_publisher_tag @@ -63,10 +68,10 @@ tr_publisherPublish( tr_publisher_t * p, void * source, void * event ) { - tr_list_t * walk; + tr_list * walk; for( walk=p->list; walk!=NULL; ) { - tr_list_t * next = walk->next; + tr_list * next = walk->next; struct tr_publisher_node * node = (struct tr_publisher_node*)walk->data; (node->func)(source, event, node->user_data); walk = next; diff --git a/libtransmission/publish.h b/libtransmission/publish.h index 4b3814735..f8e76a7f4 100644 --- a/libtransmission/publish.h +++ b/libtransmission/publish.h @@ -42,7 +42,7 @@ void tr_publisherUnsubscribe ( tr_publisher_t * publisher, tr_publisher_t * tr_publisherNew ( void ); -void tr_publisherFree ( tr_publisher_t * publisher ); +void tr_publisherFree ( tr_publisher_t ** publisher ); void tr_publisherPublish ( tr_publisher_t * publisher, void * source, diff --git a/libtransmission/ratecontrol.c b/libtransmission/ratecontrol.c index c69b3c852..6660b208b 100644 --- a/libtransmission/ratecontrol.c +++ b/libtransmission/ratecontrol.c @@ -38,13 +38,13 @@ typedef struct { uint64_t date; - int size; + uint64_t size; } tr_transfer_t; -struct tr_ratecontrol_s +struct tr_ratecontrol { - tr_lock_t * lock; + tr_lock * lock; int limit; int newest; tr_transfer_t transfers[HISTORY_SIZE]; @@ -52,14 +52,14 @@ struct tr_ratecontrol_s /* return the xfer rate over the last `interval' seconds in KiB/sec */ static float -rateForInterval( const tr_ratecontrol_t * r, int interval_msec ) +rateForInterval( const tr_ratecontrol * r, int interval_msec ) { uint64_t bytes = 0; - const uint64_t now = tr_date (); + const uint64_t cutoff = tr_date () - interval_msec; int i = r->newest; for( ;; ) { - if( r->transfers[i].date + interval_msec < now ) + if( r->transfers[i].date < cutoff ) break; bytes += r->transfers[i].size; @@ -75,17 +75,17 @@ rateForInterval( const tr_ratecontrol_t * r, int interval_msec ) **** ***/ -tr_ratecontrol_t* +tr_ratecontrol* tr_rcInit( void ) { - tr_ratecontrol_t * r = tr_new0( tr_ratecontrol_t, 1 ); + tr_ratecontrol * r = tr_new0( tr_ratecontrol, 1 ); r->limit = 0; r->lock = tr_lockNew( ); return r; } void -tr_rcClose( tr_ratecontrol_t * r ) +tr_rcClose( tr_ratecontrol * r ) { tr_rcReset( r ); tr_lockFree( r->lock ); @@ -97,26 +97,34 @@ tr_rcClose( tr_ratecontrol_t * r ) ***/ int -tr_rcCanTransfer( const tr_ratecontrol_t * r ) +tr_rcCanTransfer( const tr_ratecontrol * r ) { int ret; - tr_lockLock( (tr_lock_t*)r->lock ); - ret = rateForInterval( r, SHORT_INTERVAL_MSEC ) < r->limit; + if( r == NULL ) + ret = 0; + else { + tr_lockLock( (tr_lock*)r->lock ); + ret = rateForInterval( r, SHORT_INTERVAL_MSEC ) < r->limit; + tr_lockUnlock( (tr_lock*)r->lock ); + } - tr_lockUnlock( (tr_lock_t*)r->lock ); return ret; } float -tr_rcRate( const tr_ratecontrol_t * r ) +tr_rcRate( const tr_ratecontrol * r ) { float ret; - tr_lockLock( (tr_lock_t*)r->lock ); - ret = rateForInterval( r, LONG_INTERVAL_MSEC ); + if( r == NULL ) + ret = 0.0f; + else { + tr_lockLock( (tr_lock*)r->lock ); + ret = rateForInterval( r, LONG_INTERVAL_MSEC ); + tr_lockUnlock( (tr_lock*)r->lock ); + } - tr_lockUnlock( (tr_lock_t*)r->lock ); return ret; } @@ -125,14 +133,14 @@ tr_rcRate( const tr_ratecontrol_t * r ) ***/ void -tr_rcTransferred( tr_ratecontrol_t * r, int size ) +tr_rcTransferred( tr_ratecontrol * r, size_t size ) { uint64_t now; if( size < 100 ) /* don't count small messages */ return; - tr_lockLock( (tr_lock_t*)r->lock ); + tr_lockLock( (tr_lock*)r->lock ); now = tr_date (); if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now ) @@ -143,28 +151,28 @@ tr_rcTransferred( tr_ratecontrol_t * r, int size ) r->transfers[r->newest].size = size; } - tr_lockUnlock( (tr_lock_t*)r->lock ); + tr_lockUnlock( (tr_lock*)r->lock ); } void -tr_rcReset( tr_ratecontrol_t * r ) +tr_rcReset( tr_ratecontrol * r ) { - tr_lockLock( (tr_lock_t*)r->lock ); + tr_lockLock( (tr_lock*)r->lock ); r->newest = 0; memset( r->transfers, 0, sizeof(tr_transfer_t) * HISTORY_SIZE ); - tr_lockUnlock( (tr_lock_t*)r->lock ); + tr_lockUnlock( (tr_lock*)r->lock ); } void -tr_rcSetLimit( tr_ratecontrol_t * r, int limit ) +tr_rcSetLimit( tr_ratecontrol * r, int limit ) { - tr_lockLock( (tr_lock_t*)r->lock ); + tr_lockLock( (tr_lock*)r->lock ); r->limit = limit; - tr_lockUnlock( (tr_lock_t*)r->lock ); + tr_lockUnlock( (tr_lock*)r->lock ); } int -tr_rcGetLimit( const tr_ratecontrol_t * r ) +tr_rcGetLimit( const tr_ratecontrol * r ) { return r->limit; } diff --git a/libtransmission/ratecontrol.h b/libtransmission/ratecontrol.h index cc2495a28..fe9e05f64 100644 --- a/libtransmission/ratecontrol.h +++ b/libtransmission/ratecontrol.h @@ -25,15 +25,15 @@ #ifndef _TR_RATECONTROL_H_ #define _TR_RATECONTROL_H_ -typedef struct tr_ratecontrol_s tr_ratecontrol_t; +typedef struct tr_ratecontrol tr_ratecontrol; -tr_ratecontrol_t * tr_rcInit( void ); -void tr_rcSetLimit( tr_ratecontrol_t *, int ); -int tr_rcGetLimit( const tr_ratecontrol_t * ); -int tr_rcCanTransfer( const tr_ratecontrol_t * ); -void tr_rcTransferred( tr_ratecontrol_t *, int ); -float tr_rcRate( const tr_ratecontrol_t * ); -void tr_rcReset( tr_ratecontrol_t * ); -void tr_rcClose( tr_ratecontrol_t * ); +tr_ratecontrol * tr_rcInit( void ); +void tr_rcSetLimit( tr_ratecontrol *, int ); +int tr_rcGetLimit( const tr_ratecontrol * ); +int tr_rcCanTransfer( const tr_ratecontrol * ); +void tr_rcTransferred( tr_ratecontrol *, size_t byteCount ); +float tr_rcRate( const tr_ratecontrol * ); +void tr_rcReset( tr_ratecontrol * ); +void tr_rcClose( tr_ratecontrol * ); #endif diff --git a/libtransmission/sha1.c b/libtransmission/sha1.c deleted file mode 100644 index a44b2d788..000000000 --- a/libtransmission/sha1.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - sha1.c: Implementation of SHA-1 Secure Hash Algorithm-1 - - Based upon: NIST FIPS180-1 Secure Hash Algorithm-1 - http://www.itl.nist.gov/fipspubs/fip180-1.htm - - Non-official Japanese Translation by HIRATA Yasuyuki: - http://yasu.asuka.net/translations/SHA-1.html - - Copyright (C) 2002 vi@nwr.jp. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgement in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as beging the original software. - 3. This notice may not be removed or altered from any source distribution. - - Note: - The copyright notice above is copied from md5.h by L. Peter Deutsch - . Thank him since I'm not a good speaker of English. :) - - $Id$ - - */ - -#include -#include "sha1.h" - -#if !defined(HAVE_OPENSSL) && !defined(HAVE_LIBSSL) - -#define BITS 8 -#define INLINE inline - -struct sha1_state_s -{ - sha1_byte_t sha1_buf[64]; /* 512 bits */ - int sha1_count; /* How many bytes are used */ - sha1_word_t sha1_size1; /* Length counter Lower Word */ - sha1_word_t sha1_size2; /* Length counter Upper Word */ - sha1_word_t sha1_h[5]; /* Hash output */ -}; - -/* - * Packing bytes to a word - * - * Should not assume p is aligned to word boundary - */ -static INLINE sha1_word_t packup(sha1_byte_t *p) -{ - /* Portable, but slow */ - return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3] << 0; -} - -/* - * Unpacking a word to bytes - * - * Should not assume p is aligned to word boundary - */ -static void unpackup(sha1_byte_t *p, sha1_word_t q) -{ - p[0] = (q >> 24) & 0xff; - p[1] = (q >> 16) & 0xff; - p[2] = (q >> 8) & 0xff; - p[3] = (q >> 0) & 0xff; -} - -/* - * Processing a block - */ -static inline void sha1_update_now(sha1_state_t *pms, sha1_byte_t *bp) -{ - sha1_word_t tmp, a, b, c, d, e, w[16+16]; - int i, s; - - /* pack 64 bytes into 16 words */ - for(i = 0; i < 16; i++) { - w[i] = packup(bp + i * sizeof(sha1_word_t)); - } - memcpy(w + 16, w + 0, sizeof(sha1_word_t) * 16); - - a = pms->sha1_h[0], b = pms->sha1_h[1], c = pms->sha1_h[2], d = pms->sha1_h[3], e = pms->sha1_h[4]; - -#define rot(x,n) (((x) << n) | ((x) >> (32-n))) -#define f0(b, c, d) ((b&c)|(~b&d)) -#define f1(b, c, d) (b^c^d) -#define f2(b, c, d) ((b&c)|(b&d)|(c&d)) -#define f3(b, c, d) (b^c^d) -#define k0 0x5a827999 -#define k1 0x6ed9eba1 -#define k2 0x8f1bbcdc -#define k3 0xca62c1d6 - - /* t=0-15 */ - s = 0; - for(i = 0; i < 16; i++) { - tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0; - e = d; d = c; c = rot(b, 30); b = a; a = tmp; - s = (s + 1) % 16; - } - - /* t=16-19 */ - for(i = 16; i < 20; i++) { - w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1); - w[s+16] = w[s]; - tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0; - e = d; d = c; c = rot(b, 30); b = a; a = tmp; - s = (s + 1) % 16; - } - - /* t=20-39 */ - for(i = 0; i < 20; i++) { - w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1); - w[s+16] = w[s]; - tmp = rot(a, 5) + f1(b, c, d) + e + w[s] + k1; - e = d; d = c; c = rot(b, 30); b = a; a = tmp; - s = (s + 1) % 16; - } - - /* t=40-59 */ - for(i = 0; i < 20; i++) { - w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1); - w[s+16] = w[s]; - tmp = rot(a, 5) + f2(b, c, d) + e + w[s] + k2; - e = d; d = c; c = rot(b, 30); b = a; a = tmp; - s = (s + 1) % 16; - } - - /* t=60-79 */ - for(i = 0; i < 20; i++) { - w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1); - w[s+16] = w[s]; - tmp = rot(a, 5) + f3(b, c, d) + e + w[s] + k3; - e = d; d = c; c = rot(b, 30); b = a; a = tmp; - s = (s + 1) % 16; - } - - pms->sha1_h[0] += a, pms->sha1_h[1] += b, pms->sha1_h[2] += c, pms->sha1_h[3] += d, pms->sha1_h[4] += e; -} - -/* - * Increment sha1_size1, sha1_size2 field of sha1_state_t - */ -static INLINE void incr(sha1_state_t *pms, int v) -{ - sha1_word_t q; - - q = pms->sha1_size1 + v * BITS; - if(q < pms->sha1_size1) { - pms->sha1_size2++; - } - pms->sha1_size1 = q; -} - -/* - * Initialize sha1_state_t as FIPS specifies - */ -void sha1_init(sha1_state_t *pms) -{ - memset(pms, 0, sizeof(*pms)); - pms->sha1_h[0] = 0x67452301; /* Initialize H[0]-H[4] */ - pms->sha1_h[1] = 0xEFCDAB89; - pms->sha1_h[2] = 0x98BADCFE; - pms->sha1_h[3] = 0x10325476; - pms->sha1_h[4] = 0xC3D2E1F0; -} - -/* - * Fill block and update output when needed - */ -void sha1_update(sha1_state_t *pms, sha1_byte_t *bufp, int length) -{ - /* Is the buffer partially filled? */ - if(pms->sha1_count != 0) { - if(pms->sha1_count + length >= (signed) sizeof(pms->sha1_buf)) { /* buffer is filled enough */ - int fil = sizeof(pms->sha1_buf) - pms->sha1_count; /* length to copy */ - - memcpy(pms->sha1_buf + pms->sha1_count, bufp, fil); - sha1_update_now(pms, pms->sha1_buf); - length -= fil; - bufp += fil; - pms->sha1_count = 0; - incr(pms, fil); - } else { - memcpy(pms->sha1_buf + pms->sha1_count, bufp, length); - pms->sha1_count += length; - incr(pms, length); - return; - } - } - - /* Loop to update state */ - for(;;) { - if(length < (signed) sizeof(pms->sha1_buf)) { /* Short to fill up the buffer */ - if(length) { - memcpy(pms->sha1_buf, bufp, length); - } - pms->sha1_count = length; - incr(pms, length); - break; - } - sha1_update_now(pms, bufp); - length -= sizeof(pms->sha1_buf); - bufp += sizeof(pms->sha1_buf); - incr(pms, sizeof(pms->sha1_buf)); - } -} - -void sha1_finish(sha1_state_t *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE]) -{ - int i; - sha1_byte_t buf[1]; - - /* fill a bit */ - buf[0] = 0x80; - sha1_update(pms, buf, 1); - - /* Decrement sha1_size1, sha1_size2 */ - if((pms->sha1_size1 -= BITS) == 0) { - pms->sha1_size2--; - } - - /* fill zeros */ - if(pms->sha1_count > (signed) (sizeof(pms->sha1_buf) - 2 * sizeof(sha1_word_t))) { - memset(pms->sha1_buf + pms->sha1_count, 0, sizeof(pms->sha1_buf) - pms->sha1_count); - sha1_update_now(pms, pms->sha1_buf); - pms->sha1_count = 0; - } - memset(pms->sha1_buf + pms->sha1_count, 0, - sizeof(pms->sha1_buf) - pms->sha1_count - sizeof(sha1_word_t) * 2); - - /* fill last length */ - unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 2, pms->sha1_size2); - unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 1, pms->sha1_size1); - - /* final update */ - sha1_update_now(pms, pms->sha1_buf); - - /* move hash value to output byte array */ - for(i = 0; i < (signed) (sizeof(pms->sha1_h)/sizeof(sha1_word_t)); i++) { - unpackup(output + i * sizeof(sha1_word_t), pms->sha1_h[i]); - } -} - -void -tr_sha1( const void * input_buffer, int length, unsigned char * output) -{ - sha1_state_t pms; - sha1_init( &pms ); - sha1_update( &pms, (sha1_byte_t *) input_buffer, length ); - sha1_finish( &pms, (sha1_byte_t *) output ); -} - -#endif diff --git a/libtransmission/sha1.h b/libtransmission/sha1.h deleted file mode 100644 index ca2966113..000000000 --- a/libtransmission/sha1.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - sha1.h: Implementation of SHA-1 Secure Hash Algorithm-1 - - Based upon: NIST FIPS180-1 Secure Hash Algorithm-1 - http://www.itl.nist.gov/fipspubs/fip180-1.htm - - Non-official Japanese Translation by HIRATA Yasuyuki: - http://yasu.asuka.net/translations/SHA-1.html - - Copyright (C) 2002 vi@nwr.jp. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgement in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as beging the original software. - 3. This notice may not be removed or altered from any source distribution. - - Note: - The copyright notice above is copied from md5.h by L. Petet Deutsch - . Thank him since I'm not a good speaker of English. :) - - $Id$ - */ -#ifndef SHA1_H -#define SHA1_H - -/* We use OpenSSL whenever possible, since it is likely to be more - optimized and it is ok to use it with a MIT-licensed application. - Otherwise, we use the included implementation by vi@nwr.jp. */ -#if defined(HAVE_OPENSSL) || defined(HAVE_LIBSSL) - - #undef SHA_DIGEST_LENGTH - #include - -#else - - #include - #define SHA1_OUTPUT_SIZE 20 /* in bytes */ - - typedef struct sha1_state_s sha1_state_t; - typedef uint32_t sha1_word_t; /* 32bits unsigned integer */ - typedef uint8_t sha1_byte_t; /* 8bits unsigned integer */ - - /* Initialize SHA-1 algorithm */ - void sha1_init(sha1_state_t *pms); - - /* Append a string to SHA-1 algorithm */ - void sha1_update(sha1_state_t *pms, sha1_byte_t *input_buffer, int length); - - /* Finish the SHA-1 algorithm and return the hash */ - void sha1_finish(sha1_state_t *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE]); - - /* Convenience version of the above */ - void tr_sha1( const void * input_buffer, int length, unsigned char * output); - - #define SHA1(in,inlen,out) tr_sha1(in,inlen,out) - -#endif /* ifndef HAVE_OPENSSL, HAVE_LIBSSL */ - -#endif /* #ifndef SHA1_H */ diff --git a/libtransmission/shared.c b/libtransmission/shared.c index c202c5986..4a7b89321 100644 --- a/libtransmission/shared.c +++ b/libtransmission/shared.c @@ -25,53 +25,44 @@ #include #include #include +#include #include #include "transmission.h" -#include "choking.h" +#include "handshake.h" #include "natpmp.h" #include "net.h" -#include "peer.h" +#include "peer-io.h" +#include "peer-mgr.h" #include "platform.h" #include "shared.h" +#include "trevent.h" #include "upnp.h" #include "utils.h" -/* Maximum number of peers that we keep in our local list */ -/* This is an arbitrary number, but it seems to work well */ -#define MAX_PEER_COUNT 128 - -struct tr_shared_s +struct tr_shared { - tr_handle_t * h; - volatile int die; - tr_thread_t * thread; - tr_lock_t * lock; + tr_handle * h; + tr_lock * lock; + tr_timer * pulseTimer; /* Incoming connections */ - int publicPort; - int bindPort; - int bindSocket; - int peerCount; - tr_peer_t * peers[MAX_PEER_COUNT]; + int publicPort; + int bindPort; + int bindSocket; /* NAT-PMP/UPnP */ tr_natpmp_t * natpmp; tr_upnp_t * upnp; - - /* Choking */ - tr_choking_t * choking; }; /*********************************************************************** * Local prototypes **********************************************************************/ -static void SharedLoop( void * ); -static void SetPublicPort( tr_shared_t *, int ); -static void AcceptPeers( tr_shared_t * ); -static void ReadPeers( tr_shared_t * ); -static void DispatchPeers( tr_shared_t * ); +static int SharedLoop( void * ); +static void SetPublicPort( tr_shared *, int ); +static void AcceptPeers( tr_shared * ); /*********************************************************************** @@ -79,9 +70,9 @@ static void DispatchPeers( tr_shared_t * ); *********************************************************************** * **********************************************************************/ -tr_shared_t * tr_sharedInit( tr_handle_t * h ) +tr_shared * tr_sharedInit( tr_handle * h ) { - tr_shared_t * s = calloc( 1, sizeof( tr_shared_t ) ); + tr_shared * s = calloc( 1, sizeof( tr_shared ) ); s->h = h; s->lock = tr_lockNew( ); @@ -90,9 +81,7 @@ tr_shared_t * tr_sharedInit( tr_handle_t * h ) s->bindSocket = -1; s->natpmp = tr_natpmpInit(); s->upnp = tr_upnpInit(); - s->choking = tr_chokingInit( h ); - s->die = 0; - s->thread = tr_threadNew( SharedLoop, s, "shared" ); + s->pulseTimer = tr_timerNew( h, SharedLoop, s, 250 ); return s; } @@ -102,40 +91,27 @@ tr_shared_t * tr_sharedInit( tr_handle_t * h ) *********************************************************************** * **********************************************************************/ -void tr_sharedClose( tr_shared_t * s ) +void tr_sharedClose( tr_shared * s ) { - int ii; +fprintf( stderr, "deleting pulse tag\n" ); + tr_timerFree( &s->pulseTimer ); - /* Stop the thread */ - s->die = 1; - tr_threadJoin( s->thread ); - - /* Clean up */ - for( ii = 0; ii < s->peerCount; ii++ ) - { - tr_peerDestroy( s->peers[ii] ); - } - if( s->bindSocket > -1 ) - { - tr_netClose( s->bindSocket ); - } + tr_netClose( s->bindSocket ); tr_lockFree( s->lock ); tr_natpmpClose( s->natpmp ); tr_upnpClose( s->upnp ); - tr_chokingClose( s->choking ); free( s ); } -/*********************************************************************** - * tr_sharedLock, tr_sharedUnlock - *********************************************************************** - * - **********************************************************************/ -void tr_sharedLock( tr_shared_t * s ) +/** +*** +**/ + +void tr_sharedLock( tr_shared * s ) { tr_lockLock( s->lock ); } -void tr_sharedUnlock( tr_shared_t * s ) +void tr_sharedUnlock( tr_shared * s ) { tr_lockUnlock( s->lock ); } @@ -145,7 +121,7 @@ void tr_sharedUnlock( tr_shared_t * s ) *********************************************************************** * **********************************************************************/ -void tr_sharedSetPort( tr_shared_t * s, int port ) +void tr_sharedSetPort( tr_shared * s, int port ) { #ifdef BEOS_NETSERVER /* BeOS net_server seems to be unable to set incoming connections @@ -196,12 +172,7 @@ void tr_sharedSetPort( tr_shared_t * s, int port ) tr_sharedUnlock( s ); } -/*********************************************************************** - * tr_sharedGetPublicPort - *********************************************************************** - * - **********************************************************************/ -int tr_sharedGetPublicPort( tr_shared_t * s ) +int tr_sharedGetPublicPort( tr_shared * s ) { return s->publicPort; } @@ -211,7 +182,7 @@ int tr_sharedGetPublicPort( tr_shared_t * s ) *********************************************************************** * **********************************************************************/ -void tr_sharedTraversalEnable( tr_shared_t * s, int enable ) +void tr_sharedTraversalEnable( tr_shared * s, int enable ) { if( enable ) { @@ -225,7 +196,7 @@ void tr_sharedTraversalEnable( tr_shared_t * s, int enable ) } } -int tr_sharedTraversalStatus( tr_shared_t * s ) +int tr_sharedTraversalStatus( tr_shared * s ) { int statuses[] = { TR_NAT_TRAVERSAL_MAPPED, @@ -255,14 +226,6 @@ int tr_sharedTraversalStatus( tr_shared_t * s ) } -/*********************************************************************** - * tr_sharedSetLimit - **********************************************************************/ -void tr_sharedSetLimit( tr_shared_t * s, int limit ) -{ - tr_chokingSetLimit( s->choking, limit ); -} - /*********************************************************************** * Local functions @@ -271,61 +234,36 @@ void tr_sharedSetLimit( tr_shared_t * s, int limit ) /*********************************************************************** * SharedLoop **********************************************************************/ -static void SharedLoop( void * _s ) +static int +SharedLoop( void * vs ) { - tr_shared_t * s = _s; - uint64_t date1, date2, lastchoke = 0; - int newPort; + int newPort; + tr_shared * s = vs; tr_sharedLock( s ); - while( !s->die ) - { - date1 = tr_date(); + /* NAT-PMP and UPnP pulses */ + newPort = -1; + tr_natpmpPulse( s->natpmp, &newPort ); + if( 0 < newPort && newPort != s->publicPort ) + SetPublicPort( s, newPort ); + tr_upnpPulse( s->upnp ); - /* NAT-PMP and UPnP pulses */ - newPort = -1; - tr_natpmpPulse( s->natpmp, &newPort ); - if( 0 < newPort && newPort != s->publicPort ) - { - SetPublicPort( s, newPort ); - } - tr_upnpPulse( s->upnp ); - - /* Handle incoming connections */ - AcceptPeers( s ); - ReadPeers( s ); - DispatchPeers( s ); - - /* Update choking every second */ - if( date1 > lastchoke + 1000 ) - { - tr_chokingPulse( s->choking ); - lastchoke = date1; - } - - tr_swiftPulse ( s->h ); - - /* Wait up to 20 ms */ - date2 = tr_date(); - if( date2 < date1 + 20 ) - { - tr_sharedUnlock( s ); - tr_wait( date1 + 20 - date2 ); - tr_sharedLock( s ); - } - } + /* Handle incoming connections */ + AcceptPeers( s ); tr_sharedUnlock( s ); + + return TRUE; } /*********************************************************************** * SetPublicPort **********************************************************************/ -static void SetPublicPort( tr_shared_t * s, int port ) +static void SetPublicPort( tr_shared * s, int port ) { - tr_handle_t * h = s->h; - tr_torrent_t * tor; + tr_handle * h = s->h; + tr_torrent * tor; s->publicPort = port; @@ -338,106 +276,22 @@ static void SetPublicPort( tr_shared_t * s, int port ) *********************************************************************** * Check incoming connections and add the peers to our local list **********************************************************************/ -static void AcceptPeers( tr_shared_t * s ) -{ - int socket; - struct in_addr addr; +static void +AcceptPeers( tr_shared * s ) +{ for( ;; ) { - if( s->bindSocket < 0 || s->peerCount >= MAX_PEER_COUNT ) - { + int socket; + struct in_addr addr; + + if( s->bindSocket < 0 || !tr_peerMgrIsAcceptingConnections( s->h->peerMgr ) ) break; - } socket = tr_netAccept( s->bindSocket, &addr, NULL ); if( socket < 0 ) - { break; - } - s->peers[s->peerCount++] = tr_peerInit( &addr, 0, socket, - TR_PEER_FROM_INCOMING ); + + tr_peerMgrAddIncoming( s->h->peerMgr, &addr, socket ); } } - -/*********************************************************************** - * ReadPeers - *********************************************************************** - * Try to read handshakes - **********************************************************************/ -static void ReadPeers( tr_shared_t * s ) -{ - int ii; - - for( ii = 0; ii < s->peerCount; ) - { - if( tr_peerRead( s->peers[ii] ) ) - { - tr_peerDestroy( s->peers[ii] ); - s->peerCount--; - memmove( &s->peers[ii], &s->peers[ii+1], - ( s->peerCount - ii ) * sizeof( tr_peer_t * ) ); - continue; - } - ii++; - } -} - -/*********************************************************************** - * DispatchPeers - *********************************************************************** - * If we got a handshake, try to find the torrent for this peer - **********************************************************************/ -static void DispatchPeers( tr_shared_t * s ) -{ - tr_handle_t * h = s->h; - int ii; - const uint64_t now = tr_date(); - - for( ii = 0; ii < s->peerCount; ) - { - const uint8_t * hash = tr_peerHash( s->peers[ii] ); - - if( !hash && now > tr_peerDate( s->peers[ii] ) + 10000 ) - { - /* 10 seconds and no handshake, drop it */ - tr_peerDestroy( s->peers[ii] ); - goto removePeer; - } - if( hash ) - { - tr_torrent_t * tor; - - for( tor = h->torrentList; tor; tor = tor->next ) - { - tr_torrentWriterLock( tor ); - if( tor->runStatus != TR_RUN_RUNNING ) - { - tr_torrentWriterUnlock( tor ); - continue; - } - - if( !memcmp( tor->info.hash, hash, SHA_DIGEST_LENGTH ) ) - { - /* Found it! */ - tr_torrentAttachPeer( tor, s->peers[ii] ); - tr_torrentWriterUnlock( tor ); - goto removePeer; - } - tr_torrentWriterUnlock( tor ); - } - - /* Couldn't find a torrent, we probably removed it */ - tr_peerDestroy( s->peers[ii] ); - goto removePeer; - } - ii++; - continue; - -removePeer: - s->peerCount--; - memmove( &s->peers[ii], &s->peers[ii+1], - ( s->peerCount - ii ) * sizeof( tr_peer_t * ) ); - } -} - diff --git a/libtransmission/shared.h b/libtransmission/shared.h index b98ef2dee..3ea7c3c9d 100644 --- a/libtransmission/shared.h +++ b/libtransmission/shared.h @@ -27,7 +27,7 @@ #include "transmission.h" -typedef struct tr_shared_s tr_shared_t; +typedef struct tr_shared tr_shared; /*********************************************************************** * tr_sharedInit, tr_sharedClose @@ -35,8 +35,8 @@ typedef struct tr_shared_s tr_shared_t; * Starts / stops a thread to handle running things that are shared * among the torrents: NAT-PMP/UPnP, incoming connections, peer choking **********************************************************************/ -tr_shared_t * tr_sharedInit ( tr_handle_t * ); -void tr_sharedClose ( tr_shared_t * ); +tr_shared * tr_sharedInit ( tr_handle * ); +void tr_sharedClose ( tr_shared * ); /*********************************************************************** * tr_sharedLock, tr_sharedUnlock @@ -44,8 +44,8 @@ void tr_sharedClose ( tr_shared_t * ); * Gets / releases exclusive access to ressources used by the shared * thread **********************************************************************/ -void tr_sharedLock ( tr_shared_t * ); -void tr_sharedUnlock ( tr_shared_t * ); +void tr_sharedLock ( tr_shared * ); +void tr_sharedUnlock ( tr_shared * ); /*********************************************************************** * tr_sharedSetPort @@ -53,8 +53,8 @@ void tr_sharedUnlock ( tr_shared_t * ); * Changes the port for incoming connections. tr_sharedGetPublicPort * should be called with the shared lock held. **********************************************************************/ -void tr_sharedSetPort ( tr_shared_t *, int port ); -int tr_sharedGetPublicPort ( tr_shared_t * s ); +void tr_sharedSetPort ( tr_shared *, int port ); +int tr_sharedGetPublicPort ( tr_shared * s ); /*********************************************************************** * tr_sharedTraversalEnable, tr_sharedTraversalStatus @@ -62,15 +62,9 @@ int tr_sharedGetPublicPort ( tr_shared_t * s ); * Enables/disables and retrieves the status of NAT traversal. Should * be called with the shared lock held. **********************************************************************/ -void tr_sharedTraversalEnable ( tr_shared_t *, int enable ); -int tr_sharedTraversalStatus ( tr_shared_t * ); +void tr_sharedTraversalEnable ( tr_shared *, int enable ); +int tr_sharedTraversalStatus ( tr_shared * ); -/*********************************************************************** - * tr_sharedSetLimit - *********************************************************************** - * - **********************************************************************/ -void tr_sharedSetLimit ( tr_shared_t *, int limit ); #endif diff --git a/libtransmission/timer.c b/libtransmission/timer.c deleted file mode 100644 index 0895394e3..000000000 --- a/libtransmission/timer.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file Copyright (C) 2007 Charles Kerr - * - * This file is licensed by the GPL version 2. Works owned by the - * Transmission project are granted a special exemption to clause 2(b) - * so that the bulk of its code can remain under the MIT license. - * This exemption does not extend to derived works not owned by - * the Transmission project. - * - * $Id$ - */ - -#include - -#include /* u_char for event.h */ -#include - -#include "transmission.h" -#include "trevent.h" -#include "timer.h" -#include "utils.h" - -typedef int tr_timer_func ( void * user_data ); -typedef void tr_data_free_func( void * user_data ); - -/*** -**** -***/ - -struct timer_node -{ - tr_handle_t * handle; - struct event * event; - tr_timer_func * func; - void * user_data; - tr_data_free_func * free_func; - struct timeval tv; - uint8_t doFree; -}; - -void -tr_timerFree( struct timer_node ** node ) -{ - if( *node ) - { - (*node)->doFree = 1; - *node = NULL; - } -} - -static void -timerCB( int fd UNUSED, short event UNUSED, void * arg ) -{ - struct timer_node * node = (struct timer_node *) arg; - - if( !node->doFree ) - node->doFree = !(node->func)(node->user_data); - - if( !node->doFree ) - tr_event_add( node->handle, node->event, &node->tv ); - else { - if( node->free_func != NULL ) - (node->free_func)( node->user_data ); - tr_event_del( node->handle, node->event ); - tr_free( node ); - } -} - - -tr_timer_tag -tr_timerNew( tr_handle_t * handle, - tr_timer_func func, - void * user_data, - tr_data_free_func free_func, - int timeout_milliseconds ) -{ - struct timer_node * node; - - assert( func != NULL ); - assert( timeout_milliseconds >= 0 ); - - node = tr_new( struct timer_node, 1 ); - node->handle = handle; - node->event = tr_new0( struct event, 1 ); - node->func = func; - node->user_data = user_data; - node->free_func = free_func; - node->doFree = 0; - node->tv = timevalMsec ( timeout_milliseconds ); - timeout_set( node->event, timerCB, node ); - tr_event_add( handle, node->event, &node->tv ); - return node; -} diff --git a/libtransmission/timer.h b/libtransmission/timer.h deleted file mode 100644 index 0810bbec7..000000000 --- a/libtransmission/timer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file Copyright (C) 2007 Charles Kerr - * - * This file is licensed by the GPL version 2. Works owned by the - * Transmission project are granted a special exemption to clause 2(b) - * so that the bulk of its code can remain under the MIT license. - * This exemption does not extend to derived works not owned by - * the Transmission project. - * - * $Id$ - */ - -#ifndef _TR_TIMER_H_ -#define _TR_TIMER_H_ - -typedef struct timer_node * tr_timer_tag; - -/** - * Calls timer_func(user_data) after the specified interval. - * The timer is freed if timer_func returns zero. - * Otherwise, it's called again after the same interval. - * - * If free_func is non-NULL, free_func(user_data) is called - * by the timer when it's freed (either from timer_func returning - * zero or from a client call to tr_timerFree). This is useful - * if user_data has resources that need to be freed. - */ -tr_timer_tag tr_timerNew( struct tr_handle_s * handle, - int timer_func( void * user_data ), - void * user_data, - void free_func( void * user_data ), - int timeout_milliseconds ); - -/** - * Frees a timer and sets its tag to NULL. - */ -void tr_timerFree( tr_timer_tag * tag ); - - -#endif diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index 4a5117645..9af21d22c 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -31,44 +31,73 @@ #include "transmission.h" #include "completion.h" +#include "crypto.h" /* for tr_sha1 */ #include "fastresume.h" +#include "handshake.h" #include "inout.h" #include "metainfo.h" #include "net.h" /* tr_netNtop */ #include "peer.h" +#include "peer-mgr.h" #include "platform.h" #include "ratecontrol.h" #include "shared.h" #include "tracker.h" #include "trcompat.h" /* for strlcpy */ +#include "trevent.h" #include "utils.h" +/*** +**** +***/ + +int +tr_torrentExists( tr_handle * handle, + const uint8_t * torrentHash ) +{ + return tr_torrentFindFromHash( handle, torrentHash ) != NULL; +} + +tr_torrent* +tr_torrentFindFromHash( tr_handle * handle, + const uint8_t * torrentHash ) +{ + tr_torrent * tor; + + for( tor = handle->torrentList; tor; tor = tor->next ) + if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) ) + return tor; + + return NULL; +} + +tr_torrent* +tr_torrentFindFromObfuscatedHash( tr_handle * handle, + const uint8_t * obfuscatedTorrentHash ) +{ + tr_torrent * tor; + + for( tor = handle->torrentList; tor; tor = tor->next ) + if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) ) + return tor; + + return NULL; +} + /*** **** LOCKS ***/ void -tr_torrentReaderLock( const tr_torrent_t * tor ) +tr_torrentLock( const tr_torrent * tor ) { - tr_rwReaderLock ( (tr_rwlock_t*)tor->lock ); + tr_lockLock ( (tr_lock*)tor->lock ); } void -tr_torrentReaderUnlock( const tr_torrent_t * tor ) +tr_torrentUnlock( const tr_torrent * tor ) { - tr_rwReaderUnlock ( (tr_rwlock_t*)tor->lock ); -} - -void -tr_torrentWriterLock( tr_torrent_t * tor ) -{ - tr_rwWriterLock ( tor->lock ); -} - -void -tr_torrentWriterUnlock( tr_torrent_t * tor ) -{ - tr_rwWriterUnlock ( tor->lock ); + tr_lockUnlock ( (tr_lock*)tor->lock ); } /*** @@ -76,38 +105,38 @@ tr_torrentWriterUnlock( tr_torrent_t * tor ) ***/ void -tr_torrentSetSpeedMode( tr_torrent_t * tor, - int up_or_down, - tr_speedlimit_t mode ) +tr_torrentSetSpeedMode( tr_torrent * tor, + int up_or_down, + tr_speedlimit mode ) { - tr_speedlimit_t * limit = up_or_down==TR_UP + tr_speedlimit * limit = up_or_down==TR_UP ? &tor->uploadLimitMode : &tor->downloadLimitMode; *limit = mode; } -tr_speedlimit_t -tr_torrentGetSpeedMode( const tr_torrent_t * tor, - int up_or_down) +tr_speedlimit +tr_torrentGetSpeedMode( const tr_torrent * tor, + int up_or_down) { return up_or_down==TR_UP ? tor->uploadLimitMode : tor->downloadLimitMode; } void -tr_torrentSetSpeedLimit( tr_torrent_t * tor, - int up_or_down, - int single_KiB_sec ) +tr_torrentSetSpeedLimit( tr_torrent * tor, + int up_or_down, + int single_KiB_sec ) { - tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download; + tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download; tr_rcSetLimit( rc, single_KiB_sec ); } int -tr_torrentGetSpeedLimit( const tr_torrent_t * tor, - int up_or_down ) +tr_torrentGetSpeedLimit( const tr_torrent * tor, + int up_or_down ) { - tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download; + tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download; return tr_rcGetLimit( rc ); } @@ -115,19 +144,20 @@ tr_torrentGetSpeedLimit( const tr_torrent_t * tor, **** ***/ -static void setRunState( tr_torrent_t *, run_status_t ); - static void onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data ) { - tr_torrent_t * tor = (tr_torrent_t *) user_data; + tr_torrent * tor = (tr_torrent *) user_data; tr_tracker_event_t * event = (tr_tracker_event_t *) vevent; switch( event->messageType ) { case TR_TRACKER_PEERS: - tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER, - event->peerCompact, event->peerCount ); + tr_peerMgrAddPeers( tor->handle->peerMgr, + tor->info.hash, + TR_PEER_FROM_TRACKER, + event->peerCompact, + event->peerCount ); break; case TR_TRACKER_WARNING: @@ -143,8 +173,8 @@ onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data ) break; case TR_TRACKER_STOPPED: - if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT ) - setRunState( tor, TR_RUN_STOPPED ); + //assert( tor->runStatus == TR_RUN_STOPPING ); + tor->runStatus = TR_RUN_STOPPED; break; } } @@ -156,7 +186,7 @@ onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data ) ***/ static int -getBytePiece( const tr_info_t * info, uint64_t byteOffset ) +getBytePiece( const tr_info * info, uint64_t byteOffset ) { assert( info != NULL ); assert( info->pieceSize != 0 ); @@ -165,9 +195,9 @@ getBytePiece( const tr_info_t * info, uint64_t byteOffset ) } static void -initFilePieces ( tr_info_t * info, int fileIndex ) +initFilePieces ( tr_info * info, int fileIndex ) { - tr_file_t * file = &info->files[fileIndex]; + tr_file * file = &info->files[fileIndex]; uint64_t firstByte, lastByte; assert( info != NULL ); @@ -181,15 +211,15 @@ initFilePieces ( tr_info_t * info, int fileIndex ) } static tr_priority_t -calculatePiecePriority ( const tr_torrent_t * tor, - int piece ) +calculatePiecePriority ( const tr_torrent * tor, + int piece ) { int i; tr_priority_t priority = TR_PRI_NORMAL; for( i=0; iinfo.fileCount; ++i ) { - const tr_file_t * file = &tor->info.files[i]; + const tr_file * file = &tor->info.files[i]; if ( file->firstPiece <= piece && file->lastPiece >= piece && file->priority > priority) @@ -207,7 +237,7 @@ calculatePiecePriority ( const tr_torrent_t * tor, } static void -tr_torrentInitFilePieces( tr_torrent_t * tor ) +tr_torrentInitFilePieces( tr_torrent * tor ) { int i; uint64_t offset = 0; @@ -224,19 +254,41 @@ tr_torrentInitFilePieces( tr_torrent_t * tor ) tor->info.pieces[i].priority = calculatePiecePriority( tor, i ); } -static void torrentThreadLoop( void * ); +static void recheckCpState( tr_torrent * ); static void -torrentRealInit( tr_handle_t * h, - tr_torrent_t * tor, - const char * destination, - int flags ) +tr_torrentStartImpl( tr_torrent * tor ) +{ + assert( tor != NULL ); + assert( tor->runStatus == TR_RUN_RUNNING ); + + *tor->errorString = '\0'; + tr_torrentResetTransferStats( tor ); + recheckCpState( tor ); + tor->startDate = tr_date(); + tr_trackerStart( tor->tracker ); + tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash ); +} + +static void +recheckDoneCB( tr_torrent * tor ) +{ + recheckCpState( tor ); + + if( tor->runStatus == TR_RUN_RUNNING ) + tr_torrentStartImpl( tor ); +} + +static void +torrentRealInit( tr_handle_t * h, + tr_torrent * tor, + const char * destination, + int flags ) { uint64_t loaded; uint64_t t; - char name[512]; - tr_bitfield_t * uncheckedPieces; - tr_info_t * info = &tor->info; + tr_bitfield * uncheckedPieces; + tr_info * info = &tor->info; tor->info.flags |= flags; @@ -298,17 +350,23 @@ torrentRealInit( tr_handle_t * h, tr_torrentInitFilePieces( tor ); - tor->lock = tr_rwNew( ); + tor->lock = tr_lockNew( ); tor->upload = tr_rcInit(); tor->download = tr_rcInit(); tor->swarmspeed = tr_rcInit(); + + tr_sha1( tor->obfuscatedHash, "req2", 4, + info->hash, SHA_DIGEST_LENGTH, + NULL ); /* We have a new torrent */ tor->publicPort = tr_sharedGetPublicPort( h->shared ); tr_sharedUnlock( h->shared ); + tr_peerMgrAddTorrent( h->peerMgr, tor ); + if( !h->isPortSet ) tr_setBindPort( h, TR_DEFAULT_PORT ); @@ -346,8 +404,6 @@ torrentRealInit( tr_handle_t * h, tor->tracker = tr_trackerNew( tor ); tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor ); - if( tor->runStatus == TR_RUN_RUNNING ) - tr_trackerStart( tor->tracker ); tr_sharedLock( h->shared ); tor->next = h->torrentList; @@ -355,8 +411,7 @@ torrentRealInit( tr_handle_t * h, h->torrentCount++; tr_sharedUnlock( h->shared ); - snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name ); - tor->thread = tr_threadNew( torrentThreadLoop, tor, name ); + tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus ); } static int @@ -364,7 +419,7 @@ pathIsInUse ( const tr_handle_t * h, const char * destination, const char * name ) { - const tr_torrent_t * tor; + const tr_torrent * tor; for( tor=h->torrentList; tor; tor=tor->next ) if( !strcmp( destination, tor->destination ) @@ -378,7 +433,7 @@ static int hashExists( const tr_handle_t * h, const uint8_t * hash ) { - const tr_torrent_t * tor; + const tr_torrent * tor; for( tor=h->torrentList; tor; tor=tor->next ) if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) ) @@ -390,7 +445,7 @@ hashExists( const tr_handle_t * h, static int infoCanAdd( const tr_handle_t * h, const char * destination, - const tr_info_t * info ) + const tr_info * info ) { if( hashExists( h, info->hash ) ) return TR_EDUPLICATE; @@ -405,15 +460,15 @@ int tr_torrentParse( const tr_handle_t * h, const char * path, const char * destination, - tr_info_t * setme_info ) + tr_info * setme_info ) { int ret, doFree; - tr_info_t tmp; + tr_info tmp; if( setme_info == NULL ) setme_info = &tmp; - memset( setme_info, 0, sizeof( tr_info_t ) ); + memset( setme_info, 0, sizeof( tr_info ) ); ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE ); doFree = !ret && (setme_info == &tmp); @@ -426,7 +481,7 @@ tr_torrentParse( const tr_handle_t * h, return ret; } -tr_torrent_t * +tr_torrent * tr_torrentInit( tr_handle_t * h, const char * path, const char * destination, @@ -435,14 +490,14 @@ tr_torrentInit( tr_handle_t * h, { int val; int tmpError; - tr_torrent_t * tor = NULL; + tr_torrent * tor = NULL; if( !error ) error = &tmpError; if(( val = tr_torrentParse( h, path, destination, NULL ))) *error = val; - else if(!(( tor = tr_new0( tr_torrent_t, 1 )))) + else if(!(( tor = tr_new0( tr_torrent, 1 )))) *error = TR_EOTHER; else { tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags ); @@ -456,15 +511,15 @@ int tr_torrentParseHash( const tr_handle_t * h, const char * hashStr, const char * destination, - tr_info_t * setme_info ) + tr_info * setme_info ) { int ret, doFree; - tr_info_t tmp; + tr_info tmp; if( setme_info == NULL ) setme_info = &tmp; - memset( setme_info, 0, sizeof( tr_info_t ) ); + memset( setme_info, 0, sizeof( tr_info ) ); ret = tr_metainfoParseHash( setme_info, h->tag, hashStr ); doFree = !ret && (setme_info == &tmp); @@ -477,7 +532,7 @@ tr_torrentParseHash( const tr_handle_t * h, return ret; } -tr_torrent_t * +tr_torrent * tr_torrentInitSaved( tr_handle_t * h, const char * hashStr, const char * destination, @@ -486,14 +541,14 @@ tr_torrentInitSaved( tr_handle_t * h, { int val; int tmpError; - tr_torrent_t * tor = NULL; + tr_torrent * tor = NULL; if( !error ) error = &tmpError; if(( val = tr_torrentParseHash( h, hashStr, destination, NULL ))) *error = val; - else if(!(( tor = tr_new0( tr_torrent_t, 1 )))) + else if(!(( tor = tr_new0( tr_torrent, 1 )))) *error = TR_EOTHER; else { tr_metainfoParseHash( &tor->info, h->tag, hashStr ); @@ -508,15 +563,15 @@ tr_torrentParseData( const tr_handle_t * h, const uint8_t * data, size_t size, const char * destination, - tr_info_t * setme_info ) + tr_info * setme_info ) { int ret, doFree; - tr_info_t tmp; + tr_info tmp; if( setme_info == NULL ) setme_info = &tmp; - memset( setme_info, 0, sizeof( tr_info_t ) ); + memset( setme_info, 0, sizeof( tr_info ) ); ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE ); doFree = !ret && (setme_info == &tmp); @@ -529,7 +584,7 @@ tr_torrentParseData( const tr_handle_t * h, return ret; } -tr_torrent_t * +tr_torrent * tr_torrentInitData( tr_handle_t * h, const uint8_t * data, size_t size, @@ -539,14 +594,14 @@ tr_torrentInitData( tr_handle_t * h, { int val; int tmpError; - tr_torrent_t * tor = NULL; + tr_torrent * tor = NULL; if( !error ) error = &tmpError; if(( val = tr_torrentParseData( h, data, size, destination, NULL ))) *error = val; - else if(!(( tor = tr_new0( tr_torrent_t, 1 )))) + else if(!(( tor = tr_new0( tr_torrent, 1 )))) *error = TR_EOTHER; else { tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags ); @@ -556,8 +611,8 @@ tr_torrentInitData( tr_handle_t * h, return tor; } -const tr_info_t * -tr_torrentInfo( const tr_torrent_t * tor ) +const tr_info * +tr_torrentInfo( const tr_torrent * tor ) { return &tor->info; } @@ -567,117 +622,125 @@ tr_torrentInfo( const tr_torrent_t * tor ) ***/ #if 0 -int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d ) +int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d ) { return tr_trackerScrape( tor, s, l, d ); } #endif -static void -fastResumeSave( tr_torrent_t * tor ) +static int +saveFastResumeNow( void * vtor ) { + tr_torrent * tor = (tr_torrent *) vtor; + tr_fastResumeSave( tor ); - tor->fastResumeDirty = FALSE; + recheckCpState( tor ); + + tr_timerFree( &tor->saveTimer ); + return FALSE; } +static void +saveFastResumeSoon( void * vtor ) +{ + tr_torrent * tor = (tr_torrent *) vtor; + + if( tor->saveTimer == NULL ) + tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 1000 ); +} + +/** +*** +**/ + void -tr_torrentSetFolder( tr_torrent_t * tor, const char * path ) +tr_torrentSetFolder( tr_torrent * tor, const char * path ) { tr_free( tor->destination ); tor->destination = tr_strdup( path ); - fastResumeSave( tor ); + saveFastResumeSoon( tor ); } const char* -tr_torrentGetFolder( const tr_torrent_t * tor ) +tr_torrentGetFolder( const tr_torrent * tor ) { return tor->destination; } void -tr_torrentChangeMyPort( tr_torrent_t * tor, int port ) +tr_torrentChangeMyPort( tr_torrent * tor, int port ) { - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); tor->publicPort = port; if( tor->tracker ) tr_trackerChangeMyPort( tor->tracker ); - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } -/*********************************************************************** - * torrentReallyStop - *********************************************************************** - * Joins the download thread and frees/closes everything related to it. - **********************************************************************/ -void tr_torrentDisablePex( tr_torrent_t * tor, int disable ) +void +tr_torrentDisablePex( tr_torrent * tor, int disable ) { - tr_torrentWriterLock( tor ); + assert( tor != NULL ); + assert( disable==0 || disable==1 ); + //assert( tor->runStatus != TR_RUN_RUNNING ); - if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) ) - { - if( tor->pexDisabled != disable ) - { - int i; - tor->pexDisabled = disable; - for( i=0; ipeerCount; ++i ) - tr_peerSetPrivate( tor->peers[i], disable ); - } - } + /* pex is ALWAYS disabled for private torrents */ + if( tor->info.flags & TR_FLAG_PRIVATE ) + disable = TRUE; - tr_torrentWriterUnlock( tor ); + tor->pexDisabled = disable; } -static int tr_didStateChangeTo ( tr_torrent_t * tor, int status ) +static int tr_didStateChangeTo ( tr_torrent * tor, int status ) { int ret; - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); if (( ret = tor->hasChangedState == status )) tor->hasChangedState = -1; - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); return ret; } -int tr_getIncomplete( tr_torrent_t * tor ) +int tr_getIncomplete( tr_torrent * tor ) { return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE ); } -int tr_getDone( tr_torrent_t * tor ) +int tr_getDone( tr_torrent * tor ) { return tr_didStateChangeTo( tor, TR_CP_DONE ); } -int tr_getComplete( tr_torrent_t * tor ) +int tr_getComplete( tr_torrent * tor ) { return tr_didStateChangeTo( tor, TR_CP_COMPLETE ); } void -tr_manualUpdate( tr_torrent_t * tor ) +tr_manualUpdate( tr_torrent * tor ) { if( tor->runStatus == TR_RUN_RUNNING ) - tr_trackerReannounce( tor->tracker ); + tr_trackerReannounce( tor->tracker ); } int -tr_torrentCanManualUpdate( const tr_torrent_t * tor ) +tr_torrentCanManualUpdate( const tr_torrent * tor ) { return ( tor != NULL ) && ( tor->runStatus == TR_RUN_RUNNING ) && ( tr_trackerCanManualAnnounce( tor->tracker ) ); } -const tr_stat_t * -tr_torrentStat( tr_torrent_t * tor ) +const tr_stat * +tr_torrentStat( tr_torrent * tor ) { - tr_stat_t * s; - struct tr_tracker_s * tc; - int i; + tr_stat * s; + struct tr_tracker * tc; - tr_torrentReaderLock( tor ); + tr_torrentLock( tor ); tor->statCur = ( tor->statCur + 1 ) % 2; s = &tor->stats[tor->statCur]; @@ -689,44 +752,24 @@ tr_torrentStat( tr_torrent_t * tor ) tc = tor->tracker; s->tracker = tr_trackerGetAddress( tor->tracker ); - /* peers... */ - memset( s->peersFrom, 0, sizeof( s->peersFrom ) ); - s->peersTotal = tor->peerCount; - s->peersConnected = 0; - s->peersSendingToUs = 0; - s->peersGettingFromUs = 0; - - for( i=0; ipeerCount; ++i ) - { - const tr_peer_t * peer = tor->peers[i]; - - if( tr_peerIsConnected( peer ) ) - { - ++s->peersConnected; - ++s->peersFrom[tr_peerIsFrom(peer)]; - - if( tr_peerDownloadRate( peer ) > 0.01 ) - ++s->peersSendingToUs; - - if( tr_peerUploadRate( peer ) > 0.01 ) - ++s->peersGettingFromUs; - } - } + tr_peerMgrTorrentStats( tor->handle->peerMgr, + tor->info.hash, + &s->peersTotal, + &s->peersConnected, + &s->peersSendingToUs, + &s->peersGettingFromUs, + s->peersFrom ); s->percentComplete = tr_cpPercentComplete ( tor->completion ); s->percentDone = tr_cpPercentDone( tor->completion ); s->left = tr_cpLeftUntilDone( tor->completion ); - if( tor->uncheckedPieces ) - s->status = tor->runStatus==TR_RUN_CHECKING - ? TR_STATUS_CHECK - : TR_STATUS_CHECK_WAIT; - else switch( tor->runStatus ) { - case TR_RUN_STOPPING: /* fallthrough */ - case TR_RUN_STOPPING_NET_WAIT: s->status = TR_STATUS_STOPPING; break; - case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break; + switch( tor->runStatus ) { + case TR_RUN_CHECKING_WAIT: s->status = TR_STATUS_CHECK_WAIT; break; case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break; + case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break; + case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break; case TR_RUN_RUNNING: switch( tor->cpStatus ) { case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break; case TR_CP_DONE: s->status = TR_STATUS_DONE; break; @@ -772,7 +815,7 @@ tr_torrentStat( tr_torrent_t * tor ) ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid) : TR_RATIO_NA; - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); return s; } @@ -782,9 +825,9 @@ tr_torrentStat( tr_torrent_t * tor ) ***/ static uint64_t -fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex ) +fileBytesCompleted ( const tr_torrent * tor, int fileIndex ) { - const tr_file_t * file = &tor->info.files[fileIndex]; + const tr_file * file = &tor->info.files[fileIndex]; const uint64_t firstBlock = file->offset / tor->blockSize; const uint64_t firstBlockOffset = file->offset % tor->blockSize; const uint64_t lastOffset = file->length ? (file->length-1) : 0; @@ -824,17 +867,17 @@ fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex ) return haveBytes; } -tr_file_stat_t * -tr_torrentFiles( const tr_torrent_t * tor, int * fileCount ) +tr_file_stat * +tr_torrentFiles( const tr_torrent * tor, int * fileCount ) { int i; const int n = tor->info.fileCount; - tr_file_stat_t * files = tr_new0( tr_file_stat_t, n ); - tr_file_stat_t * walk = files; + tr_file_stat * files = tr_new0( tr_file_stat, n ); + tr_file_stat * walk = files; for( i=0; iinfo.files + i; + const tr_file * file = tor->info.files + i; cp_status_t cp; walk->bytesCompleted = fileBytesCompleted( tor, i ); @@ -859,7 +902,7 @@ tr_torrentFiles( const tr_torrent_t * tor, int * fileCount ) } void -tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED ) +tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED ) { tr_free( files ); } @@ -868,92 +911,30 @@ tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED ) **** ***/ -tr_peer_stat_t * -tr_torrentPeers( const tr_torrent_t * tor, int * peerCount ) +tr_peer_stat * +tr_torrentPeers( const tr_torrent * tor, int * peerCount ) { - tr_peer_stat_t * peers; - - tr_torrentReaderLock( tor ); - - *peerCount = tor->peerCount; - - peers = tr_new0( tr_peer_stat_t, tor->peerCount ); - if (peers != NULL) - { - tr_peer_t * peer; - struct in_addr * addr; - int i; - for( i=0; ipeerCount; ++i ) - { - peer = tor->peers[i]; - - addr = tr_peerAddress( peer ); - if( NULL != addr ) - { - tr_netNtop( addr, peers[i].addr, - sizeof( peers[i].addr ) ); - } - - peers[i].client = tr_peerClient( peer ); - peers[i].isConnected = tr_peerIsConnected( peer ); - peers[i].from = tr_peerIsFrom( peer ); - peers[i].progress = tr_peerProgress( peer ); - peers[i].port = tr_peerPort( peer ); - - peers[i].uploadToRate = tr_peerUploadRate( peer ); - peers[i].downloadFromRate = tr_peerDownloadRate( peer ); - - peers[i].isDownloading = peers[i].uploadToRate > 0.01; - peers[i].isUploading = peers[i].downloadFromRate > 0.01; - } - } - - tr_torrentReaderUnlock( tor ); - - return peers; + return tr_peerMgrPeerStats( tor->handle->peerMgr, + tor->info.hash, peerCount ); } -void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED ) +void tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED ) { tr_free( peers ); } -void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size ) +void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size ) { - int i, j, piece; - float interval; - - tr_torrentReaderLock( tor ); - - interval = (float)tor->info.pieceCount / (float)size; - for( i = 0; i < size; i++ ) - { - piece = i * interval; - - if( tr_cpPieceIsComplete( tor->completion, piece ) ) - { - tab[i] = -1; - continue; - } - - tab[i] = 0; - for( j = 0; j < tor->peerCount; j++ ) - { - if( tr_peerHasPiece( tor->peers[j], piece ) ) - { - (tab[i])++; - } - } - } - - tr_torrentReaderUnlock( tor ); + return tr_peerMgrTorrentAvailability( tor->handle->peerMgr, + tor->info.hash, + tab, size ); } -void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size ) +void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size ) { int i; float interval; - tr_torrentReaderLock( tor ); + tr_torrentLock( tor ); interval = (float)tor->info.pieceCount / (float)size; for( i = 0; i < size; i++ ) @@ -962,13 +943,13 @@ void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size ) tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece ); } - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); } void -tr_torrentResetTransferStats( tr_torrent_t * tor ) +tr_torrentResetTransferStats( tr_torrent * tor ) { - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); tor->downloadedPrev += tor->downloadedCur; tor->downloadedCur = 0; @@ -977,131 +958,61 @@ tr_torrentResetTransferStats( tr_torrent_t * tor ) tor->corruptPrev += tor->corruptCur; tor->corruptCur = 0; - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } void -tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has ) +tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has ) { - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); if( has ) tr_cpPieceAdd( tor->completion, pieceIndex ); else tr_cpPieceRem( tor->completion, pieceIndex ); - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } -void tr_torrentRemoveSaved( tr_torrent_t * tor ) +void tr_torrentRemoveSaved( tr_torrent * tor ) { tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag ); } -void tr_torrentRecheck( tr_torrent_t * tor ) +void tr_torrentRecheck( tr_torrent * tor ) { if( !tor->uncheckedPieces ) tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount ); tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount ); -} - -int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer ) -{ - int i; - tr_peer_t * otherPeer; - - assert( tor != NULL ); - assert( peer != NULL ); - - if( tor->peerCount >= TR_MAX_PEER_COUNT ) - { - tr_peerDestroy( peer ); - return 0; - } - - /* Don't accept two connections from the same IP */ - for( i = 0; i < tor->peerCount; i++ ) - { - otherPeer = tor->peers[i]; - if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) ) - { - tr_peerDestroy( peer ); - return 0; - } - } - - tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE || - tor->pexDisabled ); - tr_peerSetTorrent( peer, tor ); - tor->peers[tor->peerCount++] = peer; - - return 1; -} - -int tr_torrentAddCompact( tr_torrent_t * tor, int from, - const uint8_t * buf, int count ) -{ - struct in_addr addr; - tr_port_t port; - int i, added; - tr_peer_t * peer; - - added = 0; - for( i=0; irunStatus ); } /*** **** ***/ -static void setRunState( tr_torrent_t * tor, run_status_t run ) +void +tr_torrentStart( tr_torrent * tor ) { - tr_torrentWriterLock( tor ); - tor->runStatus = run; - tr_torrentWriterUnlock( tor ); -} - -void tr_torrentStart( tr_torrent_t * tor ) -{ - setRunState( tor, TR_RUN_RUNNING ); - - tr_trackerStart( tor->tracker ); -} - -void tr_torrentStop( tr_torrent_t * tor ) -{ - if((tor->runStatus != TR_RUN_STOPPING) && (tor->runStatus != TR_RUN_STOPPED) ) - setRunState( tor, TR_RUN_STOPPING ); -} - -void tr_torrentClose( tr_torrent_t * tor ) -{ - tor->runStatusToSave = tor->runStatus; - tor->runStatusToSaveIsSet = TRUE; - tr_torrentStop( tor ); - tor->dieFlag = TRUE; + tr_ioRecheckAdd( tor, recheckDoneCB, TR_RUN_RUNNING ); } static void -tr_torrentFree( tr_torrent_t * tor ) +tr_torrentFree( tr_torrent * tor ) { - tr_torrent_t * t; + tr_torrent * t; tr_handle_t * h = tor->handle; - tr_info_t * inf = &tor->info; + tr_info * inf = &tor->info; +fprintf( stderr, "closing torrent %s\n", tor->info.name ); + + assert( tor != NULL ); + assert( tor->runStatus == TR_RUN_STOPPED ); tr_sharedLock( h->shared ); - tr_rwFree( tor->lock ); + tr_lockFree( tor->lock ); tr_cpClose( tor->completion ); tr_rcClose( tor->upload ); @@ -1114,8 +1025,6 @@ tr_torrentFree( tr_torrent_t * tor ) tr_free( tor->destination ); - tr_metainfoFree( inf ); - if( tor == h->torrentList ) h->torrentList = tor->next; else for( t=h->torrentList; t!=NULL; t=t->next ) { @@ -1125,22 +1034,82 @@ tr_torrentFree( tr_torrent_t * tor ) } } + assert( h->torrentCount >= 1 ); h->torrentCount--; tr_inf( "closing torrent %s... %d torrents left", tor->info.name, h->torrentCount ); + tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash ); + + tr_metainfoFree( inf ); tr_free( tor ); tr_sharedUnlock( h->shared ); } +static int +freeWhenStopped( void * vtor ) +{ + tr_torrent * tor = vtor; + + if( tor->runStatus != TR_RUN_STOPPED ) /* keep waiting */ + return TRUE; + + tr_torrentFree( tor ); + return FALSE; +} + static void -recheckCpState( tr_torrent_t * tor ) +tr_torrentStopImpl( void * vtor ) +{ + tr_torrent * tor = vtor; + + switch( tor->runStatus ) + { + case TR_RUN_CHECKING_WAIT: + case TR_RUN_CHECKING: + tr_ioRecheckRemove( tor ); + tr_torrentStop( tor ); + break; + + case TR_RUN_RUNNING: + saveFastResumeNow( tor ); + tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash ); + tor->runStatus = TR_RUN_STOPPING; + tor->stopDate = tr_date( ); + tr_trackerStop( tor->tracker ); + tr_ioClose( tor ); + break; + + case TR_RUN_STOPPING: + case TR_RUN_STOPPED: + break; + } +} + +void +tr_torrentStop( tr_torrent * tor ) +{ + tr_runInEventThread( tor->handle, tr_torrentStopImpl, tor ); +} + +void +tr_torrentClose( tr_torrent * tor ) +{ + tor->runStatusToSave = tor->runStatus; + tor->runStatusToSaveIsSet = TRUE; + tor->dieFlag = TRUE; + tr_torrentStop( tor ); + tr_timerNew( tor->handle, freeWhenStopped, tor, 250 ); +} + +static void +recheckCpState( tr_torrent * tor ) { cp_status_t cpStatus; - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); cpStatus = tr_cpGetStatus( tor->completion ); if( cpStatus != tor->cpStatus ) { @@ -1150,170 +1119,25 @@ recheckCpState( tr_torrent_t * tor ) && tor->downloadedCur ) { /* and it just happened */ tr_trackerCompleted( tor->tracker ); /* tell the tracker */ } - tr_ioSync( tor->io ); - fastResumeSave( tor ); + tr_ioClose( tor ); + saveFastResumeSoon( tor ); } - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } -static void -torrentThreadLoop ( void * _tor ) -{ - static tr_lock_t * checkFilesLock = NULL; - tr_torrent_t * tor = _tor; - - /* create the check-files mutex */ - if( !checkFilesLock ) - checkFilesLock = tr_lockNew( ); - - /* loop until the torrent is being deleted */ - while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) ) - { - /* sleep a little while */ - tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 600 ); - - if( tor->fastResumeDirty ) - { - fastResumeSave( tor ); - recheckCpState( tor ); - } - - /* if we're stopping... */ - if( tor->runStatus == TR_RUN_STOPPING ) - { - int i; - tr_torrentWriterLock( tor ); - - /* close the IO */ - tr_ioClose( tor->io ); - tor->io = NULL; - fastResumeSave( tor ); - - /* close the peers */ - for( i=0; ipeerCount; ++i ) - tr_peerDestroy( tor->peers[i] ); - tor->peerCount = 0; - - /* resest the transfer rates */ - tr_rcReset( tor->download ); - tr_rcReset( tor->upload ); - tr_rcReset( tor->swarmspeed ); - - /* tell the tracker we're stopping */ - tr_trackerStop( tor->tracker ); - tor->runStatus = TR_RUN_STOPPING_NET_WAIT; - tor->stopDate = tr_date(); - tr_torrentWriterUnlock( tor ); - } - - if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT ) - { -#if 0 - tr_torrentWriterLock( tor ); - tor->runStatus = TR_RUN_STOPPED; - tr_torrentWriterUnlock( tor ); -#endif - continue; - } - - /* do we need to check files? */ - if( tor->uncheckedPieces ) - { - if( !tr_lockTryLock( checkFilesLock ) ) - { - run_status_t realStatus; - - tr_torrentWriterLock( tor ); - realStatus = tor->runStatus; - tor->runStatus = TR_RUN_CHECKING; - tr_torrentWriterUnlock( tor ); - - tr_ioCheckFiles( tor ); - setRunState( tor, realStatus ); - - tr_torrentWriterLock( tor ); - tor->cpStatus = tr_cpGetStatus( tor->completion ); - tr_torrentWriterUnlock( tor ); - - tr_lockUnlock( checkFilesLock ); - } - continue; - } - - /* if we're paused or stopped, not much to do... */ - if( tor->runStatus == TR_RUN_STOPPED ) - continue; - - /* ping our peers if we're running... */ - if( tor->runStatus == TR_RUN_RUNNING ) - { - int i; - - /* starting to run... */ - if( tor->io == NULL ) - { - *tor->errorString = '\0'; - tr_torrentResetTransferStats( tor ); - tor->io = tr_ioNew( tor ); - tor->startDate = tr_date(); - } - - /* refresh our completion state */ - recheckCpState( tor ); - - /* Shuffle peers */ - if ( tor->peerCount > 1 ) { - tr_peer_t * tmp = tor->peers[0]; - memmove( tor->peers, tor->peers+1, - (tor->peerCount-1) * sizeof(void*) ); - tor->peers[tor->peerCount - 1] = tmp; - } - - /* receive/send messages */ - tr_torrentWriterLock( tor ); - for( i=0; ipeerCount; ) { - tr_peer_t * peer = tor->peers[i]; - int ret = tr_peerPulse( peer ); - if( ret & TR_ERROR_IO_MASK ) { - tr_err( "Fatal error, stopping download (%d)", ret ); - tor->runStatus = TR_RUN_STOPPING; - tor->error = ret; - strlcpy( tor->errorString, - tr_errorString(ret), - sizeof(tor->errorString) ); - break; - } - if( ret ) { - tr_peerDestroy( peer ); - tor->peerCount--; - memmove( &tor->peers[i], &tor->peers[i+1], - (tor->peerCount-i)*sizeof(void*) ); - continue; - } - i++; - } - tr_torrentWriterUnlock( tor ); - } - } - - tr_ioClose( tor->io ); - tr_torrentFree( tor ); -} - - /** *** File priorities **/ void -tr_torrentSetFilePriority( tr_torrent_t * tor, - int fileIndex, - tr_priority_t priority ) +tr_torrentSetFilePriority( tr_torrent * tor, + int fileIndex, + tr_priority_t priority ) { int i; - tr_file_t * file; + tr_file * file; - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); assert( tor != NULL ); assert( 0<=fileIndex && fileIndexinfo.fileCount ); @@ -1328,16 +1152,16 @@ tr_torrentSetFilePriority( tr_torrent_t * tor, fileIndex, file->firstPiece, file->lastPiece, priority, tor->info.files[fileIndex].name ); - tor->fastResumeDirty = TRUE; + saveFastResumeSoon( tor ); - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } void -tr_torrentSetFilePriorities( tr_torrent_t * tor, - int * files, - int fileCount, - tr_priority_t priority ) +tr_torrentSetFilePriorities( tr_torrent * tor, + int * files, + int fileCount, + tr_priority_t priority ) { int i; for( i=0; iinfo.fileCount ); ret = tor->info.files[file].priority; - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); return ret; } tr_priority_t* -tr_torrentGetFilePriorities( const tr_torrent_t * tor ) +tr_torrentGetFilePriorities( const tr_torrent * tor ) { int i; tr_priority_t * p; - tr_torrentReaderLock( tor ); + tr_torrentLock( tor ); p = tr_new0( tr_priority_t, tor->info.fileCount ); for( i=0; iinfo.fileCount; ++i ) p[i] = tor->info.files[i].priority; - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); return p; } @@ -1379,31 +1203,31 @@ tr_torrentGetFilePriorities( const tr_torrent_t * tor ) **/ int -tr_torrentGetFileDL( const tr_torrent_t * tor, - int file ) +tr_torrentGetFileDL( const tr_torrent * tor, + int file ) { int doDownload; - tr_torrentReaderLock( tor ); + tr_torrentLock( tor ); assert( 0<=file && fileinfo.fileCount ); doDownload = !tor->info.files[file].dnd; - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); return doDownload != 0; } void -tr_torrentSetFileDL( tr_torrent_t * tor, - int fileIndex, - int doDownload ) +tr_torrentSetFileDL( tr_torrent * tor, + int fileIndex, + int doDownload ) { - tr_file_t * file; + tr_file * file; const int dnd = !doDownload; int firstPiece, firstPieceDND; int lastPiece, lastPieceDND; int i; - tr_torrentWriterLock( tor ); + tr_torrentLock( tor ); file = &tor->info.files[fileIndex]; file->dnd = dnd; @@ -1442,16 +1266,16 @@ tr_torrentSetFileDL( tr_torrent_t * tor, tr_cpInvalidateDND ( tor->completion ); - tor->fastResumeDirty = TRUE; + saveFastResumeSoon( tor ); - tr_torrentWriterUnlock( tor ); + tr_torrentUnlock( tor ); } void -tr_torrentSetFileDLs ( tr_torrent_t * tor, - int * files, - int fileCount, - int doDownload ) +tr_torrentSetFileDLs ( tr_torrent * tor, + int * files, + int fileCount, + int doDownload ) { int i; for( i=0; iinfo; + const tr_info * inf = &tor->info; return index * ( inf->pieceSize / tor->blockSize ) + begin / tor->blockSize; } diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c index e507fff16..cae5c82f4 100644 --- a/libtransmission/tracker.c +++ b/libtransmission/tracker.c @@ -28,7 +28,6 @@ #include "net.h" #include "ptrarray.h" #include "publish.h" -#include "timer.h" #include "tracker.h" #include "trevent.h" #include "utils.h" @@ -65,9 +64,9 @@ typedef struct { tr_handle_t * handle; - tr_ptrArray_t * torrents; - tr_ptrArray_t * scraping; - tr_ptrArray_t * scrapeQueue; + tr_ptrArray * torrents; + tr_ptrArray * scraping; + tr_ptrArray * scrapeQueue; /* these are set from the latest scrape or tracker response */ int announceIntervalMsec; @@ -78,8 +77,8 @@ typedef struct back than we asked for */ int multiscrapeMax; - tr_tracker_info_t * redirect; - tr_tracker_info_t * addresses; + tr_tracker_info * redirect; + tr_tracker_info * addresses; int addressIndex; int addressCount; int * tierFronts; @@ -91,14 +90,16 @@ typedef struct This is immutable for the life of the tracker object. */ char key_param[TR_KEY_LEN+1]; - tr_timer_tag scrapeTag; + tr_timer * scrapeTimer; + + struct evhttp_connection * connection; } Tracker; /* this is the Torrent struct, but since it's the pointer passed around in the public API of this tracker module, - its *public* name is tr_tracker_s... wheee */ -typedef struct tr_tracker_s + its *public* name is tr_tracker... wheee */ +typedef struct tr_tracker { tr_publisher_t * publisher; @@ -129,12 +130,12 @@ typedef struct tr_tracker_s Tracker * tracker; - tr_timer_tag scrapeTag; - tr_timer_tag reannounceTag; + tr_timer * scrapeTimer; + tr_timer * reannounceTimer; struct evhttp_request * httpReq; - tr_torrent_t * torrent; + tr_torrent * torrent; } Torrent; @@ -158,47 +159,27 @@ torrentCompare( const void * va, const void * vb ) **** ***/ -typedef struct -{ - char * address; - int port; - struct evhttp_connection * evconn; -} -connection_key_t; - -static int -connectionCompare( const void * va, const void * vb ) -{ - const connection_key_t * a = (const connection_key_t *) va; - const connection_key_t * b = (const connection_key_t *) vb; - int ret = strcmp( a->address, b->address ); - if( ret ) return ret; - return a->port - b->port; -} - static struct evhttp_connection* -getConnection( const char * address, int port ) +getConnection( Tracker * tracker, const char * address, int port ) { - connection_key_t *val, tmp; - - static tr_ptrArray_t * connections = NULL; - if( !connections ) - connections = tr_ptrArrayNew( ); - - tmp.address = (char*) address; - tmp.port = port; - val = tr_ptrArrayFindSorted( connections, &tmp, connectionCompare ); - - if( !val ) + if( tracker->connection != NULL ) { - val = tr_new( connection_key_t, 1 ); - val->address = tr_strdup( address ); - val->port = port; - val->evconn = evhttp_connection_new( address, port ); - tr_ptrArrayInsertSorted( connections, val, connectionCompare ); + char * a = NULL; + unsigned short p = 0; + evhttp_connection_get_peer( tracker->connection, &a, &p ); + + /* old one matches -- reuse it */ + if( a && !strcmp(a,address) && p==port ) + return tracker->connection; + + /* old one doesn't match -- throw it away */ + evhttp_connection_free( tracker->connection ); + tracker->connection = NULL; } - return val->evconn; + /* make a new connection */ + tracker->connection = evhttp_connection_new( address, port ); + return tracker->connection; } /*** @@ -252,10 +233,10 @@ publishStopped( Torrent * tor ) **** LIFE CYCLE ***/ -static tr_ptrArray_t * +static tr_ptrArray * getTrackerLookupTable( void ) { - static tr_ptrArray_t * myTrackers = NULL; + static tr_ptrArray * myTrackers = NULL; if( !myTrackers ) myTrackers = tr_ptrArrayNew( ); return myTrackers; @@ -280,17 +261,18 @@ tr_trackerScrapeSoon( Tracker * t ) if( !tr_ptrArrayEmpty( t->scraping ) ) return; - if( !t->scrapeTag ) - t->scrapeTag = tr_timerNew( t->handle, - onTrackerScrapeNow, t, - NULL, 1000 ); + if( !t->scrapeTimer ) + t->scrapeTimer = tr_timerNew( t->handle, onTrackerScrapeNow, t, 1000 ); } +static int trackerCount = 0; +static int torrentCount = 0; + static Tracker* -tr_trackerGet( const tr_torrent_t * tor ) +tr_trackerGet( const tr_torrent * tor ) { - const tr_info_t * info = &tor->info; - tr_ptrArray_t * trackers = getTrackerLookupTable( ); + const tr_info * info = &tor->info; + tr_ptrArray * trackers = getTrackerLookupTable( ); Tracker *t, tmp; assert( info != NULL ); assert( info->primaryAddress && *info->primaryAddress ); @@ -302,10 +284,11 @@ tr_trackerGet( const tr_torrent_t * tor ) if( t == NULL ) /* no such tracker.... create one */ { int i, j, sum, *iwalk; - tr_tracker_info_t * nwalk; + tr_tracker_info * nwalk; tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress ); t = tr_new0( Tracker, 1 ); +fprintf( stderr, "TRACKER new tracker %p addr %s; counts are trackers %d torrents %d\n", t, info->primaryAddress, ++trackerCount, torrentCount ); t->handle = tor->handle; t->primaryAddress = tr_strdup( info->primaryAddress ); t->scrapeIntervalMsec = DEFAULT_SCRAPE_INTERVAL_MSEC; @@ -319,7 +302,7 @@ tr_trackerGet( const tr_torrent_t * tor ) for( sum=i=0; itrackerTiers; ++i ) sum += info->trackerList[i].count; - t->addresses = nwalk = tr_new0( tr_tracker_info_t, sum ); + t->addresses = nwalk = tr_new0( tr_tracker_info, sum ); t->addressIndex = 0; t->addressCount = sum; t->tierFronts = iwalk = tr_new0( int, sum ); @@ -330,7 +313,7 @@ tr_trackerGet( const tr_torrent_t * tor ) for( j=0; jtrackerList[i].count; ++j ) { - const tr_tracker_info_t * src = &info->trackerList[i].list[j]; + const tr_tracker_info * src = &info->trackerList[i].list[j]; nwalk->address = tr_strdup( src->address ); nwalk->port = src->port; nwalk->announce = tr_strdup( src->announce ); @@ -370,7 +353,7 @@ escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */ *out = '\0'; } -static int +static void onTorrentFreeNow( void * vtor ) { Torrent * tor = (Torrent *) vtor; @@ -380,11 +363,14 @@ onTorrentFreeNow( void * vtor ) tr_ptrArrayRemoveSorted( t->scrapeQueue, tor, torrentCompare ); tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare ); - tr_timerFree( &tor->scrapeTag ); - tr_timerFree( &tor->reannounceTag ); - tr_publisherFree( tor->publisher ); +fprintf( stderr, "TRACKER freeing torrent %p name %s; counts are trackers %d torrents %d\n", tor, tor->torrent->info.name, trackerCount, --torrentCount ); + tr_timerFree( &tor->scrapeTimer ); + tr_timerFree( &tor->reannounceTimer ); + tr_publisherFree( &tor->publisher ); tr_free( tor->trackerID ); tr_free( tor->lastRequest ); + if( tor->httpReq != NULL ) + evhttp_request_free( tor->httpReq ); tr_free( tor ); if( tr_ptrArrayEmpty( t->torrents ) ) /* last one.. free the tracker too */ @@ -392,6 +378,11 @@ onTorrentFreeNow( void * vtor ) int i; tr_ptrArrayRemoveSorted( getTrackerLookupTable( ), t, trackerCompare ); +fprintf( stderr, "TRACKER freeing tracker %p; counts are trackers %d torrents %d\n", t, --trackerCount, torrentCount ); + + if( t->connection != NULL ) + evhttp_connection_free( t->connection ); + tr_ptrArrayFree( t->torrents ); tr_ptrArrayFree( t->scrapeQueue ); tr_ptrArrayFree( t->scraping ); @@ -404,26 +395,23 @@ onTorrentFreeNow( void * vtor ) tr_free( t->redirect ); } - tr_timerFree( &t->scrapeTag ); + tr_timerFree( &t->scrapeTimer ); tr_free( t->primaryAddress ); tr_free( t->addresses ); tr_free( t->tierFronts ); tr_free( t ); } - - return FALSE; } void tr_trackerFree( Torrent * tor ) { - tr_timerNew( tor->tracker->handle, - onTorrentFreeNow, tor, NULL, 0 ); + tr_runInEventThread( tor->tracker->handle, onTorrentFreeNow, tor ); } Torrent* -tr_trackerNew( tr_torrent_t * torrent ) +tr_trackerNew( tr_torrent * torrent ) { Torrent * tor; Tracker * t = tr_trackerGet( torrent ); @@ -431,6 +419,7 @@ tr_trackerNew( tr_torrent_t * torrent ) /* create a new Torrent and queue it for scraping */ tor = tr_new0( Torrent, 1 ); +fprintf( stderr, "TRACKER new torrent %p name %s; counts are trackers %d torrents %d\n", tor, torrent->info.name, trackerCount, ++torrentCount ); tor->publisher = tr_publisherNew( ); tor->tracker = t; tor->torrent = torrent; @@ -488,7 +477,7 @@ updateAddresses( Tracker * t, const struct evhttp_request * req ) successful, it will be moved to the front of the tier." */ const int i = t->addressIndex; const int j = t->tierFronts[i]; - const tr_tracker_info_t swap = t->addresses[i]; + const tr_tracker_info swap = t->addresses[i]; t->addresses[i] = t->addresses[j]; t->addresses[j] = swap; } @@ -497,21 +486,21 @@ updateAddresses( Tracker * t, const struct evhttp_request * req ) || ( req->response_code == HTTP_MOVETEMP ) ) { const char * loc = evhttp_find_header( req->input_headers, "Location" ); - tr_tracker_info_t tmp; + tr_tracker_info tmp; if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */ { moveToNextAddress = TRUE; } else if( req->response_code == HTTP_MOVEPERM ) { - tr_tracker_info_t * cur = &t->addresses[t->addressIndex]; + tr_tracker_info * cur = &t->addresses[t->addressIndex]; tr_trackerInfoClear( cur ); *cur = tmp; } else if( req->response_code == HTTP_MOVETEMP ) { if( t->redirect == NULL ) - t->redirect = tr_new0( tr_tracker_info_t, 1 ); + t->redirect = tr_new0( tr_tracker_info, 1 ); else tr_trackerInfoClear( t->redirect ); *t->redirect = tmp; @@ -534,7 +523,7 @@ updateAddresses( Tracker * t, const struct evhttp_request * req ) return ret; } -static tr_tracker_info_t * +static tr_tracker_info * getCurrentAddress( const Tracker * t ) { assert( t->addresses != NULL ); @@ -546,7 +535,7 @@ getCurrentAddress( const Tracker * t ) static int trackerSupportsScrape( const Tracker * t ) { - const tr_tracker_info_t * info = getCurrentAddress( t ); + const tr_tracker_info * info = getCurrentAddress( t ); return ( info != NULL ) && ( info->scrape != NULL ) @@ -559,7 +548,7 @@ addCommonHeaders( const Tracker * t, struct evhttp_request * req ) { char buf[1024]; - tr_tracker_info_t * address = getCurrentAddress( t ); + tr_tracker_info * address = getCurrentAddress( t ); snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port ); evhttp_add_header( req->output_headers, "Host", buf ); evhttp_add_header( req->output_headers, "Connection", "close" ); @@ -582,7 +571,7 @@ onTorrentScrapeNow( void * vtor ) tr_ptrArrayInsertSorted( tor->tracker->scrapeQueue, tor, torrentCompare ); tr_trackerScrapeSoon( tor->tracker ); } - tor->scrapeTag = NULL; + tor->scrapeTimer = NULL; return FALSE; } @@ -638,11 +627,8 @@ onScrapeResponse( struct evhttp_request * req, void * vt ) assert( tr_ptrArrayFindSorted(t->scraping,tor,torrentCompare) ); tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare ); - assert( !tor->scrapeTag ); - tor->scrapeTag = tr_timerNew( t->handle, - onTorrentScrapeNow, - tor, NULL, - t->scrapeIntervalMsec ); + assert( !tor->scrapeTimer ); + tor->scrapeTimer = tr_timerNew( t->handle, onTorrentScrapeNow, tor, t->scrapeIntervalMsec ); tr_dbg( "Torrent '%s' scrape successful." " Rescraping in %d seconds", tor->torrent->info.name, t->scrapeIntervalMsec/1000 ); @@ -699,7 +685,7 @@ static int onTrackerScrapeNow( void * vt ) { Tracker * t = (Tracker*) vt; - const tr_tracker_info_t * address = getCurrentAddress( t ); + const tr_tracker_info * address = getCurrentAddress( t ); assert( tr_ptrArrayEmpty( t->scraping ) ); @@ -743,7 +729,7 @@ onTrackerScrapeNow( void * vt ) /* ping the tracker */ tr_inf( "Sending scrape to tracker %s:%d: %s", address->address, address->port, uri ); - evcon = getConnection( address->address, address->port ); + evcon = getConnection( t, address->address, address->port ); evhttp_connection_set_timeout( evcon, SCRAPE_TIMEOUT_INTERVAL_SEC ); req = evhttp_request_new( onScrapeResponse, t ); assert( req ); @@ -751,7 +737,7 @@ onTrackerScrapeNow( void * vt ) tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri ); } - t->scrapeTag = NULL; + t->scrapeTimer = NULL; return FALSE; } @@ -772,7 +758,7 @@ torrentIsRunning( const Torrent * tor ) static char* buildTrackerRequestURI( const Torrent * tor, const char * eventName ) { - const tr_torrent_t * torrent = tor->torrent; + const tr_torrent * torrent = tor->torrent; const int stopping = !strcmp( eventName, "stopped" ); const int numwant = stopping ? 0 : NUMWANT; char buf[4096]; @@ -964,9 +950,8 @@ onTrackerResponse( struct evhttp_request * req, void * vtor ) else if( reannounceInterval > 0 ) { tr_dbg( "torrent '%s' reannouncing in %d seconds", tor->torrent->info.name, (reannounceInterval/1000) ); - tor->reannounceTag = tr_timerNew( tor->tracker->handle, - onReannounceNow, tor, NULL, - reannounceInterval ); + assert( tor->reannounceTimer == NULL ); + tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceInterval ); tor->manualAnnounceAllowedAt = tr_date() + MANUAL_ANNOUNCE_INTERVAL_MSEC; } @@ -976,7 +961,7 @@ static int sendTrackerRequest( void * vtor, const char * eventName ) { Torrent * tor = (Torrent *) vtor; - const tr_tracker_info_t * address = getCurrentAddress( tor->tracker ); + const tr_tracker_info * address = getCurrentAddress( tor->tracker ); char * uri = buildTrackerRequestURI( tor, eventName ); struct evhttp_connection * evcon = NULL; @@ -987,9 +972,9 @@ sendTrackerRequest( void * vtor, const char * eventName ) uri ); /* kill any pending requests */ - tr_timerFree( &tor->reannounceTag ); + tr_timerFree( &tor->reannounceTimer ); - evcon = getConnection( address->address, address->port ); + evcon = getConnection( tor->tracker, address->address, address->port ); if ( !evcon ) { tr_err( "Can't make a connection to %s:%d", address->address, address->port ); tr_free( uri ); @@ -1011,7 +996,7 @@ onReannounceNow( void * vtor ) { Torrent * tor = (Torrent *) vtor; sendTrackerRequest( tor, "" ); - tor->reannounceTag = NULL; + tor->reannounceTimer = NULL; return FALSE; } @@ -1034,7 +1019,7 @@ tr_trackerUnsubscribe( Torrent * tor, tr_publisherUnsubscribe( tor->publisher, tag ); } -const tr_tracker_info_t * +const tr_tracker_info * tr_trackerGetAddress( const Torrent * tor ) { return getCurrentAddress( tor->tracker ); @@ -1070,7 +1055,7 @@ tr_trackerStart( Torrent * tor ) { tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) ); - if( !tor->reannounceTag && !tor->httpReq ) + if( !tor->reannounceTimer && !tor->httpReq ) sendTrackerRequest( tor, "started" ); } diff --git a/libtransmission/tracker.h b/libtransmission/tracker.h index 467b2136f..00c6d2447 100644 --- a/libtransmission/tracker.h +++ b/libtransmission/tracker.h @@ -21,9 +21,9 @@ *** Locating a tracker **/ -struct tr_tracker_s * tr_trackerNew( tr_torrent_t * ); +struct tr_tracker * tr_trackerNew( tr_torrent * ); -void tr_trackerFree ( struct tr_tracker_s * ); +void tr_trackerFree ( struct tr_tracker * ); /** *** Tracker Publish / Subscribe @@ -55,34 +55,34 @@ typedef struct } tr_tracker_event_t; -tr_publisher_tag tr_trackerSubscribe ( struct tr_tracker_s * tag, - tr_delivery_func func, - void * user ); +tr_publisher_tag tr_trackerSubscribe ( struct tr_tracker * tag, + tr_delivery_func func, + void * user ); -void tr_trackerUnsubscribe ( struct tr_tracker_s * tracker, - tr_publisher_tag tag ); +void tr_trackerUnsubscribe ( struct tr_tracker * tracker, + tr_publisher_tag tag ); /*** **** ***/ -void tr_trackerStart ( struct tr_tracker_s * ); +void tr_trackerStart ( struct tr_tracker * ); -void tr_trackerCompleted ( struct tr_tracker_s * ); +void tr_trackerCompleted ( struct tr_tracker * ); -void tr_trackerStop ( struct tr_tracker_s * ); +void tr_trackerStop ( struct tr_tracker * ); -void tr_trackerReannounce ( struct tr_tracker_s * ); +void tr_trackerReannounce ( struct tr_tracker * ); -void tr_trackerChangeMyPort ( struct tr_tracker_s * ); +void tr_trackerChangeMyPort ( struct tr_tracker * ); -const tr_tracker_info_t * tr_trackerGetAddress( const struct tr_tracker_s * ); +const tr_tracker_info * tr_trackerGetAddress( const struct tr_tracker * ); -int tr_trackerCanManualAnnounce ( const struct tr_tracker_s * ); +int tr_trackerCanManualAnnounce ( const struct tr_tracker * ); -void tr_trackerGetCounts ( const struct tr_tracker_s *, - int * setme_completedCount, - int * setme_leecherCount, - int * setme_seederCount ); +void tr_trackerGetCounts ( const struct tr_tracker *, + int * setme_completedCount, + int * setme_leecherCount, + int * setme_seederCount ); #endif diff --git a/libtransmission/transmission.c b/libtransmission/transmission.c index d5d54b321..26ae95899 100644 --- a/libtransmission/transmission.c +++ b/libtransmission/transmission.c @@ -37,6 +37,7 @@ #include "fdlimit.h" #include "list.h" #include "net.h" +#include "peer-mgr.h" #include "platform.h" #include "ratecontrol.h" #include "shared.h" @@ -62,6 +63,38 @@ tr_peerIdNew ( char * buf, int buflen ) buf[TR_ID_LEN] = '\0'; } +const char* +getPeerId( void ) +{ + static char * peerId = NULL; + if( !peerId ) { + peerId = tr_new0( char, TR_ID_LEN + 1 ); + tr_peerIdNew( peerId, TR_ID_LEN + 1 ); + } + return peerId; +} + +/*** +**** +***/ + +tr_encryption_mode +tr_getEncryptionMode( tr_handle * handle ) +{ + assert( handle != NULL ); + + return handle->encryptionMode; +} + +void +tr_setEncryptionMode( tr_handle * handle, tr_encryption_mode mode ) +{ + assert( handle != NULL ); + assert( mode==TR_ENCRYPTION_PREFERRED || mode==TR_ENCRYPTION_REQUIRED ); + + handle->encryptionMode = mode; +} + /*** **** ***/ @@ -77,15 +110,24 @@ tr_handle_t * tr_init( const char * tag ) tr_handle_t * h; int i; +#ifndef WIN32 + /* Don't exit when writing on a broken socket */ + signal( SIGPIPE, SIG_IGN ); +#endif + tr_msgInit(); h = tr_new0( tr_handle_t, 1 ); if( !h ) return NULL; + h->encryptionMode = TR_ENCRYPTION_PREFERRED; + tr_eventInit( h ); + while( !h->events ) + tr_wait( 50 ); + tr_netInit(); - tr_netResolveThreadInit(); h->tag = strdup( tag ); if( !h->tag ) { @@ -93,16 +135,12 @@ tr_handle_t * tr_init( const char * tag ) return NULL; } + h->peerMgr = tr_peerMgrNew( h ); /* Azureus identity */ for( i=0; i < TR_AZ_ID_LEN; ++i ) h->azId[i] = tr_rand( 0xff ); -#ifndef WIN32 - /* Don't exit when writing on a broken socket */ - signal( SIGPIPE, SIG_IGN ); -#endif - /* Initialize rate and file descripts controls */ h->upload = tr_rcInit(); h->download = tr_rcInit(); @@ -126,6 +164,13 @@ void tr_setBindPort( tr_handle_t * h, int port ) tr_sharedSetPort( h->shared, port ); } +int +tr_getPublicPort( const tr_handle_t * h ) +{ + assert( h != NULL ); + return tr_sharedGetPublicPort( h->shared ); +} + void tr_natTraversalEnable( tr_handle_t * h, int enable ) { tr_sharedLock( h->shared ); @@ -133,9 +178,9 @@ void tr_natTraversalEnable( tr_handle_t * h, int enable ) tr_sharedUnlock( h->shared ); } -tr_handle_status_t * tr_handleStatus( tr_handle_t * h ) +tr_handle_status * tr_handleStatus( tr_handle_t * h ) { - tr_handle_status_t * s; + tr_handle_status * s; h->statCur = ( h->statCur + 1 ) % 2; s = &h->stats[h->statCur]; @@ -171,10 +216,8 @@ tr_setGlobalSpeedLimit( tr_handle_t * h, { if( up_or_down == TR_DOWN ) tr_rcSetLimit( h->download, KiB_sec ); - else { + else tr_rcSetLimit( h->upload, KiB_sec ); - tr_sharedSetLimit( h->shared, KiB_sec ); - } } void @@ -194,18 +237,18 @@ tr_getGlobalSpeedLimit( tr_handle_t * h, void tr_torrentRates( tr_handle_t * h, float * dl, float * ul ) { - tr_torrent_t * tor; + tr_torrent * tor; *dl = 0.0; *ul = 0.0; tr_sharedLock( h->shared ); for( tor = h->torrentList; tor; tor = tor->next ) { - tr_torrentReaderLock( tor ); + tr_torrentLock( tor ); if( tor->cpStatus == TR_CP_INCOMPLETE ) *dl += tr_rcRate( tor->download ); *ul += tr_rcRate( tor->upload ); - tr_torrentReaderUnlock( tor ); + tr_torrentUnlock( tor ); } tr_sharedUnlock( h->shared ); } @@ -217,7 +260,7 @@ int tr_torrentCount( tr_handle_t * h ) void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d ) { - tr_torrent_t * tor, * next; + tr_torrent * tor, * next; for( tor = h->torrentList; tor; tor = next ) { @@ -226,21 +269,47 @@ void tr_torrentIterate( tr_handle_t * h, tr_callback_t func, void * d ) } } -void tr_close( tr_handle_t * h ) +static void +tr_closeImpl( void * vh ) { + tr_handle_t * h = vh; +fprintf( stderr, "in tr_closeImpl\n" ); + tr_peerMgrFree( h->peerMgr ); +fprintf( stderr, "calling mgr free\n" ); + tr_rcClose( h->upload ); tr_rcClose( h->download ); - - tr_eventClose( h ); + +fprintf( stderr, "calling shared close\n" ); tr_sharedClose( h->shared ); +fprintf( stderr, "calling fd close\n" ); tr_fdClose(); + +fprintf( stderr, "setting h->closed to TRUE\n" ); + h->isClosed = TRUE; +} +void +tr_close( tr_handle_t * h ) +{ + fprintf( stderr, "torrentCount is %d\n", h->torrentCount ); + assert( tr_torrentCount( h ) == 0 ); + +fprintf( stderr, "here I am in tr_close...\n" ); + tr_runInEventThread( h, tr_closeImpl, h ); + while( !h->isClosed ) + tr_wait( 200 ); + + tr_eventClose( h ); + while( h->events != NULL ) { + fprintf( stderr, "waiting for libevent thread to close...\n" ); + tr_wait( 200 ); + } + free( h->tag ); free( h ); - - tr_netResolveThreadClose(); } -tr_torrent_t ** +tr_torrent ** tr_loadTorrents ( tr_handle_t * h, const char * destination, int flags, @@ -250,8 +319,8 @@ tr_loadTorrents ( tr_handle_t * h, struct stat sb; DIR * odir = NULL; const char * torrentDir = tr_getTorrentsDirectory( ); - tr_torrent_t ** torrents; - tr_list_t *l=NULL, *list=NULL; + tr_torrent ** torrents; + tr_list *l=NULL, *list=NULL; if( !stat( torrentDir, &sb ) && S_ISDIR( sb.st_mode ) @@ -262,7 +331,7 @@ tr_loadTorrents ( tr_handle_t * h, { if( d->d_name && d->d_name[0]!='.' ) /* skip dotfiles, ., and .. */ { - tr_torrent_t * tor; + tr_torrent * tor; char path[MAX_PATH_LENGTH]; tr_buildPath( path, sizeof(path), torrentDir, d->d_name, NULL ); tor = tr_torrentInit( h, path, destination, flags, NULL ); @@ -276,9 +345,9 @@ tr_loadTorrents ( tr_handle_t * h, closedir( odir ); } - torrents = tr_new( tr_torrent_t*, n ); + torrents = tr_new( tr_torrent*, n ); for( i=0, l=list; l!=NULL; l=l->next ) - torrents[i++] = (tr_torrent_t*) l->data; + torrents[i++] = (tr_torrent*) l->data; assert( i==n ); tr_list_free( &list ); diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index fe5c106e9..591013cef 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -101,10 +101,29 @@ extern "C" { * deleting a torrent used by another. The following tags are used: * beos cli daemon gtk macosx **********************************************************************/ -typedef struct tr_handle_s tr_handle_t; -tr_handle_t * tr_init( const char * tag ); -typedef struct tr_tracker_info_s tr_tracker_info_t; +typedef struct tr_handle tr_handle; +typedef struct tr_handle tr_handle_t; + +tr_handle * tr_init( const char * tag ); + +typedef struct tr_tracker_info tr_tracker_info; +typedef struct tr_tracker_info tr_tracker_info_t; + +/** +*** +**/ + +typedef enum +{ + TR_ENCRYPTION_PREFERRED, + TR_ENCRYPTION_REQUIRED +} +tr_encryption_mode; + +tr_encryption_mode tr_getEncryptionMode( tr_handle * handle ); + +void tr_setEncryptionMode( tr_handle * handle, tr_encryption_mode mode ); /*********************************************************************** * tr_setMessageLevel @@ -122,7 +141,8 @@ int tr_getMessageLevel( void ); *********************************************************************** * Enable or disable message queuing **********************************************************************/ -typedef struct tr_msg_list_s tr_msg_list_t; +typedef struct tr_msg_list tr_msg_list; +typedef struct tr_msg_list tr_msg_list_t; void tr_setMessageQueuing( int ); /*********************************************************************** @@ -130,8 +150,8 @@ void tr_setMessageQueuing( int ); *********************************************************************** * Return a list of queued messages **********************************************************************/ -tr_msg_list_t * tr_getQueuedMessages( void ); -void tr_freeMessageList( tr_msg_list_t * list ); +tr_msg_list * tr_getQueuedMessages( void ); +void tr_freeMessageList( tr_msg_list * list ); /*********************************************************************** * tr_getPrefsDirectory @@ -147,7 +167,9 @@ const char * tr_getPrefsDirectory( void ); * Sets the port to listen for incoming peer connections. * This can be safely called even with active torrents. **********************************************************************/ -void tr_setBindPort( tr_handle_t *, int ); +void tr_setBindPort( tr_handle *, int ); + +int tr_getPublicPort( const tr_handle * ); /*********************************************************************** * tr_natTraversalEnable @@ -155,15 +177,16 @@ void tr_setBindPort( tr_handle_t *, int ); *********************************************************************** * Enable or disable NAT traversal using NAT-PMP or UPnP IGD. **********************************************************************/ -void tr_natTraversalEnable( tr_handle_t *, int enable ); +void tr_natTraversalEnable( tr_handle *, int enable ); /*********************************************************************** * tr_handleStatus *********************************************************************** * Returns some status info for the given handle. **********************************************************************/ -typedef struct tr_handle_status_s tr_handle_status_t; -tr_handle_status_t * tr_handleStatus( tr_handle_t * ); +typedef struct tr_handle_status tr_handle_status; +typedef struct tr_handle_status tr_handle_status_t; +tr_handle_status * tr_handleStatus( tr_handle * ); /*********************************************************************** @@ -171,7 +194,7 @@ tr_handle_status_t * tr_handleStatus( tr_handle_t * ); *********************************************************************** * Returns the count of open torrents **********************************************************************/ -int tr_torrentCount( tr_handle_t * h ); +int tr_torrentCount( tr_handle * h ); /*********************************************************************** @@ -179,9 +202,10 @@ int tr_torrentCount( tr_handle_t * h ); *********************************************************************** * Iterates on open torrents **********************************************************************/ -typedef struct tr_torrent_s tr_torrent_t; -typedef void (*tr_callback_t) ( tr_torrent_t *, void * ); -void tr_torrentIterate( tr_handle_t *, tr_callback_t, void * ); +typedef struct tr_torrent tr_torrent; +typedef struct tr_torrent tr_torrent_t; +typedef void (*tr_callback_t) ( tr_torrent *, void * ); +void tr_torrentIterate( tr_handle *, tr_callback_t, void * ); /*********************************************************************** @@ -196,31 +220,31 @@ typedef enum TR_SPEEDLIMIT_SINGLE, /* only follow the per-torrent limit */ TR_SPEEDLIMIT_UNLIMITED /* no limits at all */ } -tr_speedlimit_t; +tr_speedlimit; -void tr_torrentSetSpeedMode( tr_torrent_t * tor, - int up_or_down, - tr_speedlimit_t mode ); +void tr_torrentSetSpeedMode( tr_torrent * tor, + int up_or_down, + tr_speedlimit mode ); -tr_speedlimit_t tr_torrentGetSpeedMode( const tr_torrent_t * tor, - int up_or_down); +tr_speedlimit tr_torrentGetSpeedMode( const tr_torrent * tor, + int up_or_down); -void tr_torrentSetSpeedLimit( tr_torrent_t * tor, - int up_or_down, - int single_KiB_sec ); +void tr_torrentSetSpeedLimit( tr_torrent * tor, + int up_or_down, + int single_KiB_sec ); -int tr_torrentGetSpeedLimit( const tr_torrent_t * tor, - int up_or_down ); +int tr_torrentGetSpeedLimit( const tr_torrent * tor, + int up_or_down ); -void tr_setUseGlobalSpeedLimit( tr_handle_t * handle, +void tr_setUseGlobalSpeedLimit( tr_handle * handle, int up_or_down, int use_flag ); -void tr_setGlobalSpeedLimit( tr_handle_t * handle, +void tr_setGlobalSpeedLimit( tr_handle * handle, int up_or_down, int global_KiB_sec ); -void tr_getGlobalSpeedLimit( tr_handle_t * handle, +void tr_getGlobalSpeedLimit( tr_handle * handle, int up_or_down, int * setme_is_enabled, int * setme_KiBsec ); @@ -239,50 +263,50 @@ enum typedef int8_t tr_priority_t; /* set a batch of files to a particular priority. */ -void tr_torrentSetFilePriorities( tr_torrent_t * tor, - int * files, - int fileCount, - tr_priority_t priority ); +void tr_torrentSetFilePriorities( tr_torrent * tor, + int * files, + int fileCount, + tr_priority_t priority ); /* single-file form of tr_torrentPrioritizeFiles. * priority must be one of TR_PRI_NORMAL, _HIGH, or _LOW */ -void tr_torrentSetFilePriority( tr_torrent_t *, int file, tr_priority_t priority ); +void tr_torrentSetFilePriority( tr_torrent *, int file, tr_priority_t priority ); /* returns a malloc()ed array of tor->info.fileCount items, * each holding a value of TR_PRI_NORMAL, _HIGH, or _LOW. free the array when done. */ -tr_priority_t* tr_torrentGetFilePriorities( const tr_torrent_t * ); +tr_priority_t* tr_torrentGetFilePriorities( const tr_torrent * ); /* single-file form of tr_torrentGetFilePriorities. * returns one of TR_PRI_NORMAL, _HIGH, or _LOW. */ -tr_priority_t tr_torrentGetFilePriority( const tr_torrent_t *, int file ); +tr_priority_t tr_torrentGetFilePriority( const tr_torrent *, int file ); /* returns true if the file's `download' flag is set */ -int tr_torrentGetFileDL( const tr_torrent_t *, int file ); +int tr_torrentGetFileDL( const tr_torrent *, int file ); /* set a batch of files to be downloaded or not. */ -void tr_torrentSetFileDLs ( tr_torrent_t * tor, +void tr_torrentSetFileDLs ( tr_torrent * tor, int * files, int fileCount, int do_download ); /* single-file form of tr_torrentSetFileDLs */ -void tr_torrentSetFileDL( tr_torrent_t *, int file, int do_download ); +void tr_torrentSetFileDL( tr_torrent *, int file, int do_download ); /*********************************************************************** * tr_torrentRates *********************************************************************** * Gets the total download and upload rates **********************************************************************/ -void tr_torrentRates( tr_handle_t *, float *, float * ); +void tr_torrentRates( tr_handle *, float *, float * ); /*********************************************************************** * tr_close *********************************************************************** * Frees memory allocated by tr_init. **********************************************************************/ -void tr_close( tr_handle_t * ); +void tr_close( tr_handle * ); @@ -291,10 +315,10 @@ void tr_close( tr_handle_t * ); * This can be used at startup to kickstart all the torrents * from the previous session. */ -tr_torrent_t ** tr_loadTorrents ( tr_handle_t * h, - const char * destination, - int flags, - int * setmeCount ); +tr_torrent ** tr_loadTorrents ( tr_handle * h, + const char * destination, + int flags, + int * setmeCount ); /*********************************************************************** @@ -311,13 +335,14 @@ tr_torrent_t ** tr_loadTorrents ( tr_handle_t * h, #define TR_EUNSUPPORTED 2 #define TR_EDUPLICATE 3 #define TR_EOTHER 666 -tr_torrent_t * tr_torrentInit( tr_handle_t * handle, - const char * metainfo_filename, - const char * destination, - int flags, - int * setme_error ); +tr_torrent * tr_torrentInit( tr_handle * handle, + const char * metainfo_filename, + const char * destination, + int flags, + int * setme_error ); -typedef struct tr_info_s tr_info_t; +typedef struct tr_info tr_info; +typedef struct tr_info tr_info_t; /** * Parses the specified metainfo file. @@ -334,20 +359,20 @@ typedef struct tr_info_s tr_info_t; * it will be filled with the metadata's info. You'll need to * call tr_metainfoFree( setme_info ) when done with it. */ -int tr_torrentParse( const tr_handle_t * handle, - const char * metainfo_filename, - const char * destination, - tr_info_t * setme_info ); +int tr_torrentParse( const tr_handle * handle, + const char * metainfo_filename, + const char * destination, + tr_info * setme_info ); /** * Parses the cached metainfo file that matches the given hash string. * See tr_torrentParse() for a description of the arguments */ int -tr_torrentParseHash( const tr_handle_t * h, - const char * hashStr, - const char * destination, - tr_info_t * setme_info ); +tr_torrentParseHash( const tr_handle * h, + const char * hashStr, + const char * destination, + tr_info * setme_info ); /*********************************************************************** @@ -356,10 +381,10 @@ tr_torrentParseHash( const tr_handle_t * h, * Like tr_torrentInit, except the actual torrent data is passed in * instead of the filename. **********************************************************************/ -tr_torrent_t * tr_torrentInitData( tr_handle_t *, - const uint8_t * data, size_t size, - const char * destination, - int flags, int * error ); +tr_torrent * tr_torrentInitData( tr_handle *, + const uint8_t * data, size_t size, + const char * destination, + int flags, int * error ); /*********************************************************************** * tr_torrentInitSaved @@ -368,10 +393,10 @@ tr_torrent_t * tr_torrentInitData( tr_handle_t *, * the hash string of a saved torrent file instead of a filename. There * are currently no valid flags for this function. **********************************************************************/ -tr_torrent_t * tr_torrentInitSaved( tr_handle_t *, - const char * hashStr, - const char * destination, - int flags, int * error ); +tr_torrent * tr_torrentInitSaved( tr_handle *, + const char * hashStr, + const char * destination, + int flags, int * error ); /*********************************************************************** * tr_torrentDisablePex @@ -380,9 +405,9 @@ tr_torrent_t * tr_torrentInitSaved( tr_handle_t *, * enabled by default, except for private torrents where pex is * disabled and cannot be enabled. **********************************************************************/ -void tr_torrentDisablePex( tr_torrent_t *, int disable ); +void tr_torrentDisablePex( tr_torrent *, int disable ); -const tr_info_t * tr_torrentInfo( const tr_torrent_t * ); +const tr_info * tr_torrentInfo( const tr_torrent * ); #if 0 /*********************************************************************** @@ -394,11 +419,11 @@ const tr_info_t * tr_torrentInfo( const tr_torrent_t * ); * replied with some error. tr_torrentScrape may block up to 20 seconds * before returning. **********************************************************************/ -int tr_torrentScrape( tr_torrent_t *, int * s, int * l, int * d ); +int tr_torrentScrape( tr_torrent *, int * s, int * l, int * d ); #endif -void tr_torrentSetFolder( tr_torrent_t *, const char * ); -const char * tr_torrentGetFolder( const tr_torrent_t * ); +void tr_torrentSetFolder( tr_torrent *, const char * ); +const char * tr_torrentGetFolder( const tr_torrent * ); /*********************************************************************** * tr_torrentStart @@ -406,7 +431,7 @@ const char * tr_torrentGetFolder( const tr_torrent_t * ); * Starts downloading. The download is launched in a seperate thread, * therefore tr_torrentStart returns immediately. **********************************************************************/ -void tr_torrentStart( tr_torrent_t * ); +void tr_torrentStart( tr_torrent * ); /*********************************************************************** * tr_torrentStop @@ -419,7 +444,7 @@ void tr_torrentStart( tr_torrent_t * ); * - by tr_torrentClose if you choose to remove the torrent without * waiting any further. **********************************************************************/ -void tr_torrentStop( tr_torrent_t * ); +void tr_torrentStop( tr_torrent * ); /*********************************************************************** * tr_getComplete, tr_getIncomplete and tr_getPartial @@ -427,9 +452,9 @@ void tr_torrentStop( tr_torrent_t * ); * The first call after a torrent changed state returns 1. Returns 0 * in other cases. **********************************************************************/ -int tr_getIncomplete( tr_torrent_t * tor ); -int tr_getDone( tr_torrent_t * tor ); -int tr_getComplete( tr_torrent_t * tor ); +int tr_getIncomplete( tr_torrent * tor ); +int tr_getDone( tr_torrent * tor ); +int tr_getComplete( tr_torrent * tor ); /** @@ -445,33 +470,36 @@ int tr_getComplete( tr_torrent_t * tor ); * tr_torrentCanManualUpdate(). */ -void tr_manualUpdate( tr_torrent_t * ); +void tr_manualUpdate( tr_torrent * ); -int tr_torrentCanManualUpdate( const tr_torrent_t * ); +int tr_torrentCanManualUpdate( const tr_torrent * ); /*********************************************************************** * tr_torrentStat *********************************************************************** - * Returns a pointer to an tr_stat_t structure with updated information + * Returns a pointer to an tr_stat structure with updated information * on the torrent. The structure belongs to libtransmission (do not * free it) and is guaranteed to be unchanged until the next call to * tr_torrentStat. * The interface should call this function every second or so in order * to update itself. **********************************************************************/ -typedef struct tr_stat_s tr_stat_t; -const tr_stat_t * tr_torrentStat( tr_torrent_t * ); +typedef struct tr_stat tr_stat; +typedef struct tr_stat tr_stat_t; +const tr_stat * tr_torrentStat( tr_torrent * ); /*********************************************************************** * tr_torrentPeers ***********************************************************************/ -typedef struct tr_peer_stat_s tr_peer_stat_t; -tr_peer_stat_t * tr_torrentPeers( const tr_torrent_t *, int * peerCount ); -void tr_torrentPeersFree( tr_peer_stat_t *, int peerCount ); +typedef struct tr_peer_stat tr_peer_stat; +typedef struct tr_peer_stat tr_peer_stat_t; +tr_peer_stat * tr_torrentPeers( const tr_torrent *, int * peerCount ); +void tr_torrentPeersFree( tr_peer_stat *, int peerCount ); -typedef struct tr_file_stat_s tr_file_stat_t; -tr_file_stat_t * tr_torrentFiles( const tr_torrent_t *, int * fileCount ); -void tr_torrentFilesFree( tr_file_stat_t *, int fileCount ); +typedef struct tr_file_stat tr_file_stat; +typedef struct tr_file_stat tr_file_stat_t; +tr_file_stat * tr_torrentFiles( const tr_torrent *, int * fileCount ); +void tr_torrentFilesFree( tr_file_stat *, int fileCount ); /*********************************************************************** @@ -482,9 +510,9 @@ void tr_torrentFilesFree( tr_file_stat_t *, int fileCount ); * to either -1 if we have the piece, otherwise it is set to the number * of connected peers who have the piece. **********************************************************************/ -void tr_torrentAvailability( const tr_torrent_t *, int8_t * tab, int size ); +void tr_torrentAvailability( const tr_torrent *, int8_t * tab, int size ); -void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size ); +void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size ); /*********************************************************************** * tr_torrentRemoveSaved @@ -492,9 +520,9 @@ void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size ) * Removes the saved copy of a torrent file for torrents which the * TR_FLAG_SAVE flag is set. **********************************************************************/ -void tr_torrentRemoveSaved( tr_torrent_t * ); +void tr_torrentRemoveSaved( tr_torrent * ); -void tr_torrentRecheck( tr_torrent_t * ); +void tr_torrentRecheck( tr_torrent * ); /*********************************************************************** * tr_torrentClose @@ -502,13 +530,13 @@ void tr_torrentRecheck( tr_torrent_t * ); * Frees memory allocated by tr_torrentInit. If the torrent was running, * it is stopped first. **********************************************************************/ -void tr_torrentClose( tr_torrent_t * ); +void tr_torrentClose( tr_torrent * ); /*********************************************************************** - * tr_info_s + * tr_info **********************************************************************/ -typedef struct tr_file_s +typedef struct tr_file { uint64_t length; /* Length of the file, in bytes */ char name[MAX_PATH_LENGTH]; /* Path to the file */ @@ -518,17 +546,17 @@ typedef struct tr_file_s int lastPiece; /* ...lastPiece] to dl this file */ uint64_t offset; /* file begins at the torrent's nth byte */ } -tr_file_t; +tr_file; -typedef struct tr_piece_s +typedef struct tr_piece { uint8_t hash[SHA_DIGEST_LENGTH]; /* pieces hash */ int8_t priority; /* TR_PRI_HIGH, _NORMAL, or _LOW */ int8_t dnd; /* nonzero if the piece shouldn't be downloaded */ } -tr_piece_t; +tr_piece; -struct tr_info_s +struct tr_info { /* Path to torrent */ char torrent[MAX_PATH_LENGTH]; @@ -547,7 +575,7 @@ struct tr_info_s /* Tracker info */ struct { - tr_tracker_info_t * list; + tr_tracker_info * list; int count; } * trackerList; int trackerTiers; @@ -562,12 +590,12 @@ struct tr_info_s int pieceSize; int pieceCount; uint64_t totalSize; - tr_piece_t * pieces; + tr_piece * pieces; /* Files info */ int multifile; int fileCount; - tr_file_t * files; + tr_file * files; }; typedef enum @@ -598,9 +626,9 @@ typedef enum cp_status_t; /*********************************************************************** - * tr_stat_s + * tr_stat **********************************************************************/ -struct tr_stat_s +struct tr_stat { torrent_status_t status; cp_status_t cpStatus; @@ -608,7 +636,7 @@ struct tr_stat_s int error; char errorString[128]; - const tr_tracker_info_t * tracker; + const tr_tracker_info * tracker; float recheckProgress; float percentComplete; @@ -639,38 +667,40 @@ struct tr_stat_s uint64_t activityDate; }; -struct tr_file_stat_s +struct tr_file_stat { uint64_t bytesCompleted; float progress; cp_status_t completionStatus; }; -struct tr_peer_stat_s +struct tr_peer_stat { char addr[INET_ADDRSTRLEN]; const char * client; - int isConnected; - int from; - float progress; - int port; + unsigned int isConnected : 1; + unsigned int isEncrypted : 1; + unsigned int isDownloading : 1; + unsigned int isUploading : 1; + + uint8_t from; + uint16_t port; - int isDownloading; - int isUploading; - float downloadFromRate; - float uploadToRate; + float progress; + float downloadFromRate; + float uploadToRate; }; -struct tr_msg_list_s +struct tr_msg_list { - int level; - time_t when; - char * message; - struct tr_msg_list_s * next; + int level; + time_t when; + char * message; + struct tr_msg_list * next; }; -struct tr_tracker_info_s +struct tr_tracker_info { char * address; int port; @@ -678,7 +708,7 @@ struct tr_tracker_info_s char * scrape; }; -struct tr_handle_status_s +struct tr_handle_status { #define TR_NAT_TRAVERSAL_MAPPING 1 #define TR_NAT_TRAVERSAL_MAPPED 2 diff --git a/libtransmission/trevent.c b/libtransmission/trevent.c index 5cd23dc2f..d97bb68df 100644 --- a/libtransmission/trevent.c +++ b/libtransmission/trevent.c @@ -16,21 +16,18 @@ #include #include +#include #include /* for evhttp */ #include /* for evhttp */ -#ifdef WIN32 - #include - #define pipe(f) _pipe(f, 1000, _O_BINARY) -#else - #include -#endif - #include +#include #include #include "transmission.h" +#include "list.h" #include "platform.h" +#include "trevent.h" #include "utils.h" /* #define DEBUG */ @@ -44,96 +41,137 @@ **** ***/ -typedef struct tr_event_handle_s +typedef struct tr_event_handle { - int fds[2]; - tr_lock_t * lock; + tr_lock * lock; tr_handle_t * h; - tr_thread_t * thread; + tr_thread * thread; + tr_list * commands; struct event_base * base; - struct event pipeEvent; + struct event pulse; + struct timeval pulseInterval; + uint8_t die; + + int timerCount; } -tr_event_handle_t; +tr_event_handle; #ifdef DEBUG static int reads = 0; static int writes = 0; #endif - -void -readFromPipe( int fd, short eventType UNUSED, void * unused UNUSED ) +enum mode { - char ch; - int ret; - struct event * event; - struct timeval interval; + TR_EV_EVHTTP_MAKE_REQUEST, + TR_EV_BUFFEREVENT_SET, + TR_EV_BUFFEREVENT_WRITE, + TR_EV_BUFFEREVENT_FREE, + TR_EV_TIMER_ADD, + TR_EV_TIMER_DEL, + TR_EV_EXEC +}; + +typedef int timer_func(void*); + +struct tr_timer +{ + struct event event; + struct timeval tv; + timer_func * func; + void * user_data; + struct tr_event_handle * eh; + uint8_t inCallback; +}; + +struct tr_event_command +{ + int mode; + + struct tr_timer * timer; + struct evhttp_connection * evcon; struct evhttp_request * req; - enum evhttp_cmd_type type; + enum evhttp_cmd_type evtype; char * uri; + + struct bufferevent * bufev; + short enable; + short disable; char * buf; size_t buflen; - struct bufferevent * bufev; - short mode; -#ifdef DEBUG - fprintf( stderr, "reading...reads: [%d] writes: [%d]\n", ++reads, writes ); -#endif + void (*func)( void* ); + void * user_data; +}; - ch = '\0'; - do { - ret = read( fd, &ch, 1 ); - } while ( ret<0 && errno==EAGAIN ); +static void +pumpList( int i UNUSED, short s UNUSED, void * veh ) +{ + tr_event_handle * eh = veh; - if( ret < 0 ) + while( !eh->die ) { - tr_err( "Couldn't read from libevent pipe: %s", strerror(errno) ); + struct tr_event_command * cmd; + + /* get the next command */ + tr_lockLock( eh->lock ); + cmd = tr_list_pop_front( &eh->commands ); + tr_lockUnlock( eh->lock ); + if( cmd == NULL ) + break; + + /* process the command */ + switch( cmd->mode ) + { + case TR_EV_TIMER_ADD: + timeout_add( &cmd->timer->event, &cmd->timer->tv ); + ++eh->timerCount; + break; + + case TR_EV_TIMER_DEL: + event_del( &cmd->timer->event ); + tr_free( cmd->timer ); + --eh->timerCount; + break; + + case TR_EV_EVHTTP_MAKE_REQUEST: + evhttp_make_request( cmd->evcon, cmd->req, cmd->evtype, cmd->uri ); + tr_free( cmd->uri ); + break; + + case TR_EV_BUFFEREVENT_SET: + bufferevent_enable( cmd->bufev, cmd->enable ); + bufferevent_disable( cmd->bufev, cmd->disable ); + break; + + case TR_EV_BUFFEREVENT_WRITE: + bufferevent_write( cmd->bufev, cmd->buf, cmd->buflen ); + tr_free( cmd->buf ); + break; + + case TR_EV_BUFFEREVENT_FREE: + bufferevent_free( cmd->bufev ); + break; + + case TR_EV_EXEC: + (cmd->func)( cmd->user_data ); + break; + + default: + assert( 0 && "unhandled command type!" ); + } + + /* cleanup */ + tr_free( cmd ); } - else switch( ch ) - { - case 'd': /* event_del */ - read( fd, &event, sizeof(struct event*) ); - event_del( event ); - tr_free( event ); - break; - case 'e': /* event_add */ - read( fd, &event, sizeof(struct event*) ); - read( fd, &interval, sizeof(struct timeval) ); - event_add( event, &interval ); - break; - - case 'h': /* http_make_request */ - ret = read( fd, &evcon, sizeof(struct evhttp_connection*) ); - read( fd, &req, sizeof(struct evhttp_request*) ); - read( fd, &type, sizeof(enum evhttp_cmd_type) ); - read( fd, &uri, sizeof(char*) ); - evhttp_make_request( evcon, req, type, uri ); - tr_free( uri ); - break; - - case 'm': /* set bufferevent mode */ - read( fd, &bufev, sizeof(struct evhttp_request*) ); - mode = 0; - read( fd, &mode, sizeof(short) ); - bufferevent_enable( bufev, mode ); - mode = 0; - read( fd, &mode, sizeof(short) ); - bufferevent_disable( bufev, mode ); -fprintf( stderr, "after enable/disable, the mode is %hd\n", bufev->enabled ); - break; - - case 'w': /* bufferevent_write */ - read( fd, &bufev, sizeof(struct bufferevent*) ); - read( fd, &buf, sizeof(char*) ); - read( fd, &buflen, sizeof(size_t) ); - bufferevent_write( bufev, buf, buflen ); - tr_free( buf ); - break; - - default: - assert( 0 && "unhandled event pipe condition!" ); + if( !eh->die ) + timeout_add( &eh->pulse, &eh->pulseInterval ); + else { + assert( eh->timerCount == 0 ); + evdns_shutdown( FALSE ); + event_del( &eh->pulse ); } } @@ -157,92 +195,69 @@ logFunc( int severity, const char * message ) static void libeventThreadFunc( void * veh ) { - tr_event_handle_t * eh = (tr_event_handle_t *) veh; + tr_event_handle * eh = (tr_event_handle *) veh; tr_dbg( "Starting libevent thread" ); - eh->base = event_init( ); - event_set_log_callback( logFunc ); +#ifndef WIN32 + /* Don't exit when writing on a broken socket */ + signal( SIGPIPE, SIG_IGN ); +#endif - /* listen to the pipe's read fd */ - event_set( &eh->pipeEvent, eh->fds[0], EV_READ|EV_PERSIST, readFromPipe, NULL ); - event_add( &eh->pipeEvent, NULL ); + eh->base = event_init( ); + //event_set_log_callback( logFunc ); + evdns_init( ); + timeout_set( &eh->pulse, pumpList, veh ); + timeout_add( &eh->pulse, &eh->pulseInterval ); + eh->h->events = eh; event_dispatch( ); +fprintf( stderr, "w00t!!!!!!!!!!!!!!!!!!!\n" ); - event_del( &eh->pipeEvent ); tr_lockFree( eh->lock ); event_base_free( eh->base ); - tr_free( eh ); + eh->h->events = NULL; + + tr_free( eh ); tr_dbg( "Closing libevent thread" ); } void tr_eventInit( tr_handle_t * handle ) { - tr_event_handle_t * eh; + tr_event_handle * eh; - eh = tr_new0( tr_event_handle_t, 1 ); + eh = tr_new0( tr_event_handle, 1 ); eh->lock = tr_lockNew( ); - pipe( eh->fds ); eh->h = handle; - handle->events = eh; + eh->pulseInterval = timevalMsec( 20 ); eh->thread = tr_threadNew( libeventThreadFunc, eh, "libeventThreadFunc" ); } void tr_eventClose( tr_handle_t * handle ) { - tr_event_handle_t * eh = handle->events; + tr_event_handle * eh = handle->events; - event_base_loopexit( eh->base, NULL ); + tr_lockLock( eh->lock ); + tr_list_foreach( eh->commands, tr_free ); + tr_list_free( &eh->commands ); + eh->die = TRUE; + tr_lockUnlock( eh->lock ); + + //event_base_loopexit( eh->base, NULL ); } -void -tr_event_add( tr_handle_t * handle, - struct event * event, - struct timeval * interval ) +/** +*** +**/ + +static void +pushList( struct tr_event_handle * eh, struct tr_event_command * command ) { - if( tr_amInThread( handle->events->thread ) ) - { - event_add( event, interval ); - } - else - { - const char ch = 'e'; - int fd = handle->events->fds[1]; - tr_lock_t * lock = handle->events->lock; - - tr_lockLock( lock ); - tr_dbg( "writing event to pipe: event.ev_arg is %p", event->ev_arg ); - write( fd, &ch, 1 ); - write( fd, &event, sizeof(struct event*) ); - write( fd, interval, sizeof(struct timeval) ); - tr_lockUnlock( lock ); - } -} - -void -tr_event_del( tr_handle_t * handle, - struct event * event ) -{ - if( tr_amInThread( handle->events->thread ) ) - { - event_del( event ); - tr_free( event ); - } - else - { - const char ch = 'd'; - int fd = handle->events->fds[1]; - tr_lock_t * lock = handle->events->lock; - - tr_lockLock( lock ); - tr_dbg( "writing event to pipe: del event %p", event ); - write( fd, &ch, 1 ); - write( fd, &event, sizeof(struct event*) ); - tr_lockUnlock( lock ); - } + tr_lockLock( eh->lock ); + tr_list_append( &eh->commands, command ); + tr_lockUnlock( eh->lock ); } void @@ -252,25 +267,17 @@ tr_evhttp_make_request (tr_handle_t * handle, enum evhttp_cmd_type type, char * uri) { - if( tr_amInThread( handle->events->thread ) ) - { + if( tr_amInThread( handle->events->thread ) ) { evhttp_make_request( evcon, req, type, uri ); tr_free( uri ); - } - else - { - const char ch = 'h'; - int fd = handle->events->fds[1]; - tr_lock_t * lock = handle->events->lock; - - tr_lockLock( lock ); - tr_dbg( "writing HTTP req to pipe: req.cb_arg is %p", req->cb_arg ); - write( fd, &ch, 1 ); - write( fd, &evcon, sizeof(struct evhttp_connection*) ); - write( fd, &req, sizeof(struct evhttp_request*) ); - write( fd, &type, sizeof(enum evhttp_cmd_type) ); - write( fd, &uri, sizeof(char*) ); - tr_lockUnlock( lock ); + } else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_EVHTTP_MAKE_REQUEST; + cmd->evcon = evcon; + cmd->req = req; + cmd->evtype = type; + cmd->uri = uri; + pushList( handle->events, cmd ); } } @@ -281,48 +288,170 @@ tr_bufferevent_write( tr_handle_t * handle, size_t buflen ) { if( tr_amInThread( handle->events->thread ) ) - { bufferevent_write( bufev, (void*)buf, buflen ); - } - else - { - const char ch = 'w'; - int fd = handle->events->fds[1]; - tr_lock_t * lock = handle->events->lock; - char * local = tr_strndup( buf, buflen ); - - tr_lockLock( lock ); - tr_dbg( "writing bufferevent_write pipe" ); - write( fd, &ch, 1 ); - write( fd, &bufev, sizeof(struct bufferevent*) ); - write( fd, &local, sizeof(char*) ); - write( fd, &buflen, sizeof(size_t) ); - tr_lockUnlock( lock ); + else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_BUFFEREVENT_WRITE; + cmd->bufev = bufev; + cmd->buf = tr_strndup( buf, buflen ); + cmd->buflen = buflen; + pushList( handle->events, cmd ); } } void -tr_setBufferEventMode( struct tr_handle_s * handle, +tr_setBufferEventMode( struct tr_handle * handle, struct bufferevent * bufev, short mode_enable, short mode_disable ) { - if( tr_amInThread( handle->events->thread ) ) - { + if( tr_amInThread( handle->events->thread ) ) { bufferevent_enable( bufev, mode_enable ); bufferevent_disable( bufev, mode_disable ); - } - else - { - const char ch = 'm'; - int fd = handle->events->fds[1]; - tr_lock_t * lock = handle->events->lock; - - tr_lockLock( lock ); - write( fd, &ch, 1 ); - write( fd, &bufev, sizeof(struct bufferevent*) ); - write( fd, &mode_enable, sizeof(short) ); - write( fd, &mode_disable, sizeof(short) ); - tr_lockUnlock( lock ); + } else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_BUFFEREVENT_SET; + cmd->bufev = bufev; + cmd->enable = mode_enable; + cmd->disable = mode_disable; + pushList( handle->events, cmd ); + } +} + +/** +*** +**/ + +static int +timerCompareFunc( const void * va, const void * vb ) +{ + const struct tr_event_command * a = va; + const struct tr_timer * b = vb; + return a->timer == b ? 0 : 1; +} + +static void +timerCallback( int fd UNUSED, short event UNUSED, void * vtimer ) +{ + int more; + struct tr_timer * timer = vtimer; + void * del; + + del = tr_list_remove( &timer->eh->commands, timer, timerCompareFunc ); + + if( del != NULL ) /* there's a TIMER_DEL command queued for this timer... */ + more = FALSE; + else { + timer->inCallback = 1; + more = (*timer->func)( timer->user_data ); + timer->inCallback = 0; + } + + if( more ) + timeout_add( &timer->event, &timer->tv ); + else + tr_timerFree( &timer ); +} + +void +tr_timerFree( tr_timer ** ptimer ) +{ + tr_timer * timer; + + /* zero out the argument passed in */ + assert( ptimer ); + timer = *ptimer; + *ptimer = NULL; + + /* destroy the timer directly or via the command queue */ + if( timer!=NULL && !timer->inCallback ) { + if( tr_amInThread( timer->eh->thread ) ) { + --timer->eh->timerCount; + event_del( &timer->event ); + tr_free( timer ); + } else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_TIMER_DEL; + cmd->timer = timer; + pushList( timer->eh, cmd ); + } + } +} + +tr_timer* +tr_timerNew( struct tr_handle * handle, + timer_func func, + void * user_data, + int timeout_milliseconds ) +{ + tr_timer * timer = tr_new0( tr_timer, 1 ); + timer->tv = timevalMsec( timeout_milliseconds ); + timer->func = func; + timer->user_data = user_data; + timer->eh = handle->events; + timeout_set( &timer->event, timerCallback, timer ); + + if( tr_amInThread( handle->events->thread ) ) { + timeout_add( &timer->event, &timer->tv ); + ++handle->events->timerCount; + } else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_TIMER_ADD; + cmd->timer = timer; + pushList( handle->events, cmd ); + } + + return timer; +} + +void +tr_runInEventThread( struct tr_handle * handle, + void func( void* ), + void * user_data ) +{ + if( tr_amInThread( handle->events->thread ) ) + (func)( user_data ); + else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_EXEC; + cmd->func = func; + cmd->user_data = user_data; + pushList( handle->events, cmd ); + } +} + + +/** +*** +**/ + +static int +bufCompareFunc( const void * va, const void * vb ) +{ + const struct tr_event_command * a = va; + const struct bufferevent * b = vb; + return a->bufev == b ? 0 : 1; +} + +void +tr_bufferevent_free( struct tr_handle * handle, + struct bufferevent * bufev ) +{ + void * v; + tr_event_handle * eh = handle->events; + + /* purge pending commands from the list */ + tr_lockLock( eh->lock ); + while(( v = tr_list_remove( &eh->commands, bufev, bufCompareFunc ) )) + tr_free( v ); + tr_lockUnlock( eh->lock ); + + if( tr_amInThread( handle->events->thread ) ) + bufferevent_free( bufev ); + else { + struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 ); + cmd->mode = TR_EV_BUFFEREVENT_FREE; + cmd->bufev = bufev; + pushList( handle->events, cmd ); } } diff --git a/libtransmission/trevent.h b/libtransmission/trevent.h index a0f1f034b..51bf520d5 100644 --- a/libtransmission/trevent.h +++ b/libtransmission/trevent.h @@ -17,9 +17,9 @@ /** **/ -void tr_eventInit( struct tr_handle_s * tr_handle ); +void tr_eventInit( struct tr_handle * tr_handle ); -void tr_eventClose( struct tr_handle_s * tr_handle ); +void tr_eventClose( struct tr_handle * tr_handle ); /** **/ @@ -30,27 +30,49 @@ struct evhttp_request; struct evhttp_connection; struct bufferevent; -void tr_event_add( struct tr_handle_s * tr_handle, - struct event * event, - struct timeval * interval ); - -void tr_event_del( struct tr_handle_s * tr_handle, - struct event * event ); - -void tr_evhttp_make_request (struct tr_handle_s * tr_handle, +void tr_evhttp_make_request (struct tr_handle * tr_handle, struct evhttp_connection * evcon, struct evhttp_request * req, enum evhttp_cmd_type type, char * uri); -void tr_bufferevent_write( struct tr_handle_s * tr_handle, +void tr_bufferevent_write( struct tr_handle * tr_handle, struct bufferevent * bufferEvent, const void * buf, size_t buflen ); -void tr_setBufferEventMode( struct tr_handle_s * tr_handle, +void tr_bufferevent_free( struct tr_handle * handle, + struct bufferevent * bufev ); + + +void tr_setBufferEventMode( struct tr_handle * tr_handle, struct bufferevent * bufferEvent, short mode_enable, short mode_disable ); +/** +*** +**/ + +typedef struct tr_timer tr_timer; + +/** + * Calls timer_func(user_data) after the specified interval. + * The timer is freed if timer_func returns zero. + * Otherwise, it's called again after the same interval. + */ +tr_timer* tr_timerNew( struct tr_handle * handle, + int func( void * user_data ), + void * user_data, + int timeout_milliseconds ); + +/** + * Frees a timer and sets the timer pointer to NULL. + */ +void tr_timerFree( tr_timer ** timer ); + +void tr_runInEventThread( struct tr_handle * handle, + void func( void* ), + void * user_data ); + #endif diff --git a/libtransmission/upnp.c b/libtransmission/upnp.c index 520a57a89..95539879c 100644 --- a/libtransmission/upnp.c +++ b/libtransmission/upnp.c @@ -210,6 +210,7 @@ tr_upnpInit( void ) vlog = fopen( path, "a" ); stupid_api = time( NULL ); fprintf( vlog, "opened log at %s\n\n", ctime( &stupid_api ) ); + fflush( vlog ); } #endif @@ -306,10 +307,9 @@ tr_upnpClose( tr_upnp_t * upnp ) #ifdef VERBOSE_LOG if( NULL != vlog ) { - fflush( vlog ); + fclose( vlog ); } -#endif - +#endif } void @@ -403,6 +403,7 @@ sendSSDP( int fd ) fprintf( vlog, "send ssdp message, %i bytes:\n", len ); fwrite( buf, 1, len, vlog ); fputs( "\n\n", vlog ); + fflush( vlog ); #endif if( 0 > sendto( fd, buf, len, 0, @@ -524,6 +525,7 @@ recvSSDP( int fd, char * buf, int * len ) fprintf( vlog, "receive ssdp message, %i bytes:\n", *len ); fwrite( buf, 1, *len, vlog ); fputs( "\n\n", vlog ); + fflush( vlog ); #endif return TR_NET_OK; } @@ -964,6 +966,7 @@ devicePulseGetHttp( struct upnp_dev * dev ) fprintf( vlog, "\n\nsend http message, %i bytes (body):\n", len ); fwrite( body, 1, len, vlog ); fputs( "\n\n", vlog ); + fflush( vlog ); } #endif @@ -1007,6 +1010,7 @@ devicePulseHttp( struct upnp_dev * dev, fprintf( vlog, "receive http message, %i bytes:\n", hlen ); fwrite( headers, 1, hlen, vlog ); fputs( "\n\n", vlog ); + fflush( vlog ); #endif code = tr_httpResponseCode( headers, hlen ); if( SOAP_METHOD_NOT_ALLOWED == code && !dev->soapretry ) diff --git a/libtransmission/utils.c b/libtransmission/utils.c index 75a3eec97..15c05686c 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -49,11 +49,11 @@ #define SPRINTF_BUFSIZE 100 -static tr_lock_t * messageLock = NULL; -static int messageLevel = 0; -static int messageQueuing = FALSE; -static tr_msg_list_t * messageQueue = NULL; -static tr_msg_list_t ** messageQueueTail = &messageQueue; +static tr_lock * messageLock = NULL; +static int messageLevel = 0; +static int messageQueuing = FALSE; +static tr_msg_list * messageQueue = NULL; +static tr_msg_list ** messageQueueTail = &messageQueue; void tr_msgInit( void ) { @@ -89,9 +89,9 @@ void tr_setMessageQueuing( int enabled ) tr_lockUnlock( messageLock ); } -tr_msg_list_t * tr_getQueuedMessages( void ) +tr_msg_list * tr_getQueuedMessages( void ) { - tr_msg_list_t * ret; + tr_msg_list * ret; assert( NULL != messageLock ); tr_lockLock( messageLock ); @@ -103,9 +103,9 @@ tr_msg_list_t * tr_getQueuedMessages( void ) return ret; } -void tr_freeMessageList( tr_msg_list_t * list ) +void tr_freeMessageList( tr_msg_list * list ) { - tr_msg_list_t * next; + tr_msg_list * next; while( NULL != list ) { @@ -118,8 +118,8 @@ void tr_freeMessageList( tr_msg_list_t * list ) void tr_msg( int level, char * msg, ... ) { - va_list args1, args2; - tr_msg_list_t * newmsg; + va_list args1, args2; + tr_msg_list * newmsg; int len1, len2; assert( NULL != messageLock ); @@ -185,6 +185,65 @@ int tr_rand( int sup ) return rand() % sup; } +/*** +**** +***/ + +void +tr_set_compare( const void * va, size_t aCount, + const void * vb, size_t bCount, + int compare( const void * a, const void * b ), + size_t elementSize, + tr_set_func in_a_cb, + tr_set_func in_b_cb, + tr_set_func in_both_cb, + void * userData ) +{ + const uint8_t * a = (const uint8_t *) va; + const uint8_t * b = (const uint8_t *) vb; + const uint8_t * aend = a + elementSize*aCount; + const uint8_t * bend = b + elementSize*bCount; + + while( a!=aend || b!=bend ) + { + if( a==aend ) + { + (*in_b_cb)( (void*)b, userData ); + b += elementSize; + } + else if ( b==bend ) + { + (*in_a_cb)( (void*)a, userData ); + a += elementSize; + } + else + { + const int val = (*compare)( a, b ); + + if( !val ) + { + (*in_both_cb)( (void*)a, userData ); + a += elementSize; + b += elementSize; + } + else if( val < 0 ) + { + (*in_a_cb)( (void*)a, userData ); + a += elementSize; + } + else if( val > 0 ) + { + (*in_b_cb)( (void*)b, userData ); + b += elementSize; + } + } + } +} + +/*** +**** +***/ + #if 0 void* @@ -237,6 +296,46 @@ void * tr_memmem ( const void *vbig, size_t big_len, } #endif +/** +*** +**/ + +int +tr_compareUint8 ( uint8_t a, uint8_t b ) +{ + if( a < b ) return -1; + if( a > b ) return 1; + return 0; +} + +int +tr_compareUint16( uint16_t a, uint16_t b ) +{ + if( a < b ) return -1; + if( a > b ) return 1; + return 0; +} + +int +tr_compareUint32( uint32_t a, uint32_t b ) +{ + if( a < b ) return -1; + if( a > b ) return 1; + return 0; +} + +int +tr_compareUint64( uint64_t a, uint64_t b ) +{ + if( a < b ) return -1; + if( a > b ) return 1; + return 0; +} + +/** +*** +**/ + struct timeval timevalSec ( int seconds ) { @@ -566,14 +665,14 @@ void tr_free( void * p ) ****/ /* note that the argument is how many bits are needed, not bytes */ -tr_bitfield_t* +tr_bitfield* tr_bitfieldNew( size_t bitcount ) { - tr_bitfield_t * ret = calloc( 1, sizeof(tr_bitfield_t) ); + tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) ); if( NULL == ret ) return NULL; - ret->len = ( bitcount + 7u ) / 8u; + ret->len = (bitcount+7u) / 8u; ret->bits = calloc( ret->len, 1 ); if( NULL == ret->bits ) { free( ret ); @@ -583,17 +682,17 @@ tr_bitfieldNew( size_t bitcount ) return ret; } -tr_bitfield_t* -tr_bitfieldDup( const tr_bitfield_t * in ) +tr_bitfield* +tr_bitfieldDup( const tr_bitfield * in ) { - tr_bitfield_t * ret = calloc( 1, sizeof(tr_bitfield_t) ); + tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) ); ret->len = in->len; ret->bits = malloc( ret->len ); memcpy( ret->bits, in->bits, ret->len ); return ret; } -void tr_bitfieldFree( tr_bitfield_t * bitfield ) +void tr_bitfieldFree( tr_bitfield * bitfield ) { if( bitfield ) { @@ -603,13 +702,13 @@ void tr_bitfieldFree( tr_bitfield_t * bitfield ) } void -tr_bitfieldClear( tr_bitfield_t * bitfield ) +tr_bitfieldClear( tr_bitfield * bitfield ) { memset( bitfield->bits, 0, bitfield->len ); } int -tr_bitfieldIsEmpty( const tr_bitfield_t * bitfield ) +tr_bitfieldIsEmpty( const tr_bitfield * bitfield ) { unsigned int i; @@ -624,7 +723,7 @@ tr_bitfieldIsEmpty( const tr_bitfield_t * bitfield ) #define BIT(nth) (1<<(7-(nth%8))) void -tr_bitfieldAdd( tr_bitfield_t * bitfield, size_t nth ) +tr_bitfieldAdd( tr_bitfield * bitfield, size_t nth ) { assert( bitfield != NULL ); assert( BIN(nth) < bitfield->len ); @@ -632,9 +731,9 @@ tr_bitfieldAdd( tr_bitfield_t * bitfield, size_t nth ) } void -tr_bitfieldAddRange( tr_bitfield_t * bitfield, - size_t begin, - size_t end ) +tr_bitfieldAddRange( tr_bitfield * bitfield, + size_t begin, + size_t end ) { /* TODO: there are faster ways to do this */ unsigned int i; @@ -643,8 +742,8 @@ tr_bitfieldAddRange( tr_bitfield_t * bitfield, } void -tr_bitfieldRem( tr_bitfield_t * bitfield, - size_t nth ) +tr_bitfieldRem( tr_bitfield * bitfield, + size_t nth ) { if( bitfield != NULL ) { @@ -655,9 +754,9 @@ tr_bitfieldRem( tr_bitfield_t * bitfield, } void -tr_bitfieldRemRange ( tr_bitfield_t * b, - size_t begin, - size_t end ) +tr_bitfieldRemRange ( tr_bitfield * b, + size_t begin, + size_t end ) { /* TODO: there are faster ways to do this */ unsigned int i; @@ -665,8 +764,8 @@ tr_bitfieldRemRange ( tr_bitfield_t * b, tr_bitfieldRem( b, i ); } -tr_bitfield_t* -tr_bitfieldNegate( tr_bitfield_t * b ) +tr_bitfield* +tr_bitfieldNegate( tr_bitfield * b ) { uint8_t *it; const uint8_t *end; @@ -677,8 +776,8 @@ tr_bitfieldNegate( tr_bitfield_t * b ) return b; } -tr_bitfield_t* -tr_bitfieldAnd( tr_bitfield_t * a, const tr_bitfield_t * b ) +tr_bitfield* +tr_bitfieldAnd( tr_bitfield * a, const tr_bitfield * b ) { uint8_t *ait; const uint8_t *aend, *bit; @@ -692,7 +791,7 @@ tr_bitfieldAnd( tr_bitfield_t * a, const tr_bitfield_t * b ) } size_t -tr_bitfieldCountTrueBits( const tr_bitfield_t* b ) +tr_bitfieldCountTrueBits( const tr_bitfield* b ) { size_t ret = 0; const uint8_t *it, *end; diff --git a/libtransmission/utils.h b/libtransmission/utils.h index b6c7ebc97..aef0d75af 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -138,28 +138,50 @@ char* tr_strndup( const char * str, int len ); **** ***/ -typedef struct tr_bitfield_s +typedef void (tr_set_func)(void * element, void * userData ); + +void tr_set_compare( const void * a, size_t aCount, + const void * b, size_t bCount, + int compare( const void * a, const void * b ), + size_t elementSize, + tr_set_func in_a_cb, + tr_set_func in_b_cb, + tr_set_func in_both_cb, + void * userData ); + +int tr_compareUint8 ( uint8_t a, uint8_t b ); +int tr_compareUint16( uint16_t a, uint16_t b ); +int tr_compareUint32( uint32_t a, uint32_t b ); +int tr_compareUint64( uint64_t a, uint64_t b ); + +/*** +**** +***/ + +struct tr_bitfield { uint8_t * bits; size_t len; -} -tr_bitfield_t; +}; -tr_bitfield_t* tr_bitfieldNew( size_t bitcount ); -tr_bitfield_t* tr_bitfieldDup( const tr_bitfield_t* ); -void tr_bitfieldFree( tr_bitfield_t*); +typedef struct tr_bitfield tr_bitfield; +typedef struct tr_bitfield tr_bitfield_t; -void tr_bitfieldClear( tr_bitfield_t* ); -void tr_bitfieldAdd( tr_bitfield_t*, size_t bit ); -void tr_bitfieldRem( tr_bitfield_t*, size_t bit ); -void tr_bitfieldAddRange( tr_bitfield_t *, size_t begin, size_t end ); -void tr_bitfieldRemRange ( tr_bitfield_t*, size_t begin, size_t end ); +tr_bitfield* tr_bitfieldNew( size_t bitcount ); +tr_bitfield* tr_bitfieldDup( const tr_bitfield* ); +void tr_bitfieldFree( tr_bitfield*); -int tr_bitfieldIsEmpty( const tr_bitfield_t* ); -size_t tr_bitfieldCountTrueBits( const tr_bitfield_t* ); +void tr_bitfieldClear( tr_bitfield* ); +void tr_bitfieldAdd( tr_bitfield*, size_t bit ); +void tr_bitfieldRem( tr_bitfield*, size_t bit ); +void tr_bitfieldAddRange( tr_bitfield *, size_t begin, size_t end ); +void tr_bitfieldRemRange ( tr_bitfield*, size_t begin, size_t end ); -tr_bitfield_t* tr_bitfieldNegate( tr_bitfield_t* ); -tr_bitfield_t* tr_bitfieldAnd( tr_bitfield_t*, const tr_bitfield_t* ); +int tr_bitfieldIsEmpty( const tr_bitfield* ); +size_t tr_bitfieldCountTrueBits( const tr_bitfield* ); + +tr_bitfield* tr_bitfieldNegate( tr_bitfield* ); +tr_bitfield* tr_bitfieldAnd( tr_bitfield*, const tr_bitfield* ); #define tr_bitfieldHas(bitfield,nth) \ ( ( bitfield ) && ( (bitfield)->bits[(nth)>>3] & 128 >>( (nth) & 7 ) ) ) diff --git a/mk/version.sh b/mk/version.sh index d5ec9a719..f8d45ce3e 100755 --- a/mk/version.sh +++ b/mk/version.sh @@ -2,11 +2,9 @@ # # $Id$ -# convention: -TR MAJOR MINOR MAINT BETA - (each a single char) -# BETA: "Z" for beta, "0" for stable # these should be the only two lines you need to change -PEERID_PREFIX="-TR082Z-" -USERAGENT_PREFIX="0.82+" +PEERID_PREFIX="-TR080Z-" +USERAGENT_PREFIX="0.80+" SVN_REVISION=`find ./ -name "*\.[ch]" -o -name "*\.cpp" -o -name "*\.po" | \