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

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

(trunk libT) #3617 "1000+ warnings of 'inlining failed' in libtransmission when compiled with gcc 4.4.4" -- fixed.

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