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

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

(trunk) it's bad form to #include so many system headers in libtransmission/utils.h...

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