source: trunk/libtransmission/shared.c @ 2177

Last change on this file since 2177 was 2177, checked in by charles, 15 years ago

adding experimental implementation of Tamilmani's `Swift' tit-for-tat algorithm for testing. To tweak or disable, change the values around line 50 of libtransmission/peer.c

  • Property svn:keywords set to Date Rev Author Id
File size: 12.1 KB
Line 
1/******************************************************************************
2 * $Id: shared.c 2177 2007-06-21 14:47:26Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "shared.h"
26#include "peer.h"
27
28/* Maximum number of peers that we keep in our local list */
29#define MAX_PEER_COUNT 42
30
31struct tr_shared_s
32{
33    tr_handle_t  * h;
34    volatile int die;
35    tr_thread_t  thread;
36    tr_lock_t    lock;
37
38    /* Incoming connections */
39    int          publicPort;
40    int          bindPort;
41    int          bindSocket;
42    int          peerCount;
43    tr_peer_t    * peers[MAX_PEER_COUNT];
44
45    /* NAT-PMP/UPnP */
46    tr_natpmp_t  * natpmp;
47    tr_upnp_t    * upnp;
48
49    /* Choking */
50    tr_choking_t * choking;
51};
52
53/***********************************************************************
54 * Local prototypes
55 **********************************************************************/
56static void SharedLoop( void * );
57static void SetPublicPort( tr_shared_t *, int );
58static void AcceptPeers( tr_shared_t * );
59static void ReadPeers( tr_shared_t * );
60static void DispatchPeers( tr_shared_t * );
61
62
63/***********************************************************************
64 * tr_sharedInit
65 ***********************************************************************
66 *
67 **********************************************************************/
68tr_shared_t * tr_sharedInit( tr_handle_t * h )
69{
70    tr_shared_t * s = calloc( 1, sizeof( tr_shared_t ) );
71
72    s->h = h;
73    tr_lockInit( &s->lock );
74
75    s->publicPort = -1;
76    s->bindPort   = -1;
77    s->bindSocket = -1;
78    s->natpmp     = tr_natpmpInit();
79    s->upnp       = tr_upnpInit();
80    s->choking    = tr_chokingInit( h );
81
82    /* Launch the thread */
83    s->die = 0;
84    tr_threadCreate( &s->thread, SharedLoop, s, "shared" );
85
86    return s;
87}
88
89/***********************************************************************
90 * tr_sharedClose
91 ***********************************************************************
92 *
93 **********************************************************************/
94void tr_sharedClose( tr_shared_t * s )
95{
96    int ii;
97
98    /* Stop the thread */
99    s->die = 1;
100    tr_threadJoin( &s->thread );
101
102    /* Clean up */
103    for( ii = 0; ii < s->peerCount; ii++ )
104    {
105        tr_peerDestroy( s->peers[ii] );
106    }
107    if( s->bindSocket > -1 )
108    {
109        tr_netClose( s->bindSocket );
110    }
111    tr_lockClose( &s->lock );
112    tr_natpmpClose( s->natpmp );
113    tr_upnpClose( s->upnp );
114    tr_chokingClose( s->choking );
115    free( s );
116}
117
118/***********************************************************************
119 * tr_sharedLock, tr_sharedUnlock
120 ***********************************************************************
121 *
122 **********************************************************************/
123void tr_sharedLock( tr_shared_t * s )
124{
125    tr_lockLock( &s->lock );
126}
127void tr_sharedUnlock( tr_shared_t * s )
128{
129    tr_lockUnlock( &s->lock );
130}
131
132/***********************************************************************
133 * tr_sharedSetPort
134 ***********************************************************************
135 *
136 **********************************************************************/
137void tr_sharedSetPort( tr_shared_t * s, int port )
138{
139#ifdef BEOS_NETSERVER
140    /* BeOS net_server seems to be unable to set incoming connections
141     * to non-blocking. Too bad. */
142    return;
143#endif
144
145    tr_sharedLock( s );
146
147    if( port == s->bindPort )
148    {
149        tr_sharedUnlock( s );
150        return;
151    }
152    s->bindPort = port;
153
154    /* Close the previous accept socket, if any */
155    if( s->bindSocket > -1 )
156    {
157        tr_netClose( s->bindSocket );
158    }
159
160    /* Create the new one */
161    /* XXX should handle failure here in a better way */
162    s->bindSocket = tr_netBindTCP( port );
163    if( 0 > s->bindSocket )
164    {
165        /* Notify the trackers */
166        SetPublicPort( s, 0 );
167        /* Remove the forwarding for the old port */
168        tr_natpmpRemoveForwarding( s->natpmp );
169        tr_upnpRemoveForwarding( s->upnp );
170    }
171    else
172    {
173        tr_inf( "Bound listening port %d", port );
174        listen( s->bindSocket, 5 );
175        if( port != s->publicPort )
176        {
177            /* Notify the trackers */
178            SetPublicPort( s, port );
179        }
180        /* Forward the new port */
181        tr_natpmpForwardPort( s->natpmp, port );
182        tr_upnpForwardPort( s->upnp, port );
183    }
184
185    tr_sharedUnlock( s );
186}
187
188/***********************************************************************
189 * tr_sharedGetPublicPort
190 ***********************************************************************
191 *
192 **********************************************************************/
193int tr_sharedGetPublicPort( tr_shared_t * s )
194{
195    return s->publicPort;
196}
197
198/***********************************************************************
199 * tr_sharedTraversalEnable, tr_sharedTraversalStatus
200 ***********************************************************************
201 *
202 **********************************************************************/
203void tr_sharedTraversalEnable( tr_shared_t * s, int enable )
204{
205    if( enable )
206    {
207        tr_natpmpStart( s->natpmp );
208        tr_upnpStart( s->upnp );
209    }
210    else
211    {
212        tr_natpmpStop( s->natpmp );
213        tr_upnpStop( s->upnp );
214    }
215}
216
217int tr_sharedTraversalStatus( tr_shared_t * s )
218{
219    int statuses[] = {
220        TR_NAT_TRAVERSAL_MAPPED,
221        TR_NAT_TRAVERSAL_MAPPING,
222        TR_NAT_TRAVERSAL_UNMAPPING,
223        TR_NAT_TRAVERSAL_ERROR,
224        TR_NAT_TRAVERSAL_NOTFOUND,
225        TR_NAT_TRAVERSAL_DISABLED,
226        -1,
227    };
228    int natpmp, upnp, ii;
229
230    natpmp = tr_natpmpStatus( s->natpmp );
231    upnp = tr_upnpStatus( s->upnp );
232
233    for( ii = 0; 0 <= statuses[ii]; ii++ )
234    {
235        if( statuses[ii] == natpmp || statuses[ii] == upnp )
236        {
237            return statuses[ii];
238        }
239    }
240
241    assert( 0 );
242
243    return TR_NAT_TRAVERSAL_ERROR;
244
245}
246
247/***********************************************************************
248 * tr_sharedSetLimit
249 **********************************************************************/
250void tr_sharedSetLimit( tr_shared_t * s, int limit )
251{
252    tr_chokingSetLimit( s->choking, limit );
253}
254
255
256/***********************************************************************
257 * Local functions
258 **********************************************************************/
259
260/***********************************************************************
261 * SharedLoop
262 **********************************************************************/
263static void SharedLoop( void * _s )
264{
265    tr_shared_t * s = _s;
266    uint64_t      date1, date2, lastchoke = 0;
267    int           newPort;
268
269    tr_sharedLock( s );
270
271    while( !s->die )
272    {
273        date1 = tr_date();
274
275        /* NAT-PMP and UPnP pulses */
276        newPort = -1;
277        tr_natpmpPulse( s->natpmp, &newPort );
278        if( 0 < newPort && newPort != s->publicPort )
279        {
280            SetPublicPort( s, newPort );
281        }
282        tr_upnpPulse( s->upnp );
283
284        /* Handle incoming connections */
285        AcceptPeers( s );
286        ReadPeers( s );
287        DispatchPeers( s );
288
289        /* Update choking every second */
290        if( date1 > lastchoke + 1000 )
291        {
292            tr_chokingPulse( s->choking );
293            lastchoke = date1;
294        }
295
296        tr_swiftPulse ( s->h );
297
298        /* Wait up to 20 ms */
299        date2 = tr_date();
300        if( date2 < date1 + 20 )
301        {
302            tr_sharedUnlock( s );
303            tr_wait( date1 + 20 - date2 );
304            tr_sharedLock( s );
305        }
306    }
307
308    tr_sharedUnlock( s );
309}
310
311/***********************************************************************
312 * SetPublicPort
313 **********************************************************************/
314static void SetPublicPort( tr_shared_t * s, int port )
315{
316    tr_handle_t * h = s->h;
317    tr_torrent_t * tor;
318
319    s->publicPort = port;
320
321    for( tor = h->torrentList; tor; tor = tor->next )
322    {
323        tr_lockLock( &tor->lock );
324        tor->publicPort = port;
325        tr_lockUnlock( &tor->lock );
326    }
327}
328
329/***********************************************************************
330 * AcceptPeers
331 ***********************************************************************
332 * Check incoming connections and add the peers to our local list
333 **********************************************************************/
334static void AcceptPeers( tr_shared_t * s )
335{
336    int socket;
337    struct in_addr addr;
338
339    for( ;; )
340    {
341        if( s->bindSocket < 0 || s->peerCount >= MAX_PEER_COUNT )
342        {
343            break;
344        }
345
346        socket = tr_netAccept( s->bindSocket, &addr, NULL );
347        if( socket < 0 )
348        {
349            break;
350        }
351        s->peers[s->peerCount++] = tr_peerInit( addr, 0, socket,
352                                                TR_PEER_FROM_INCOMING );
353    }
354}
355
356/***********************************************************************
357 * ReadPeers
358 ***********************************************************************
359 * Try to read handshakes
360 **********************************************************************/
361static void ReadPeers( tr_shared_t * s )
362{
363    int ii;
364
365    for( ii = 0; ii < s->peerCount; )
366    {
367        if( tr_peerRead( s->peers[ii] ) )
368        {
369            tr_peerDestroy( s->peers[ii] );
370            s->peerCount--;
371            memmove( &s->peers[ii], &s->peers[ii+1],
372                     ( s->peerCount - ii ) * sizeof( tr_peer_t * ) );
373            continue;
374        }
375        ii++;
376    }
377}
378
379/***********************************************************************
380 * DispatchPeers
381 ***********************************************************************
382 * If we got a handshake, try to find the torrent for this peer
383 **********************************************************************/
384static void DispatchPeers( tr_shared_t * s )
385{
386    tr_handle_t * h = s->h;
387    int ii;
388    const uint64_t now = tr_date();
389
390    for( ii = 0; ii < s->peerCount; )
391    {
392        const uint8_t * hash = tr_peerHash( s->peers[ii] );
393
394        if( !hash && now > tr_peerDate( s->peers[ii] ) + 10000 )
395        {
396            /* 10 seconds and no handshake, drop it */
397            tr_peerDestroy( s->peers[ii] );
398            goto removePeer;
399        }
400        if( hash )
401        {
402            tr_torrent_t * tor;
403
404            for( tor = h->torrentList; tor; tor = tor->next )
405            {
406                tr_lockLock( &tor->lock );
407                if( tor->status & TR_STATUS_INACTIVE )
408                {
409                    tr_lockUnlock( &tor->lock );
410                    continue;
411                }
412
413                if( 0 == memcmp( tor->info.hash, hash,
414                            SHA_DIGEST_LENGTH ) )
415                {
416                    /* Found it! */
417                    tr_torrentAttachPeer( tor, s->peers[ii] );
418                    tr_lockUnlock( &tor->lock );
419                    goto removePeer;
420                }
421                tr_lockUnlock( &tor->lock );
422            }
423
424            /* Couldn't find a torrent, we probably removed it */
425            tr_peerDestroy( s->peers[ii] );
426            goto removePeer;
427        }
428        ii++;
429        continue;
430
431removePeer:
432        s->peerCount--;
433        memmove( &s->peers[ii], &s->peers[ii+1],
434                ( s->peerCount - ii ) * sizeof( tr_peer_t * ) );
435    }
436}
437
Note: See TracBrowser for help on using the repository browser.