source: trunk/libtransmission/shared.c @ 1420

Last change on this file since 1420 was 1420, checked in by titer, 15 years ago

Officially give up on making libT reentrant, and simplify our code instead

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