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

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

(trunk) #3060 "Local Peer Discovery" -- in the code, rename LDS as LPD for Local Peer Discovery

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>
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    const char fmt[] =
433        "BT-SEARCH * HTTP/%u.%u" CRLF
434        "Host: %s:%u" CRLF
435        "Port: %u" CRLF
436        "Infohash: %s" CRLF
437        CRLF
438        CRLF;
439
440    char hashString[lengthof( t->info.hashString )];
441    char query[lpd_maxDatagramLength + 1] = { };
442
443    if( t == NULL )
444        return FALSE;
445
446    /* make sure the hash string is normalized, just in case */
447    for( size_t i = 0; i < sizeof hashString; i++ )
448        hashString[i] = toupper( t->info.hashString[i] );
449
450    /* prepare a zero-terminated announce message */
451    snprintf( query, lpd_maxDatagramLength + 1, fmt, 1, 1,
452        lpd_mcastGroup, lpd_mcastPort, lpd_port, hashString );
453
454    /* actually send the query out using [lpd_socket2] */
455    {
456        const int len = strlen( query );
457
458        /* destination address info has already been set up in tr_lpdInit(),
459         * so we refrain from preparing another sockaddr_in here */
460        int res = sendto( lpd_socket2, query, len, 0,
461            (const struct sockaddr*) &lpd_mcastAddr, sizeof lpd_mcastAddr );
462
463        if( res != len )
464            return FALSE;
465    }
466
467    tr_tordbg( t, "LPD announce message away" );
468
469    return TRUE;
470}
471
472/**
473* @brief Process incoming unsolicited messages and add the peer to the announced
474* torrent if all checks are passed.
475*
476* @param[in,out] peer Adress information of the peer to add
477* @param[in] msg The announcement message to consider
478* @return Returns 0 if any input parameter or the announce was invalid, 1 if the peer
479* was successfully added, -1 if not; a non-null return value indicates a side-effect to
480* the peer in/out parameter.
481*
482* @note The port information gets added to the peer structure if tr_lpdConsiderAnnounce
483* is able to extract the necessary information from the announce message.  That is, if
484* return != 0, the caller may retrieve the value from the passed structure.
485*/
486static int tr_lpdConsiderAnnounce( tr_pex* peer, const char* const msg )
487{
488    enum
489    {
490        maxValueLen = 25,
491        maxHashLen = lengthof(lpd_torStaticType->info.hashString)
492    };
493
494    struct lpd_protocolVersion ver = { -1, -1 };
495    char value[maxValueLen] = { };
496    char hashString[maxHashLen] = { };
497    int res = 0, peerPort = 0;
498
499    if( peer != NULL && msg != NULL )
500    {
501        tr_torrent* tor = NULL;
502
503        const char* params = lpd_extractHeader( msg, &ver );
504        if( params == NULL || ver.major != 1 ) /* allow messages of protocol v1 */
505            return 0;
506
507        /* save the effort to check Host, which seems to be optional anyway */
508
509        if( lpd_extractParam( params, "Port", maxValueLen, value ) == 0 )
510            return 0;
511
512        /* determine announced peer port, refuse if value too large */
513        if( sscanf( value, "%u", &peerPort ) != 1 || peerPort > (in_port_t)-1 )
514            return 0;
515
516        peer->port = htons( peerPort );
517        res = -1; /* signal caller side-effect to peer->port via return != 0 */
518
519        if( lpd_extractParam( params, "Infohash", maxHashLen, hashString ) == 0 )
520            return res;
521
522        tor = tr_torrentFindFromHashString( session, hashString );
523
524        if( tr_isTorrent( tor ) && tr_torrentAllowsLPD( tor ) )
525        {
526            /* we found a suitable peer, add it to the torrent */
527            tr_peerMgrAddPex( tor, TR_PEER_FROM_LPD, peer, -1 );
528            tr_tordbg( tor, "Learned %d local peer from LPD (%s:%u)",
529                1, inet_ntoa( peer->addr.addr.addr4 ), peerPort );
530
531            /* periodic reconnectPulse() deals with the rest... */
532
533            return 1;
534        }
535        else
536            tr_ndbg( "LPD", "Cannot serve torrent #%s", hashString );
537    }
538
539    return res;
540}
541
542/**
543* @} */
544
545/**
546* @note Since it possible for tr_lpdAnnounceMore to get called from outside the LPD module,
547* the function needs to be informed of the externally employed housekeeping interval.
548* Further, by setting interval to zero (or negative) the caller may actually disable LPD
549* announces on a per-interval basis.
550*/
551int tr_lpdAnnounceMore( const time_t now, const int interval )
552{
553    tr_torrent* tor = NULL;
554    int announcesSent = 0;
555
556    if( !tr_isSession( session ) )
557        return -1;
558
559    while(( tor = tr_torrentNext( session, tor ) )
560          && tr_sessionAllowsLPD( session ) )
561    {
562        if( tr_isTorrent( tor ) )
563        {
564            if( !tr_torrentAllowsLPD( tor ) || (
565                    ( tr_torrentGetActivity( tor ) != TR_STATUS_DOWNLOAD ) &&
566                    ( tr_torrentGetActivity( tor ) != TR_STATUS_SEED ) ) )
567                continue;
568
569            if( tor->lpdAnnounceAt <= now )
570            {
571                if( tr_lpdSendAnnounce( tor ) )
572                    announcesSent++;
573
574                tor->lpdAnnounceAt = now + lpd_announceInterval;
575                break; /* that's enough; for this interval */
576            }
577        }
578    }
579
580    /* perform housekeeping for the flood protection mechanism */
581    {
582        const int maxAnnounceCap = interval * lpd_announceCapFactor;
583
584        if( lpd_unsolicitedMsgCounter < 0 )
585            tr_ninf( "LPD", "Dropped %d announces in the last interval (max. %d "
586                     "allowed)", -lpd_unsolicitedMsgCounter, maxAnnounceCap );
587
588        lpd_unsolicitedMsgCounter = maxAnnounceCap;
589    }
590
591    return announcesSent;
592}
593
594/**
595* @brief Processing of timeout notifications and incoming data on the socket
596* @note maximum rate of read events is limited according to @a lpd_maxAnnounceCap
597* @see DoS */
598static void event_callback( int s UNUSED, short type, void* ignore UNUSED )
599{
600    assert( tr_isSession( session ) );
601
602    /* do not allow announces to be processed if LPD is disabled */
603    if( !tr_sessionAllowsLPD( session ) )
604        return;
605
606    if( ( type & EV_READ ) != 0 )
607    {
608        struct sockaddr_in foreignAddr;
609        int addrLen = sizeof foreignAddr;
610
611        /* be paranoid enough about zero terminating the foreign string */
612        char foreignMsg[lpd_maxDatagramLength + 1] = { };
613
614        /* process local announcement from foreign peer */
615        int res = recvfrom( lpd_socket, foreignMsg, lpd_maxDatagramLength,
616            0, (struct sockaddr*) &foreignAddr, (socklen_t*) &addrLen );
617
618        /* besides, do we get flooded? then bail out! */
619        if( --lpd_unsolicitedMsgCounter < 0 )
620            return;
621
622        if( res > 0 && res <= lpd_maxDatagramLength )
623        {
624            struct tr_pex foreignPeer =
625                {
626                    .port = 0, /* the peer-to-peer port is yet unknown */
627                    .flags = 0
628                };
629
630            foreignPeer.addr.addr.addr4 = foreignAddr.sin_addr;
631            if( tr_lpdConsiderAnnounce( &foreignPeer, foreignMsg ) != 0 )
632                return; /* OK so far, no log message */
633        }
634
635        tr_ndbg( "LPD", "Discarded invalid multicast message" );
636    }
637}
638
Note: See TracBrowser for help on using the repository browser.