source: trunk/libtransmission/tr-lpd.c @ 10714

Last change on this file since 10714 was 10714, checked in by charles, 12 years ago

(trunk libT) #3249 "C99-ism in libtransmission/tr-lpd.c" -- fixed in trunk for 2.00

File size: 19.6 KB
Line 
1/*
2Copyright (c) 2010 by Johannes Lieder
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23/* ansi */
24#include <errno.h>
25#include <stdio.h>
26
27/* posix */
28#include <netinet/in.h> /* sockaddr_in */
29#include <signal.h> /* sig_atomic_t */
30#include <sys/time.h>
31#include <sys/types.h>
32#include <sys/socket.h> /* socket(), bind() */
33#include <unistd.h> /* close() */
34#include <fcntl.h> /* fcntl(), O_NONBLOCK */
35#include <ctype.h> /* toupper() */
36
37/* third party */
38#include <event.h>
39
40/* libT */
41#include "transmission.h"
42#include "crypto.h"
43#include "net.h"
44#include "peer-mgr.h" /* tr_peerMgrAddPex() */
45#include "session.h"
46#include "torrent.h" /* tr_torrentFindFromHash() */
47#include "tr-lpd.h"
48#include "utils.h"
49#include "version.h"
50
51/**
52* @brief Local Peer Discovery
53* @file tr-lpd.c
54*
55* This module implements the Local Peer Discovery (LPD) protocol as supported by the
56* uTorrent client application.  A typical LPD datagram is 119 bytes long.
57*
58* $Id$
59*/
60
61static void event_callback( int, short, void* );
62
63static int lpd_socket; /**<separate multicast receive socket */
64static int lpd_socket2; /**<and multicast send socket */
65static struct event lpd_event;
66static tr_port lpd_port;
67
68static tr_torrent* lpd_torStaticType UNUSED; /* just a helper for static type analysis */
69static tr_session* session;
70
71enum { lpd_maxDatagramLength = 200 }; /**<the size an LPD datagram must not exceed */
72const char lpd_mcastGroup[] = "239.192.152.143"; /**<LPD multicast group */
73const int lpd_mcastPort = 6771; /**<LPD source and destination UPD port */
74static struct sockaddr_in lpd_mcastAddr; /**<initialized from the above constants in tr_lpdInit */
75
76/**
77* @brief Protocol-related information carried by a Local Peer Discovery packet */
78struct lpd_protocolVersion
79{
80    int major, minor;
81};
82
83enum lpd_enumTimeToLive {
84    lpd_ttlSameSubnet = 1,
85    lpd_ttlSameSite = 32,
86    lpd_ttlSameRegion = 64,
87    lpd_ttlSameContinent = 128,
88    lpd_ttlUnrestricted = 255
89};
90
91enum {
92    lpd_announceInterval = 4 * 60, /**<4 min announce interval per torrent */
93    lpd_announceScope = lpd_ttlSameSubnet /**<the maximum scope for LPD datagrams */
94};
95
96
97/**
98* @defgroup DoS Message Flood Protection
99* @{
100* We want to have a means to protect the libtransmission backend against message
101* flooding: the strategy is to cap event processing once more than ten messages
102* per second (that is, taking the average over one of our housekeeping intervals)
103* got into our processing handler.
104* If we'd really hit the limit and start discarding events, we either joined an
105* extremely crowded multicast group or a malevolent host is sending bogus data to
106* our socket. In this situation, we rather miss some announcements than blocking
107* the actual task.
108* @}
109*/
110
111/**
112* @ingroup DoS
113* @brief allow at most ten messages per second (interval average)
114* @note this constraint is only enforced once per housekeeping interval */
115enum { lpd_announceCapFactor = 10 };
116
117/**
118* @ingroup DoS
119* @brief number of unsolicited messages during the last HK interval
120* @remark counts downwards */
121static int lpd_unsolicitedMsgCounter;
122
123/**
124* @def CRLF
125* @brief a line-feed, as understood by the LPD protocol */
126#define CRLF "\r\n"
127
128
129/**
130* @defgroup HttpReqProc HTTP-style request handling
131* @{
132*/
133
134/**
135* @brief Checks for BT-SEARCH method and separates the parameter section
136* @param[in] s The request string
137* @param[out] ver If non-NULL, gets filled with protocol info from the request
138* @return Returns a relative pointer to the beginning of the parameter section;
139*         if result is NULL, s was invalid and no information will be returned
140* @remark Note that the returned pointer is only usable as long as the given
141*         pointer s is valid; that is, return storage is temporary.
142*
143* Determines whether the given string checks out to be a valid BT-SEARCH message.
144* If so, the return value points to the beginning of the parameter section (note:
145* in this case the function returns a character sequence beginning with CRLF).
146* If parameter is not NULL, the declared protocol version is returned as part of
147* the lpd_protocolVersion structure.
148*/
149static const char* lpd_extractHeader( const char* s, struct lpd_protocolVersion* const ver )
150{
151    int major = -1, minor = -1;
152    size_t len;
153
154    assert( s != NULL );
155    len = strlen( s );
156
157    /* something might be rotten with this chunk of data */
158    if( len == 0 || len > lpd_maxDatagramLength )
159        return NULL;
160
161    /* now we can attempt to look up the BT-SEARCH header */
162    if( sscanf( s, "BT-SEARCH * HTTP/%d.%d" CRLF, &major, &minor ) != 2 )
163        return NULL;
164
165    if( major < 0 || minor < 0 )
166        return NULL;
167
168    {
169        /* a pair of blank lines at the end of the string, no place else */
170        const char* const two_blank = CRLF CRLF CRLF;
171        const char* const end = strstr( s, two_blank );
172
173        if( end == NULL || strlen( end ) > strlen( two_blank ) )
174            return NULL;
175    }
176
177    if( ver != NULL )
178    {
179        ver->major = major;
180        ver->minor = minor;
181    }
182
183    /* separate the header, begins with CRLF */
184    return strstr( s, CRLF );
185}
186
187/**
188* @brief Return the value of a named parameter
189*
190* @param[in] str Input string of "\r\nName: Value" pairs without HTTP-style method part
191* @param[in] name Name of parameter to extract
192* @param[in] n Maximum available storage for value to return
193* @param[out] val Output parameter for the actual value
194* @return Returns 1 if value could be copied successfully
195*
196* Extracts the associated value of a named parameter from a HTTP-style header by
197* performing the following steps:
198*   - assemble search string "\r\nName: " and locate position
199*   - copy back value from end to next "\r\n"
200*/
201static int lpd_extractParam( const char* const str, const char* const name, int n, char* const val )
202{
203    /* configure maximum length of search string here */
204    enum { maxLength = 30 };
205    char sstr[maxLength] = { };
206    const char* pos;
207
208    assert( str != NULL && name != NULL );
209    assert( val != NULL );
210
211    if( strlen( name ) > maxLength - strlen( CRLF ": " ) )
212        return 0;
213
214    /* compose the string token to search for */
215    snprintf( sstr, maxLength, CRLF "%s: ", name );
216
217    pos = strstr( str, sstr );
218    if( pos == NULL )
219        return 0; /* search was not successful */
220
221    {
222        const char* const beg = pos + strlen( sstr );
223        const char* const new_line = strstr( beg, CRLF );
224
225        /* the value is delimited by the next CRLF */
226        int len = new_line - beg;
227
228        /* if value string hits the length limit n,
229         * leave space for a trailing '\0' character */
230        if( len < n-- )
231            n = len;
232
233        strncpy( val, beg, n );
234        val[n] = 0;
235    }
236
237    /* we successfully returned the value string */
238    return 1;
239}
240
241/**
242* @} */
243
244
245/**
246* @brief Configures additional capabilities for a socket */
247static inline int lpd_configureSocket( int sock, int add )
248{
249    /* read-modify-write socket flags */
250    int flags = fcntl( sock, F_GETFL );
251
252    if( flags < 0 )
253        return -1;
254
255    if( fcntl( sock, F_SETFL, add | flags ) == -1 )
256        return -1;
257
258    return add;
259}
260
261/**
262* @brief Initializes Local Peer Discovery for this node
263*
264* For the most part, this means setting up an appropriately configured multicast socket
265* and event-based message handling.
266*
267* @remark Since the LPD service does not use another protocol family yet, this code is
268* IPv4 only for the time being.
269*/
270int tr_lpdInit( tr_session* ss, tr_address* tr_addr UNUSED )
271{
272    struct ip_mreq mcastReq;
273    const int opt_on = 1, opt_off = 0;
274
275    if( session ) /* already initialized */
276        return -1;
277
278    assert( lpd_announceInterval > 0 );
279    assert( lpd_announceScope > 0 );
280
281    lpd_port = tr_sessionGetPeerPort( ss );
282    if( lpd_port <= 0 )
283        return -1;
284
285    tr_ndbg( "LPD", "Initialising Local Peer Discovery" );
286
287    /* setup datagram socket (receive) */
288    {
289        lpd_socket = socket( PF_INET, SOCK_DGRAM, 0 );
290        if( lpd_socket < 0 )
291            goto fail;
292
293        /* enable non-blocking operation */
294        if( lpd_configureSocket( lpd_socket, O_NONBLOCK ) < 0 )
295            goto fail;
296
297        if( setsockopt( lpd_socket, SOL_SOCKET, SO_REUSEADDR,
298                &opt_on, sizeof opt_on ) < 0 )
299            goto fail;
300
301        memset( &lpd_mcastAddr, 0, sizeof lpd_mcastAddr );
302        lpd_mcastAddr.sin_family = AF_INET;
303        lpd_mcastAddr.sin_port = htons( lpd_mcastPort );
304        if( inet_pton( lpd_mcastAddr.sin_family, lpd_mcastGroup,
305                &lpd_mcastAddr.sin_addr ) < 0 )
306            goto fail;
307
308        if( bind( lpd_socket, (struct sockaddr*) &lpd_mcastAddr,
309                sizeof lpd_mcastAddr ) < 0 )
310            goto fail;
311
312        /* we want to join that LPD multicast group */
313        memset( &mcastReq, 0, sizeof mcastReq );
314        mcastReq.imr_multiaddr = lpd_mcastAddr.sin_addr;
315        mcastReq.imr_interface.s_addr = htonl( INADDR_ANY );
316        if( setsockopt( lpd_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
317                &mcastReq, sizeof mcastReq ) < 0 )
318            goto fail;
319
320        if( setsockopt( lpd_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
321                &opt_off, sizeof opt_off ) < 0 )
322            goto fail;
323    }
324
325    /* setup datagram socket (send) */
326    {
327        const unsigned char scope = lpd_announceScope;
328
329        lpd_socket2 = socket( PF_INET, SOCK_DGRAM, 0 );
330        if( lpd_socket2 < 0 )
331            goto fail;
332
333        /* enable non-blocking operation */
334        if( lpd_configureSocket( lpd_socket2, O_NONBLOCK ) < 0 )
335            goto fail;
336
337        /* configure outbound multicast TTL */
338        if( setsockopt( lpd_socket2, IPPROTO_IP, IP_MULTICAST_TTL,
339                &scope, sizeof scope ) < 0 )
340            goto fail;
341
342        if( setsockopt( lpd_socket2, IPPROTO_IP, IP_MULTICAST_LOOP,
343                &opt_off, sizeof opt_off ) < 0 )
344            goto fail;
345    }
346
347    session = ss;
348
349    /* Note: lpd_unsolicitedMsgCounter remains 0 until the first timeout event, thus
350     * any announcement received during the initial interval will be discarded. */
351
352    event_set( &lpd_event, lpd_socket, EV_READ | EV_PERSIST, event_callback, NULL );
353    event_add( &lpd_event, NULL );
354
355    tr_ndbg( "LPD", "Local Peer Discovery initialised" );
356
357    return 1;
358
359    fail:
360    {
361        const int save = errno;
362        close( lpd_socket );
363        close( lpd_socket2 );
364        lpd_socket = lpd_socket2 = -1;
365        session = NULL;
366        tr_ndbg( "LPD", "LPD initialisation failed (errno = %d)", save );
367        errno = save;
368    }
369
370
371    return -1;
372}
373
374void tr_lpdUninit( tr_session* ss )
375{
376    if( session != ss )
377        return;
378
379    tr_ndbg( "LPD", "Uninitialising Local Peer Discovery" );
380
381    event_del( &lpd_event );
382
383    /* just shut down, we won't remember any former nodes */
384    EVUTIL_CLOSESOCKET( lpd_socket );
385    EVUTIL_CLOSESOCKET( lpd_socket2 );
386    tr_ndbg( "LPD", "Done uninitialising Local Peer Discovery" );
387
388    session = NULL;
389}
390
391tr_bool tr_lpdEnabled( const tr_session* ss )
392{
393    return ss && ( ss == session );
394}
395
396
397/**
398* @cond
399* @brief Performs some (internal) software consistency checks at compile time.
400* @remark Declared inline for the compiler not to allege us of feeding unused
401* functions. In any other respect, lpd_consistencyCheck is an orphaned function.
402*/
403static inline void lpd_consistencyCheck( void )
404{
405    /* if the following check fails, the definition of a hash string has changed
406     * without our knowledge; revise string handling in functions tr_lpdSendAnnounce
407     * and tr_lpdConsiderAnnounce. However, the code is designed to function as long
408     * as interfaces to the rest of the lib remain compatible with char* strings. */
409    STATIC_ASSERT( sizeof(lpd_torStaticType->info.hashString[0]) == sizeof(char) );
410}
411/**
412* @endcond */
413
414
415/**
416* @defgroup LdsProto LPD announcement processing
417* @{
418*/
419
420/**
421* @brief Announce the given torrent on the local network
422*
423* @param[in] t Torrent to announce
424* @return Returns TRUE on success
425*
426* Send a query for torrent t out to the LPD multicast group (or the LAN, for that
427* matter).  A listening client on the same network might react by adding us to his
428* peer pool for torrent t.
429*/
430tr_bool tr_lpdSendAnnounce( const tr_torrent* t )
431{
432    size_t i;
433    const char fmt[] =
434        "BT-SEARCH * HTTP/%u.%u" CRLF
435        "Host: %s:%u" CRLF
436        "Port: %u" CRLF
437        "Infohash: %s" CRLF
438        CRLF
439        CRLF;
440
441    char hashString[lengthof( t->info.hashString )];
442    char query[lpd_maxDatagramLength + 1] = { };
443
444    if( t == NULL )
445        return FALSE;
446
447    /* make sure the hash string is normalized, just in case */
448    for( i = 0; i < sizeof hashString; i++ )
449        hashString[i] = toupper( t->info.hashString[i] );
450
451    /* prepare a zero-terminated announce message */
452    snprintf( query, lpd_maxDatagramLength + 1, fmt, 1, 1,
453        lpd_mcastGroup, lpd_mcastPort, lpd_port, hashString );
454
455    /* actually send the query out using [lpd_socket2] */
456    {
457        const int len = strlen( query );
458
459        /* destination address info has already been set up in tr_lpdInit(),
460         * so we refrain from preparing another sockaddr_in here */
461        int res = sendto( lpd_socket2, query, len, 0,
462            (const struct sockaddr*) &lpd_mcastAddr, sizeof lpd_mcastAddr );
463
464        if( res != len )
465            return FALSE;
466    }
467
468    tr_tordbg( t, "LPD announce message away" );
469
470    return TRUE;
471}
472
473/**
474* @brief Process incoming unsolicited messages and add the peer to the announced
475* torrent if all checks are passed.
476*
477* @param[in,out] peer Adress information of the peer to add
478* @param[in] msg The announcement message to consider
479* @return Returns 0 if any input parameter or the announce was invalid, 1 if the peer
480* was successfully added, -1 if not; a non-null return value indicates a side-effect to
481* the peer in/out parameter.
482*
483* @note The port information gets added to the peer structure if tr_lpdConsiderAnnounce
484* is able to extract the necessary information from the announce message.  That is, if
485* return != 0, the caller may retrieve the value from the passed structure.
486*/
487static int tr_lpdConsiderAnnounce( tr_pex* peer, const char* const msg )
488{
489    enum
490    {
491        maxValueLen = 25,
492        maxHashLen = lengthof(lpd_torStaticType->info.hashString)
493    };
494
495    struct lpd_protocolVersion ver = { -1, -1 };
496    char value[maxValueLen] = { };
497    char hashString[maxHashLen] = { };
498    int res = 0, peerPort = 0;
499
500    if( peer != NULL && msg != NULL )
501    {
502        tr_torrent* tor = NULL;
503
504        const char* params = lpd_extractHeader( msg, &ver );
505        if( params == NULL || ver.major != 1 ) /* allow messages of protocol v1 */
506            return 0;
507
508        /* save the effort to check Host, which seems to be optional anyway */
509
510        if( lpd_extractParam( params, "Port", maxValueLen, value ) == 0 )
511            return 0;
512
513        /* determine announced peer port, refuse if value too large */
514        if( sscanf( value, "%u", &peerPort ) != 1 || peerPort > (in_port_t)-1 )
515            return 0;
516
517        peer->port = htons( peerPort );
518        res = -1; /* signal caller side-effect to peer->port via return != 0 */
519
520        if( lpd_extractParam( params, "Infohash", maxHashLen, hashString ) == 0 )
521            return res;
522
523        tor = tr_torrentFindFromHashString( session, hashString );
524
525        if( tr_isTorrent( tor ) && tr_torrentAllowsLPD( tor ) )
526        {
527            /* we found a suitable peer, add it to the torrent */
528            tr_peerMgrAddPex( tor, TR_PEER_FROM_LPD, peer, -1 );
529            tr_tordbg( tor, "Learned %d local peer from LPD (%s:%u)",
530                1, inet_ntoa( peer->addr.addr.addr4 ), peerPort );
531
532            /* periodic reconnectPulse() deals with the rest... */
533
534            return 1;
535        }
536        else
537            tr_ndbg( "LPD", "Cannot serve torrent #%s", hashString );
538    }
539
540    return res;
541}
542
543/**
544* @} */
545
546/**
547* @note Since it possible for tr_lpdAnnounceMore to get called from outside the LPD module,
548* the function needs to be informed of the externally employed housekeeping interval.
549* Further, by setting interval to zero (or negative) the caller may actually disable LPD
550* announces on a per-interval basis.
551*/
552int tr_lpdAnnounceMore( const time_t now, const int interval )
553{
554    tr_torrent* tor = NULL;
555    int announcesSent = 0;
556
557    if( !tr_isSession( session ) )
558        return -1;
559
560    while(( tor = tr_torrentNext( session, tor ) )
561          && tr_sessionAllowsLPD( session ) )
562    {
563        if( tr_isTorrent( tor ) )
564        {
565            if( !tr_torrentAllowsLPD( tor ) || (
566                    ( tr_torrentGetActivity( tor ) != TR_STATUS_DOWNLOAD ) &&
567                    ( tr_torrentGetActivity( tor ) != TR_STATUS_SEED ) ) )
568                continue;
569
570            if( tor->lpdAnnounceAt <= now )
571            {
572                if( tr_lpdSendAnnounce( tor ) )
573                    announcesSent++;
574
575                tor->lpdAnnounceAt = now + lpd_announceInterval;
576                break; /* that's enough; for this interval */
577            }
578        }
579    }
580
581    /* perform housekeeping for the flood protection mechanism */
582    {
583        const int maxAnnounceCap = interval * lpd_announceCapFactor;
584
585        if( lpd_unsolicitedMsgCounter < 0 )
586            tr_ninf( "LPD", "Dropped %d announces in the last interval (max. %d "
587                     "allowed)", -lpd_unsolicitedMsgCounter, maxAnnounceCap );
588
589        lpd_unsolicitedMsgCounter = maxAnnounceCap;
590    }
591
592    return announcesSent;
593}
594
595/**
596* @brief Processing of timeout notifications and incoming data on the socket
597* @note maximum rate of read events is limited according to @a lpd_maxAnnounceCap
598* @see DoS */
599static void event_callback( int s UNUSED, short type, void* ignore UNUSED )
600{
601    assert( tr_isSession( session ) );
602
603    /* do not allow announces to be processed if LPD is disabled */
604    if( !tr_sessionAllowsLPD( session ) )
605        return;
606
607    if( ( type & EV_READ ) != 0 )
608    {
609        struct sockaddr_in foreignAddr;
610        int addrLen = sizeof foreignAddr;
611
612        /* be paranoid enough about zero terminating the foreign string */
613        char foreignMsg[lpd_maxDatagramLength + 1] = { };
614
615        /* process local announcement from foreign peer */
616        int res = recvfrom( lpd_socket, foreignMsg, lpd_maxDatagramLength,
617            0, (struct sockaddr*) &foreignAddr, (socklen_t*) &addrLen );
618
619        /* besides, do we get flooded? then bail out! */
620        if( --lpd_unsolicitedMsgCounter < 0 )
621            return;
622
623        if( res > 0 && res <= lpd_maxDatagramLength )
624        {
625            struct tr_pex foreignPeer =
626                {
627                    .port = 0, /* the peer-to-peer port is yet unknown */
628                    .flags = 0
629                };
630
631            foreignPeer.addr.addr.addr4 = foreignAddr.sin_addr;
632            if( tr_lpdConsiderAnnounce( &foreignPeer, foreignMsg ) != 0 )
633                return; /* OK so far, no log message */
634        }
635
636        tr_ndbg( "LPD", "Discarded invalid multicast message" );
637    }
638}
639
Note: See TracBrowser for help on using the repository browser.