source: trunk/libtransmission/shared.c @ 1425

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

More simplifications

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