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

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

(trunk libT) oops, two more files needing $Id$ properties

  • 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 11091 2010-08-01 20:20:01Z 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 inline int lpd_configureSocket( int sock, int add )
258{
259#ifdef WIN32
260    unsigned long flags = 1;
261
262    if (add != O_NONBLOCK)
263        return -1;              /* not supported */
264    if (ioctlsocket(sock, FIONBIO, &flags) == SOCKET_ERROR)
265        return -1;
266#else
267    /* read-modify-write socket flags */
268    int flags = fcntl( sock, F_GETFL );
269
270    if( flags < 0 )
271        return -1;
272
273    if( fcntl( sock, F_SETFL, add | flags ) == -1 )
274        return -1;
275#endif
276
277    return add;
278}
279
280/**
281* @brief Initializes Local Peer Discovery for this node
282*
283* For the most part, this means setting up an appropriately configured multicast socket
284* and event-based message handling.
285*
286* @remark Since the LPD service does not use another protocol family yet, this code is
287* IPv4 only for the time being.
288*/
289int tr_lpdInit( tr_session* ss, tr_address* tr_addr UNUSED )
290{
291    struct ip_mreq mcastReq;
292    const int opt_on = 1, opt_off = 0;
293
294    if( session ) /* already initialized */
295        return -1;
296
297    assert( lpd_announceInterval > 0 );
298    assert( lpd_announceScope > 0 );
299
300    lpd_port = tr_sessionGetPeerPort( ss );
301    if( lpd_port <= 0 )
302        return -1;
303
304    tr_ndbg( "LPD", "Initialising Local Peer Discovery" );
305
306    /* setup datagram socket (receive) */
307    {
308        lpd_socket = socket( PF_INET, SOCK_DGRAM, 0 );
309        if( lpd_socket < 0 )
310            goto fail;
311
312        /* enable non-blocking operation */
313        if( lpd_configureSocket( lpd_socket, O_NONBLOCK ) < 0 )
314            goto fail;
315
316        if( setsockopt( lpd_socket, SOL_SOCKET, SO_REUSEADDR,
317                &opt_on, sizeof opt_on ) < 0 )
318            goto fail;
319
320        memset( &lpd_mcastAddr, 0, sizeof lpd_mcastAddr );
321        lpd_mcastAddr.sin_family = AF_INET;
322        lpd_mcastAddr.sin_port = htons( lpd_mcastPort );
323        if( inet_pton( lpd_mcastAddr.sin_family, lpd_mcastGroup,
324                &lpd_mcastAddr.sin_addr ) < 0 )
325            goto fail;
326
327        if( bind( lpd_socket, (struct sockaddr*) &lpd_mcastAddr,
328                sizeof lpd_mcastAddr ) < 0 )
329            goto fail;
330
331        /* we want to join that LPD multicast group */
332        memset( &mcastReq, 0, sizeof mcastReq );
333        mcastReq.imr_multiaddr = lpd_mcastAddr.sin_addr;
334        mcastReq.imr_interface.s_addr = htonl( INADDR_ANY );
335        if( setsockopt( lpd_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
336                &mcastReq, sizeof mcastReq ) < 0 )
337            goto fail;
338
339        if( setsockopt( lpd_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
340                &opt_off, sizeof opt_off ) < 0 )
341            goto fail;
342    }
343
344    /* setup datagram socket (send) */
345    {
346        const unsigned char scope = lpd_announceScope;
347
348        lpd_socket2 = socket( PF_INET, SOCK_DGRAM, 0 );
349        if( lpd_socket2 < 0 )
350            goto fail;
351
352        /* enable non-blocking operation */
353        if( lpd_configureSocket( lpd_socket2, O_NONBLOCK ) < 0 )
354            goto fail;
355
356        /* configure outbound multicast TTL */
357        if( setsockopt( lpd_socket2, IPPROTO_IP, IP_MULTICAST_TTL,
358                &scope, sizeof scope ) < 0 )
359            goto fail;
360
361        if( setsockopt( lpd_socket2, IPPROTO_IP, IP_MULTICAST_LOOP,
362                &opt_off, sizeof opt_off ) < 0 )
363            goto fail;
364    }
365
366    session = ss;
367
368    /* Note: lpd_unsolicitedMsgCounter remains 0 until the first timeout event, thus
369     * any announcement received during the initial interval will be discarded. */
370
371    event_set( &lpd_event, lpd_socket, EV_READ | EV_PERSIST, event_callback, NULL );
372    event_add( &lpd_event, NULL );
373
374    tr_ndbg( "LPD", "Local Peer Discovery initialised" );
375
376    return 1;
377
378    fail:
379    {
380        const int save = errno;
381        close( lpd_socket );
382        close( lpd_socket2 );
383        lpd_socket = lpd_socket2 = -1;
384        session = NULL;
385        tr_ndbg( "LPD", "LPD initialisation failed (errno = %d)", save );
386        errno = save;
387    }
388
389
390    return -1;
391}
392
393void tr_lpdUninit( tr_session* ss )
394{
395    if( session != ss )
396        return;
397
398    tr_ndbg( "LPD", "Uninitialising Local Peer Discovery" );
399
400    event_del( &lpd_event );
401
402    /* just shut down, we won't remember any former nodes */
403    EVUTIL_CLOSESOCKET( lpd_socket );
404    EVUTIL_CLOSESOCKET( lpd_socket2 );
405    tr_ndbg( "LPD", "Done uninitialising Local Peer Discovery" );
406
407    session = NULL;
408}
409
410tr_bool tr_lpdEnabled( const tr_session* ss )
411{
412    return ss && ( ss == session );
413}
414
415
416/**
417* @cond
418* @brief Performs some (internal) software consistency checks at compile time.
419* @remark Declared inline for the compiler not to allege us of feeding unused
420* functions. In any other respect, lpd_consistencyCheck is an orphaned function.
421*/
422static inline void lpd_consistencyCheck( void )
423{
424    /* if the following check fails, the definition of a hash string has changed
425     * without our knowledge; revise string handling in functions tr_lpdSendAnnounce
426     * and tr_lpdConsiderAnnounce. However, the code is designed to function as long
427     * as interfaces to the rest of the lib remain compatible with char* strings. */
428    STATIC_ASSERT( sizeof(lpd_torStaticType->info.hashString[0]) == sizeof(char) );
429}
430/**
431* @endcond */
432
433
434/**
435* @defgroup LdsProto LPD announcement processing
436* @{
437*/
438
439/**
440* @brief Announce the given torrent on the local network
441*
442* @param[in] t Torrent to announce
443* @return Returns TRUE on success
444*
445* Send a query for torrent t out to the LPD multicast group (or the LAN, for that
446* matter).  A listening client on the same network might react by adding us to his
447* peer pool for torrent t.
448*/
449tr_bool tr_lpdSendAnnounce( const tr_torrent* t )
450{
451    size_t i;
452    const char fmt[] =
453        "BT-SEARCH * HTTP/%u.%u" CRLF
454        "Host: %s:%u" CRLF
455        "Port: %u" CRLF
456        "Infohash: %s" CRLF
457        CRLF
458        CRLF;
459
460    char hashString[lengthof( t->info.hashString )];
461    char query[lpd_maxDatagramLength + 1] = { };
462
463    if( t == NULL )
464        return FALSE;
465
466    /* make sure the hash string is normalized, just in case */
467    for( i = 0; i < sizeof hashString; i++ )
468        hashString[i] = toupper( t->info.hashString[i] );
469
470    /* prepare a zero-terminated announce message */
471    snprintf( query, lpd_maxDatagramLength + 1, fmt, 1, 1,
472        lpd_mcastGroup, lpd_mcastPort, lpd_port, hashString );
473
474    /* actually send the query out using [lpd_socket2] */
475    {
476        const int len = strlen( query );
477
478        /* destination address info has already been set up in tr_lpdInit(),
479         * so we refrain from preparing another sockaddr_in here */
480        int res = sendto( lpd_socket2, query, len, 0,
481            (const struct sockaddr*) &lpd_mcastAddr, sizeof lpd_mcastAddr );
482
483        if( res != len )
484            return FALSE;
485    }
486
487    tr_tordbg( t, "LPD announce message away" );
488
489    return TRUE;
490}
491
492/**
493* @brief Process incoming unsolicited messages and add the peer to the announced
494* torrent if all checks are passed.
495*
496* @param[in,out] peer Adress information of the peer to add
497* @param[in] msg The announcement message to consider
498* @return Returns 0 if any input parameter or the announce was invalid, 1 if the peer
499* was successfully added, -1 if not; a non-null return value indicates a side-effect to
500* the peer in/out parameter.
501*
502* @note The port information gets added to the peer structure if tr_lpdConsiderAnnounce
503* is able to extract the necessary information from the announce message.  That is, if
504* return != 0, the caller may retrieve the value from the passed structure.
505*/
506static int tr_lpdConsiderAnnounce( tr_pex* peer, const char* const msg )
507{
508    enum
509    {
510        maxValueLen = 25,
511        maxHashLen = lengthof(lpd_torStaticType->info.hashString)
512    };
513
514    struct lpd_protocolVersion ver = { -1, -1 };
515    char value[maxValueLen] = { };
516    char hashString[maxHashLen] = { };
517    int res = 0, peerPort = 0;
518
519    if( peer != NULL && msg != NULL )
520    {
521        tr_torrent* tor = NULL;
522
523        const char* params = lpd_extractHeader( msg, &ver );
524        if( params == NULL || ver.major != 1 ) /* allow messages of protocol v1 */
525            return 0;
526
527        /* save the effort to check Host, which seems to be optional anyway */
528
529        if( lpd_extractParam( params, "Port", maxValueLen, value ) == 0 )
530            return 0;
531
532        /* determine announced peer port, refuse if value too large */
533        if( sscanf( value, "%d", &peerPort ) != 1 || peerPort > (in_port_t)-1 )
534            return 0;
535
536        peer->port = htons( peerPort );
537        res = -1; /* signal caller side-effect to peer->port via return != 0 */
538
539        if( lpd_extractParam( params, "Infohash", maxHashLen, hashString ) == 0 )
540            return res;
541
542        tor = tr_torrentFindFromHashString( session, hashString );
543
544        if( tr_isTorrent( tor ) && tr_torrentAllowsLPD( tor ) )
545        {
546            /* we found a suitable peer, add it to the torrent */
547            tr_peerMgrAddPex( tor, TR_PEER_FROM_LPD, peer, -1 );
548            tr_tordbg( tor, "Learned %d local peer from LPD (%s:%u)",
549                1, inet_ntoa( peer->addr.addr.addr4 ), peerPort );
550
551            /* periodic reconnectPulse() deals with the rest... */
552
553            return 1;
554        }
555        else
556            tr_ndbg( "LPD", "Cannot serve torrent #%s", hashString );
557    }
558
559    return res;
560}
561
562/**
563* @} */
564
565/**
566* @note Since it possible for tr_lpdAnnounceMore to get called from outside the LPD module,
567* the function needs to be informed of the externally employed housekeeping interval.
568* Further, by setting interval to zero (or negative) the caller may actually disable LPD
569* announces on a per-interval basis.
570*/
571int tr_lpdAnnounceMore( const time_t now, const int interval )
572{
573    tr_torrent* tor = NULL;
574    int announcesSent = 0;
575
576    if( !tr_isSession( session ) )
577        return -1;
578
579    while(( tor = tr_torrentNext( session, tor ) )
580          && tr_sessionAllowsLPD( session ) )
581    {
582        if( tr_isTorrent( tor ) )
583        {
584            if( !tr_torrentAllowsLPD( tor ) || (
585                    ( tr_torrentGetActivity( tor ) != TR_STATUS_DOWNLOAD ) &&
586                    ( tr_torrentGetActivity( tor ) != TR_STATUS_SEED ) ) )
587                continue;
588
589            if( tor->lpdAnnounceAt <= now )
590            {
591                if( tr_lpdSendAnnounce( tor ) )
592                    announcesSent++;
593
594                tor->lpdAnnounceAt = now + lpd_announceInterval;
595                break; /* that's enough; for this interval */
596            }
597        }
598    }
599
600    /* perform housekeeping for the flood protection mechanism */
601    {
602        const int maxAnnounceCap = interval * lpd_announceCapFactor;
603
604        if( lpd_unsolicitedMsgCounter < 0 )
605            tr_ninf( "LPD", "Dropped %d announces in the last interval (max. %d "
606                     "allowed)", -lpd_unsolicitedMsgCounter, maxAnnounceCap );
607
608        lpd_unsolicitedMsgCounter = maxAnnounceCap;
609    }
610
611    return announcesSent;
612}
613
614/**
615* @brief Processing of timeout notifications and incoming data on the socket
616* @note maximum rate of read events is limited according to @a lpd_maxAnnounceCap
617* @see DoS */
618static void event_callback( int s UNUSED, short type, void* ignore UNUSED )
619{
620    assert( tr_isSession( session ) );
621
622    /* do not allow announces to be processed if LPD is disabled */
623    if( !tr_sessionAllowsLPD( session ) )
624        return;
625
626    if( ( type & EV_READ ) != 0 )
627    {
628        struct sockaddr_in foreignAddr;
629        int addrLen = sizeof foreignAddr;
630
631        /* be paranoid enough about zero terminating the foreign string */
632        char foreignMsg[lpd_maxDatagramLength + 1] = { };
633
634        /* process local announcement from foreign peer */
635        int res = recvfrom( lpd_socket, foreignMsg, lpd_maxDatagramLength,
636            0, (struct sockaddr*) &foreignAddr, (socklen_t*) &addrLen );
637
638        /* besides, do we get flooded? then bail out! */
639        if( --lpd_unsolicitedMsgCounter < 0 )
640            return;
641
642        if( res > 0 && res <= lpd_maxDatagramLength )
643        {
644            struct tr_pex foreignPeer =
645                {
646                    .port = 0, /* the peer-to-peer port is yet unknown */
647                    .flags = 0
648                };
649
650            foreignPeer.addr.addr.addr4 = foreignAddr.sin_addr;
651            if( tr_lpdConsiderAnnounce( &foreignPeer, foreignMsg ) != 0 )
652                return; /* OK so far, no log message */
653        }
654
655        tr_ndbg( "LPD", "Discarded invalid multicast message" );
656    }
657}
658
Note: See TracBrowser for help on using the repository browser.