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

Last change on this file since 12183 was 12183, checked in by jordan, 11 years ago

(trunk libT) as a followup to r12182, move LPD's periodic upkeep timer into the tr-lpd.c module where it can be started & stopped with the pre-existing tr_lpdInit() and tr_lpdUninit() functions.

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