source: trunk/libtransmission/shared.c @ 2343

Last change on this file since 2343 was 2343, checked in by joshe, 15 years ago

Change a couple functions to take an in_addr pointer instead of an in_addr.
Forward declare struct in_addr and include the relevant headers in the .c files where it's used.

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