source: trunk/libtransmission/peeraz.h @ 1595

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

Revert unintended changes.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 KB
Line 
1/******************************************************************************
2 * $Id: peeraz.h 1595 2007-03-26 19:21:25Z 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    assert( !peer->private );
238    tr_bencInitStr( &val, tor->info.hash, sizeof( tor->info.hash ), 1 );
239    buf = makeCommonPex( tor, peer, len, peertreeToBencAZ, "infohash", &val );
240
241    return buf;
242}
243
244static int
245sendAZHandshake( tr_torrent_t * tor, tr_peer_t * peer )
246{
247    uint8_t * buf;
248    int len;
249
250    /* XXX this is kind of evil to use this buffer like this */
251    if( NULL == peer->outMessages )
252    {
253        buf = makeAZHandshake( tor, peer, &len );
254        if( NULL == buf )
255        {
256            return TR_NET_CLOSE;
257        }
258        peer->outMessages     = buf;
259        peer->outMessagesSize = len;
260        peer->outMessagesPos  = 0;
261    }
262
263    len = tr_netSend( peer->socket, peer->outMessages + peer->outMessagesPos,
264                      peer->outMessagesSize - peer->outMessagesPos );
265    if( peer->outMessagesPos + len < peer->outMessagesSize )
266    {
267        peer->outMessagesPos += len;
268        return TR_NET_BLOCK;
269    }
270
271    len = peer->outMessagesSize;
272    free( peer->outMessages );
273    peer->outMessages     = NULL;
274    peer->outMessagesSize = 0;
275    peer->outMessagesPos  = 0;
276
277    return len;
278}
279
280static inline int
281parseAZMessageHeader( tr_peer_t * peer, uint8_t * buf, int len,
282                      int * msgidret, int * msglenret )
283{
284    uint8_t * name, vers;
285    int       off, namelen, msglen, index, msgid;
286
287    if( 8 > len )
288    {
289        return TR_NET_BLOCK;
290    }
291    /* message length */
292    TR_NTOHL( buf, msglen );
293    msglen += 4;
294    off = 4;
295    if( msglen > len )
296    {
297        return TR_NET_BLOCK;
298    }
299    if( 9 > msglen )
300    {
301        peer_dbg( "Azureus peer message is too short to make sense" );
302        return TR_NET_CLOSE;
303    }
304    /* name length */
305    TR_NTOHL( buf + off, namelen );
306    off += 4;
307    if( off + namelen + 1 > msglen )
308    {
309        peer_dbg( "Azureus peer message name is too long to make sense" );
310        return TR_NET_CLOSE;
311    }
312    /* message name */
313    name = buf + off;
314    off += namelen;
315    /* message version */
316    vers = buf[off];
317    off++;
318    /* get payload length from message length */
319    msglen -= off;
320
321    index = azmsgNameIndex( ( char * )name, namelen );
322    if( AZ_EXT_VERSION != vers )
323    {
324        /* XXX should we close the connection here? */
325        peer_dbg( "unsupported Azureus message version %hhu", vers );
326        msgid = AZ_MSG_INVALID;
327    }
328    else if( 0 > index )
329    {
330        name[namelen] = '\0';
331        peer_dbg( "got unknown Azureus message: \"%s\"", name );
332        name[namelen] = vers;
333        msgid = AZ_MSG_INVALID;
334    }
335    else
336    {
337        msgid = azmsgId( index );
338    }
339
340    *msgidret  = msgid;
341    *msglenret = msglen;
342
343    return off;
344}
345
346static inline int
347parseAZHandshake( tr_peer_t * peer, uint8_t * buf, int len )
348{
349    benc_val_t      val, * sub, * dict, * subsub;
350    tr_bitfield_t * msgs;
351    int             ii, idx;
352
353    if( tr_bencLoad( buf, len, &val, NULL ) )
354    {
355        peer_dbg( "invalid bencoding in Azureus handshake" );
356        return TR_ERROR;
357    }
358
359    if( TYPE_DICT != val.type )
360    {
361        peer_dbg( "Azureus handshake is not a dictionary" );
362        tr_bencFree( &val );
363        return TR_ERROR;
364    }
365
366    /* get the peer's listening port */
367    sub = tr_bencDictFind( &val, "tcp_port" );
368    if( NULL != sub )
369    {
370        if( TYPE_INT == sub->type && 0x0 < sub->val.i && 0xffff >= sub->val.i )
371        {
372            peer->port = htons( sub->val.i );
373        }
374    }
375
376    /* find the supported message list */
377    sub = tr_bencDictFind( &val, "messages" );
378    if( NULL == sub && TYPE_LIST != sub->type )
379    {
380        tr_bencFree( &val );
381        peer_dbg( "Azureus handshake is missing 'messages'" );
382        return TR_ERROR;
383    }
384
385    /* fill bitmask with supported message info */
386    msgs = tr_bitfieldNew( azmsgCount() );
387    for( ii = 0; ii < sub->val.l.count; ii++ )
388    {
389        dict = &sub->val.l.vals[ii];
390        if( TYPE_DICT != dict->type )
391        {
392            continue;
393        }
394        subsub = tr_bencDictFind( dict, "id" );
395        if( NULL == subsub || TYPE_STR != subsub->type )
396        {
397            continue;
398        }
399        idx = azmsgNameIndex( subsub->val.s.s, subsub->val.s.i );
400        if( 0 > idx )
401        {
402            continue;
403        }
404        subsub = tr_bencDictFind( dict, "ver" );
405        if( NULL == subsub || TYPE_STR != subsub->type ||
406            1 != subsub->val.s.i || AZ_EXT_VERSION != subsub->val.s.s[0] )
407        {
408            continue;
409        }
410        tr_bitfieldAdd( msgs, idx );
411    }
412
413    tr_bencFree( &val );
414
415    /* check bitmask to see if we're missing any messages */
416    for( ii = 0; azmsgCount() > ii; ii++ )
417    {
418        if( AZ_MSG_AZ_PEER_EXCHANGE == azmsgId( ii ) )
419        {
420            peer->pexStatus = tr_bitfieldHas( msgs, ii );
421        }
422        if( !AZ_MSG_IS_OPTIONAL( azmsgId( ii ) ) &&
423            !tr_bitfieldHas( msgs, ii ) )
424        {
425            peer_dbg( "Azureus message %s not supported", azmsgStr( ii ) );
426            tr_bitfieldFree( msgs );
427            return TR_ERROR;
428        }
429    }
430    tr_bitfieldFree( msgs );
431
432    return TR_OK;
433}
434
435static inline int
436parseAZPex( tr_torrent_t * tor, tr_peer_t * peer, uint8_t * buf, int len )
437{
438    tr_info_t * info = &tor->info;
439    benc_val_t  val, * list, * pair;
440    int         ii;
441
442    if( peer->private || PEX_PEER_CUTOFF <= tor->peerCount )
443    {
444        return TR_OK;
445    }
446
447    if( tr_bencLoad( buf, len, &val, NULL ) )
448    {
449        peer_dbg( "invalid bencoding in Azureus peer exchange" );
450        return TR_ERROR;
451    }
452    if( TYPE_DICT != val.type )
453    {
454        tr_bencFree( &val );
455        peer_dbg( "Azureus peer exchange is not a dictionary" );
456        return TR_ERROR;
457    }
458
459    list = tr_bencDictFind( &val, "infohash" );
460    if( NULL == list || TYPE_STR != list->type ||
461        sizeof( info->hash ) != list->val.s.i ||
462        0 != memcmp( info->hash, list->val.s.s, sizeof( info->hash ) ) )
463    {
464        tr_bencFree( &val );
465        peer_dbg( "Azureus peer exchange has missing or invalid 'infohash'" );
466        return TR_ERROR;
467    }
468
469    list = tr_bencDictFind( &val, "added" );
470    if( NULL == list || TYPE_LIST != list->type )
471    {
472        tr_bencFree( &val );
473        return TR_OK;
474    }
475
476    for( ii = 0; ii < list->val.l.count; ii++ )
477    {
478        pair = &list->val.l.vals[ii];
479        if( TYPE_STR == pair->type && 6 == pair->val.s.i )
480        {
481            tr_torrentAddCompact( tor, TR_PEER_FROM_PEX,
482                                  ( uint8_t * )pair->val.s.s, 1 );
483        }
484    }
485
486    tr_bencFree( &val );
487
488    return TR_OK;
489}
Note: See TracBrowser for help on using the repository browser.