source: trunk/libtransmission/shared.c @ 2311

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

clean up #includes a bit.

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