source: trunk/libtransmission/shared.c @ 1419

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

Reorganizes a few things, don't accept two connections from the same IP

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