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

Last change on this file since 11599 was 11599, checked in by charles, 11 years ago

(trunk) Join the 21st century and use only 1 space at the end sentences. This commit is nearly as important as the semi-annual ones that remove trailing spaces from the ends of lines of code... :)

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