source: trunk/libtransmission/peeraz.h @ 1594

Last change on this file since 1594 was 1594, checked in by joshe, 15 years ago

Don't save or load cached peers for private torrents.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.6 KB
Line 
1/******************************************************************************
2 * $Id: peeraz.h 1594 2007-03-26 19:19:33Z joshe $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#define AZ_EXT_VERSION          1
26
27#define AZ_MSG_BT_HANDSHAKE     -1
28#define AZ_MSG_BT_KEEP_ALIVE    -2
29#define AZ_MSG_AZ_HANDSHAKE     -3
30#define AZ_MSG_AZ_PEER_EXCHANGE -4
31#define AZ_MSG_INVALID          -5
32
33#define AZ_MSG_IS_OPTIONAL( id ) \
34    ( AZ_MSG_AZ_PEER_EXCHANGE == (id) || AZ_MSG_IS_UNUSED( id ) )
35#define AZ_MSG_IS_UNUSED( id ) \
36    ( AZ_MSG_AZ_HANDSHAKE == (id) || AZ_MSG_BT_HANDSHAKE == (id) )
37
38static const struct
39{
40    char    * label;
41    const int len;
42    const int id;
43}
44az_msgs[] = {
45    { "AZ_PEER_EXCHANGE", 16, AZ_MSG_AZ_PEER_EXCHANGE },
46    { "AZ_HANDSHAKE",     12, AZ_MSG_AZ_HANDSHAKE },
47    { "BT_HANDSHAKE",     12, AZ_MSG_BT_HANDSHAKE },
48    { "BT_KEEP_ALIVE",    13, AZ_MSG_BT_KEEP_ALIVE },
49    { "BT_CHOKE",          8, PEER_MSG_CHOKE },
50    { "BT_UNCHOKE",       10, PEER_MSG_UNCHOKE },
51    { "BT_INTERESTED",    13, PEER_MSG_INTERESTED },
52    { "BT_UNINTERESTED",  15, PEER_MSG_UNINTERESTED },
53    { "BT_HAVE",           7, PEER_MSG_HAVE },
54    { "BT_BITFIELD",      11, PEER_MSG_BITFIELD },
55    { "BT_REQUEST",       10, PEER_MSG_REQUEST },
56    { "BT_PIECE",          8, PEER_MSG_PIECE },
57    { "BT_CANCEL",         9, PEER_MSG_CANCEL },
58};
59#define azmsgStr( idx ) ( az_msgs[(idx)].label )
60#define azmsgLen( idx ) ( az_msgs[(idx)].len )
61#define azmsgId( idx )  ( az_msgs[(idx)].id )
62#define azmsgCount()    ( (int)(sizeof( az_msgs ) / sizeof( az_msgs[0] ) ) )
63
64static inline int
65azmsgIdIndex( int id )
66{
67    int ii;
68
69    for( ii = 0; azmsgCount() > ii; ii++ )
70    {
71        if( id == azmsgId( ii ) )
72        {
73            return ii;
74        }
75    }
76
77    assert( 0 );
78
79    return 0;
80}
81
82static inline int
83azmsgNameIndex( const char * name, int len )
84{
85    int ii;
86
87    for( ii = 0; azmsgCount() > ii; ii++ )
88    {
89        if( azmsgLen( ii ) == len &&
90            0 == memcmp( azmsgStr( ii ), name, len ) )
91        {
92            return ii;
93        }
94    }
95
96    return -1;
97}
98
99static uint8_t *
100makeAZHandshake( tr_torrent_t * tor, tr_peer_t * peer, int * buflen )
101{
102    char       * buf;
103    benc_val_t   val, * idval, * clientval, * versval;
104    benc_val_t * tcpval, * msgsval, * msgdictval, * msgidval, * msgversval;
105    int          len, max, idx;
106    uint8_t      vers;
107
108    *buflen = 0;
109    idx = azmsgIdIndex( AZ_MSG_AZ_HANDSHAKE );
110    len = 4 + 4 + azmsgLen( idx ) + 1;
111    buf = malloc( len );
112    if( NULL == buf )
113    {
114        return NULL;
115    }
116
117    /* set length to zero for now, we won't know it until after bencoding */
118    TR_HTONL( 0, buf );
119    /* set name length, name, and version */
120    TR_HTONL( azmsgLen( idx ), buf + 4 );
121    memcpy( buf + 8, azmsgStr( idx ), azmsgLen( idx ) );
122    buf[8 + azmsgLen( idx )] = AZ_EXT_VERSION;
123
124    /* start building a dictionary for handshake data */
125    tr_bencInit( &val, TYPE_DICT );
126    if( tr_bencDictAppendNofree( &val,
127                                 "identity",       &idval,
128                                 "client",         &clientval,
129                                 "version",        &versval,
130                                 "tcp_port",       &tcpval,
131                                 "messages",       &msgsval,
132                                 NULL ) )
133    {
134        free( buf );
135        tr_bencFree( &val );
136        return NULL;
137    }
138
139    /* fill in the dictionary values */
140    tr_bencInitStr( idval,     tor->azId,      TR_AZ_ID_LEN, 1 );
141    tr_bencInitStr( clientval, TR_NAME,        0,            1 );
142    tr_bencInitStr( versval,   VERSION_STRING, 0,            1 );
143    tr_bencInitInt( tcpval,    tor->publicPort );
144    tr_bencInit(    msgsval,   TYPE_LIST );
145
146    /* fill in the supported message list */
147    vers = AZ_EXT_VERSION;
148    for( idx = 0; azmsgCount() > idx; idx++ )
149    {
150        if( AZ_MSG_IS_UNUSED( azmsgId( idx ) ) )
151        {
152            continue;
153        }
154        /* XXX azureus sucks, we can't enable or disable pex after handshake */
155        if( AZ_MSG_AZ_PEER_EXCHANGE == azmsgId( idx ) && peer->private )
156        {
157            /* no point in saying we can do pex if the torrent is private */
158            continue;
159        }
160        if( tr_bencListAppend( msgsval, &msgdictval, NULL ) )
161        {
162            tr_bencFree( &val );
163            free( buf );
164            return NULL;
165        }
166        /* each item in the list is a dict with id and ver keys */
167        tr_bencInit( msgdictval, TYPE_DICT );
168        if( tr_bencDictAppendNofree( msgdictval,
169                                     "id",  &msgidval,
170                                     "ver", &msgversval,
171                                     NULL ) )
172        {
173            tr_bencFree( &val );
174            free( buf );
175            return NULL;
176        }
177        tr_bencInitStr( msgidval,   azmsgStr( idx ), azmsgLen( idx ), 1 );
178        tr_bencInitStr( msgversval, &vers,           1,               1 );
179    }
180
181    /* bencode the dictionary and append it to the buffer */
182    max = len;
183    if( tr_bencSave( &val, &buf, &len, &max ) )
184    {
185        tr_bencFree( &val );
186        free( buf );
187        return NULL;
188    }
189
190    tr_bencFree( &val );
191    /* we know the length now, fill it in */
192    TR_HTONL( len - 4, buf );
193
194    /* XXX is there a way to tell azureus that the public port has changed? */
195    peer->advertisedPort = tor->publicPort;
196
197    *buflen = len;
198    return ( uint8_t * )buf;
199}
200
201static int
202peertreeToBencAZ( tr_peertree_t * tree, benc_val_t * val )
203{
204    int                   count, jj;
205    tr_peertree_entry_t * ii;
206
207    tr_bencInit( val, TYPE_LIST );
208    count = peertreeCount( tree );
209    if( 0 == count )
210    {
211        return 0;
212    }
213    if( tr_bencListExtend( val, count ) )
214    {
215        return 1;
216    }
217
218    ii = peertreeFirst( tree );
219    jj = val->val.l.count;
220    while( NULL != ii )
221    {
222        assert( jj < val->val.l.alloc );
223        tr_bencInitStr( &val->val.l.vals[jj], ii->peer, 6, 1 );
224        val->val.l.count++;
225        ii = peertreeNext( tree, ii );
226        jj++;
227    }
228
229    return 0;
230}
231
232static char *
233makeAZPex( tr_torrent_t * tor, tr_peer_t * peer, int * len )
234{
235    benc_val_t val;
236    char * buf;
237
238    assert( !peer->private );
239    tr_bencInitStr( &val, tor->info.hash, sizeof( tor->info.hash ), 1 );
240    buf = makeCommonPex( tor, peer, len, peertreeToBencAZ, "infohash", &val );
241
242    return buf;
243}
244
245static int
246sendAZHandshake( tr_torrent_t * tor, tr_peer_t * peer )
247{
248    uint8_t * buf;
249    int len;
250
251    /* XXX this is kind of evil to use this buffer like this */
252    if( NULL == peer->outMessages )
253    {
254        buf = makeAZHandshake( tor, peer, &len );
255        if( NULL == buf )
256        {
257            return TR_NET_CLOSE;
258        }
259        peer->outMessages     = buf;
260        peer->outMessagesSize = len;
261        peer->outMessagesPos  = 0;
262    }
263
264    len = tr_netSend( peer->socket, peer->outMessages + peer->outMessagesPos,
265                      peer->outMessagesSize - peer->outMessagesPos );
266    if( peer->outMessagesPos + len < peer->outMessagesSize )
267    {
268        peer->outMessagesPos += len;
269        return TR_NET_BLOCK;
270    }
271
272    len = peer->outMessagesSize;
273    free( peer->outMessages );
274    peer->outMessages     = NULL;
275    peer->outMessagesSize = 0;
276    peer->outMessagesPos  = 0;
277
278    return len;
279}
280
281static inline int
282parseAZMessageHeader( tr_peer_t * peer, uint8_t * buf, int len,
283                      int * msgidret, int * msglenret )
284{
285    uint8_t * name, vers;
286    int       off, namelen, msglen, index, msgid;
287
288    if( 8 > len )
289    {
290        return TR_NET_BLOCK;
291    }
292    /* message length */
293    TR_NTOHL( buf, msglen );
294    msglen += 4;
295    off = 4;
296    if( msglen > len )
297    {
298        return TR_NET_BLOCK;
299    }
300    if( 9 > msglen )
301    {
302        peer_dbg( "Azureus peer message is too short to make sense" );
303        return TR_NET_CLOSE;
304    }
305    /* name length */
306    TR_NTOHL( buf + off, namelen );
307    off += 4;
308    if( off + namelen + 1 > msglen )
309    {
310        peer_dbg( "Azureus peer message name is too long to make sense" );
311        return TR_NET_CLOSE;
312    }
313    /* message name */
314    name = buf + off;
315    off += namelen;
316    /* message version */
317    vers = buf[off];
318    off++;
319    /* get payload length from message length */
320    msglen -= off;
321
322    index = azmsgNameIndex( ( char * )name, namelen );
323    if( AZ_EXT_VERSION != vers )
324    {
325        /* XXX should we close the connection here? */
326        peer_dbg( "unsupported Azureus message version %hhu", vers );
327        msgid = AZ_MSG_INVALID;
328    }
329    else if( 0 > index )
330    {
331        name[namelen] = '\0';
332        peer_dbg( "got unknown Azureus message: \"%s\"", name );
333        name[namelen] = vers;
334        msgid = AZ_MSG_INVALID;
335    }
336    else
337    {
338        msgid = azmsgId( index );
339    }
340
341    *msgidret  = msgid;
342    *msglenret = msglen;
343
344    return off;
345}
346
347static inline int
348parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len )
349{
350    benc_val_t      val, * sub, * dict, * subsub;
351    tr_bitfield_t * msgs;
352    int             ii, idx;
353
354    if( tr_bencLoad( buf, len, &val, NULL ) )
355    {
356        peer_dbg( "invalid bencoding in Azureus handshake" );
357        return TR_ERROR;
358    }
359
360    if( TYPE_DICT != val.type )
361    {
362        peer_dbg( "Azureus handshake is not a dictionary" );
363        tr_bencFree( &val );
364        return TR_ERROR;
365    }
366
367    /* get the peer's listening port */
368    sub = tr_bencDictFind( &val, "tcp_port" );
369    if( NULL != sub )
370    {
371        if( TYPE_INT == sub->type && 0x0 < sub->val.i && 0xffff >= sub->val.i )
372        {
373            peer->port = htons( sub->val.i );
374        }
375    }
376
377    /* find the supported message list */
378    sub = tr_bencDictFind( &val, "messages" );
379    if( NULL == sub && TYPE_LIST != sub->type )
380    {
381        tr_bencFree( &val );
382        peer_dbg( "Azureus handshake is missing 'messages'" );
383        return TR_ERROR;
384    }
385
386    /* fill bitmask with supported message info */
387    msgs = tr_bitfieldNew( azmsgCount() );
388    for( ii = 0; ii < sub->val.l.count; ii++ )
389    {
390        dict = &sub->val.l.vals[ii];
391        if( TYPE_DICT != dict->type )
392        {
393            continue;
394        }
395        subsub = tr_bencDictFind( dict, "id" );
396        if( NULL == subsub || TYPE_STR != subsub->type )
397        {
398            continue;
399        }
400        idx = azmsgNameIndex( subsub->val.s.s, subsub->val.s.i );
401        if( 0 > idx )
402        {
403            continue;
404        }
405        subsub = tr_bencDictFind( dict, "ver" );
406        if( NULL == subsub || TYPE_STR != subsub->type ||
407            1 != subsub->val.s.i || AZ_EXT_VERSION != subsub->val.s.s[0] )
408        {
409            continue;
410        }
411        tr_bitfieldAdd( msgs, idx );
412    }
413
414    tr_bencFree( &val );
415
416    /* check bitmask to see if we're missing any messages */
417    for( ii = 0; azmsgCount() > ii; ii++ )
418    {
419        if( AZ_MSG_AZ_PEER_EXCHANGE == azmsgId( ii ) )
420        {
421            peer->pexStatus = tr_bitfieldHas( msgs, ii );
422        }
423        if( !AZ_MSG_IS_OPTIONAL( azmsgId( ii ) ) &&
424            !tr_bitfieldHas( msgs, ii ) )
425        {
426            peer_dbg( "Azureus message %s not supported", azmsgStr( ii ) );
427            tr_bitfieldFree( msgs );
428            return TR_ERROR;
429        }
430    }
431    tr_bitfieldFree( msgs );
432
433    return TR_OK;
434}
435
436static inline int
437parseAZPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len )
438{
439    tr_info_t * info = &tor->info;
440    benc_val_t  val, * list, * pair;
441    int         ii;
442
443    if( peer->private || PEX_PEER_CUTOFF <= tor->peerCount )
444    {
445        return TR_OK;
446    }
447
448    if( tr_bencLoad( buf, len, &val, NULL ) )
449    {
450        peer_dbg( "invalid bencoding in Azureus peer exchange" );
451        return TR_ERROR;
452    }
453    if( TYPE_DICT != val.type )
454    {
455        tr_bencFree( &val );
456        peer_dbg( "Azureus peer exchange is not a dictionary" );
457        return TR_ERROR;
458    }
459
460    list = tr_bencDictFind( &val, "infohash" );
461    if( NULL == list || TYPE_STR != list->type ||
462        sizeof( info->hash ) != list->val.s.i ||
463        0 != memcmp( info->hash, list->val.s.s, sizeof( info->hash ) ) )
464    {
465        tr_bencFree( &val );
466        peer_dbg( "Azureus peer exchange has missing or invalid 'infohash'" );
467        return TR_ERROR;
468    }
469
470    list = tr_bencDictFind( &val, "added" );
471    if( NULL == list || TYPE_LIST != list->type )
472    {
473        tr_bencFree( &val );
474        return TR_OK;
475    }
476
477    for( ii = 0; ii < list->val.l.count; ii++ )
478    {
479        pair = &list->val.l.vals[ii];
480        if( TYPE_STR == pair->type && 6 == pair->val.s.i )
481        {
482            tr_torrentAddCompact( tor, TR_PEER_FROM_PEX,
483                                  ( uint8_t * )pair->val.s.s, 1 );
484        }
485    }
486
487    tr_bencFree( &val );
488
489    return TR_OK;
490}
Note: See TracBrowser for help on using the repository browser.