source: trunk/libtransmission/peeraz.h @ 1603

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

Show peers found and used when adding new peers.
Accept --without-foo in addition to --disable-foo in the configure script.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.0 KB
Line 
1/******************************************************************************
2 * $Id: peeraz.h 1603 2007-03-29 00:19:09Z 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        if( AZ_MSG_AZ_PEER_EXCHANGE == azmsgId( idx ) && peer->private )
155        {
156            /* no point in saying we can do pex if the torrent is private */
157            continue;
158        }
159        if( tr_bencListAppend( msgsval, &msgdictval, NULL ) )
160        {
161            tr_bencFree( &val );
162            free( buf );
163            return NULL;
164        }
165        /* each item in the list is a dict with id and ver keys */
166        tr_bencInit( msgdictval, TYPE_DICT );
167        if( tr_bencDictAppendNofree( msgdictval,
168                                     "id",  &msgidval,
169                                     "ver", &msgversval,
170                                     NULL ) )
171        {
172            tr_bencFree( &val );
173            free( buf );
174            return NULL;
175        }
176        tr_bencInitStr( msgidval,   azmsgStr( idx ), azmsgLen( idx ), 1 );
177        tr_bencInitStr( msgversval, &vers,           1,               1 );
178    }
179
180    /* bencode the dictionary and append it to the buffer */
181    max = len;
182    if( tr_bencSave( &val, &buf, &len, &max ) )
183    {
184        tr_bencFree( &val );
185        free( buf );
186        return NULL;
187    }
188
189    tr_bencFree( &val );
190    /* we know the length now, fill it in */
191    TR_HTONL( len - 4, buf );
192
193    /* XXX is there a way to tell azureus that the public port has changed? */
194    peer->advertisedPort = tor->publicPort;
195
196    *buflen = len;
197    return ( uint8_t * )buf;
198}
199
200static int
201peertreeToBencAZ( tr_peertree_t * tree, benc_val_t * val )
202{
203    int                   count, jj;
204    tr_peertree_entry_t * ii;
205
206    tr_bencInit( val, TYPE_LIST );
207    count = peertreeCount( tree );
208    if( 0 == count )
209    {
210        return 0;
211    }
212    if( tr_bencListExtend( val, count ) )
213    {
214        return 1;
215    }
216
217    ii = peertreeFirst( tree );
218    jj = val->val.l.count;
219    while( NULL != ii )
220    {
221        assert( jj < val->val.l.alloc );
222        tr_bencInitStr( &val->val.l.vals[jj], ii->peer, 6, 1 );
223        val->val.l.count++;
224        ii = peertreeNext( tree, ii );
225        jj++;
226    }
227
228    return 0;
229}
230
231static char *
232makeAZPex( tr_torrent_t * tor, tr_peer_t * peer, int * len )
233{
234    benc_val_t val;
235    char * buf;
236
237    peer_dbg( "SEND azureus-pex" );
238
239    assert( !peer->private );
240    tr_bencInitStr( &val, tor->info.hash, sizeof( tor->info.hash ), 1 );
241    buf = makeCommonPex( tor, peer, len, peertreeToBencAZ, "infohash", &val );
242
243    return buf;
244}
245
246static int
247sendAZHandshake( tr_torrent_t * tor, tr_peer_t * peer )
248{
249    uint8_t * buf;
250    int len;
251
252    /* XXX this is kind of evil to use this buffer like this */
253    if( NULL == peer->outMessages )
254    {
255        buf = makeAZHandshake( tor, peer, &len );
256        if( NULL == buf )
257        {
258            return TR_NET_CLOSE;
259        }
260        peer->outMessages     = buf;
261        peer->outMessagesSize = len;
262        peer->outMessagesPos  = 0;
263    }
264
265    len = tr_netSend( peer->socket, peer->outMessages + peer->outMessagesPos,
266                      peer->outMessagesSize - peer->outMessagesPos );
267    if( peer->outMessagesPos + len < peer->outMessagesSize )
268    {
269        peer->outMessagesPos += len;
270        return TR_NET_BLOCK;
271    }
272
273    peer_dbg( "SEND azureus-handshake" );
274
275    len = peer->outMessagesSize;
276    free( peer->outMessages );
277    peer->outMessages     = NULL;
278    peer->outMessagesSize = 0;
279    peer->outMessagesPos  = 0;
280
281    return len;
282}
283
284static inline int
285parseAZMessageHeader( tr_peer_t * peer, uint8_t * buf, int len,
286                      int * msgidret, int * msglenret )
287{
288    uint8_t * name, vers;
289    int       off, namelen, msglen, index, msgid;
290
291    if( 8 > len )
292    {
293        return TR_NET_BLOCK;
294    }
295    /* message length */
296    TR_NTOHL( buf, msglen );
297    msglen += 4;
298    off = 4;
299    if( msglen > len )
300    {
301        return TR_NET_BLOCK;
302    }
303    if( 9 > msglen )
304    {
305        peer_dbg( "azureus peer message is too short to make sense" );
306        return TR_NET_CLOSE;
307    }
308    /* name length */
309    TR_NTOHL( buf + off, namelen );
310    off += 4;
311    if( off + namelen + 1 > msglen )
312    {
313        peer_dbg( "azureus peer message name is too long to make sense" );
314        return TR_NET_CLOSE;
315    }
316    /* message name */
317    name = buf + off;
318    off += namelen;
319    /* message version */
320    vers = buf[off];
321    off++;
322    /* get payload length from message length */
323    msglen -= off;
324
325    index = azmsgNameIndex( ( char * )name, namelen );
326    if( AZ_EXT_VERSION != vers )
327    {
328        /* XXX should we close the connection here? */
329        peer_dbg( "GET  unsupported azureus message version %hhu", vers );
330        msgid = AZ_MSG_INVALID;
331    }
332    else if( 0 > index )
333    {
334        name[namelen] = '\0';
335        peer_dbg( "GET  unknown azureus message: \"%s\"", name );
336        name[namelen] = vers;
337        msgid = AZ_MSG_INVALID;
338    }
339    else
340    {
341        msgid = azmsgId( index );
342    }
343
344    *msgidret  = msgid;
345    *msglenret = msglen;
346
347    return off;
348}
349
350static inline int
351parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len )
352{
353    benc_val_t      val, * sub, * dict, * subsub;
354    tr_bitfield_t * msgs;
355    int             ii, idx;
356
357    if( tr_bencLoad( buf, len, &val, NULL ) )
358    {
359        peer_dbg( "GET  azureus-handshake, invalid bencoding" );
360        return TR_ERROR;
361    }
362
363    if( TYPE_DICT != val.type )
364    {
365        peer_dbg( "GET  azureus-handshake, data not a dictionary" );
366        tr_bencFree( &val );
367        return TR_ERROR;
368    }
369
370    /* get the peer's listening port */
371    sub = tr_bencDictFind( &val, "tcp_port" );
372    if( NULL != sub )
373    {
374        if( TYPE_INT == sub->type && 0x0 < sub->val.i && 0xffff >= sub->val.i )
375        {
376            peer->port = htons( sub->val.i );
377        }
378    }
379
380    /* find the supported message list */
381    sub = tr_bencDictFind( &val, "messages" );
382    if( NULL == sub && TYPE_LIST != sub->type )
383    {
384        tr_bencFree( &val );
385        peer_dbg( "GET  azureus-handshake, missing 'messages'" );
386        return TR_ERROR;
387    }
388
389    peer_dbg( "GET  azureus-handshake, ok" );
390
391    /* fill bitmask with supported message info */
392    msgs = tr_bitfieldNew( azmsgCount() );
393    for( ii = 0; ii < sub->val.l.count; ii++ )
394    {
395        dict = &sub->val.l.vals[ii];
396        if( TYPE_DICT != dict->type )
397        {
398            continue;
399        }
400        subsub = tr_bencDictFind( dict, "id" );
401        if( NULL == subsub || TYPE_STR != subsub->type )
402        {
403            continue;
404        }
405        idx = azmsgNameIndex( subsub->val.s.s, subsub->val.s.i );
406        if( 0 > idx )
407        {
408            continue;
409        }
410        subsub = tr_bencDictFind( dict, "ver" );
411        if( NULL == subsub || TYPE_STR != subsub->type ||
412            1 != subsub->val.s.i || AZ_EXT_VERSION != subsub->val.s.s[0] )
413        {
414            continue;
415        }
416        tr_bitfieldAdd( msgs, idx );
417    }
418
419    tr_bencFree( &val );
420
421    /* check bitmask to see if we're missing any messages */
422    for( ii = 0; azmsgCount() > ii; ii++ )
423    {
424        if( AZ_MSG_AZ_PEER_EXCHANGE == azmsgId( ii ) )
425        {
426            peer->pexStatus = tr_bitfieldHas( msgs, ii );
427        }
428        if( !AZ_MSG_IS_OPTIONAL( azmsgId( ii ) ) &&
429            !tr_bitfieldHas( msgs, ii ) )
430        {
431            peer_dbg( "azureus message %s not supported by peer",
432                      azmsgStr( ii ) );
433            tr_bitfieldFree( msgs );
434            return TR_ERROR;
435        }
436    }
437    tr_bitfieldFree( msgs );
438
439    return TR_OK;
440}
441
442static inline int
443parseAZPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len )
444{
445    tr_info_t * info = &tor->info;
446    benc_val_t  val, * list, * pair;
447    int         ii, used;
448
449    if( peer->private || PEX_PEER_CUTOFF <= tor->peerCount )
450    {
451        peer_dbg( "GET  azureus-pex, ignoring p=%i c=(%i<=%i)",
452                  peer->private, PEX_PEER_CUTOFF, tor->peerCount );
453        return TR_OK;
454    }
455
456    if( tr_bencLoad( buf, len, &val, NULL ) )
457    {
458        peer_dbg( "GET  azureus-pex, invalid bencoding" );
459        return TR_ERROR;
460    }
461    if( TYPE_DICT != val.type )
462    {
463        tr_bencFree( &val );
464        peer_dbg( "GET  azureus-pex, data not a dictionary" );
465        return TR_ERROR;
466    }
467
468    list = tr_bencDictFind( &val, "infohash" );
469    if( NULL == list || TYPE_STR != list->type ||
470        sizeof( info->hash ) != list->val.s.i ||
471        0 != memcmp( info->hash, list->val.s.s, sizeof( info->hash ) ) )
472    {
473        tr_bencFree( &val );
474        peer_dbg( "GET  azureus-pex, bad infohash" );
475        return TR_ERROR;
476    }
477
478    list = tr_bencDictFind( &val, "added" );
479    if( NULL == list || TYPE_LIST != list->type )
480    {
481        peer_dbg( "GET  azureus-pex, no peers" );
482        tr_bencFree( &val );
483        return TR_OK;
484    }
485
486    used = 0;
487    for( ii = 0; ii < list->val.l.count; ii++ )
488    {
489        pair = &list->val.l.vals[ii];
490        if( TYPE_STR == pair->type && 6 == pair->val.s.i )
491        {
492            used += tr_torrentAddCompact( tor, TR_PEER_FROM_PEX,
493                                          ( uint8_t * )pair->val.s.s, 1 );
494        }
495    }
496
497    peer_dbg( "GET  azureus-pex, found %i peers, using %i",
498              list->val.l.count, used );
499
500    tr_bencFree( &val );
501
502    return TR_OK;
503}
Note: See TracBrowser for help on using the repository browser.