source: branches/pex/libtransmission/peermessages.h @ 1540

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

Implement Azureus peer protocol, including PEX message.
Implement extended messages, including uTorrent PEX.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.1 KB
Line 
1/******************************************************************************
2 * $Id: peermessages.h 1540 2007-03-08 04:06:58Z joshe $
3 *
4 * Copyright (c) 2005-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/***********************************************************************
26 * This file handles all outgoing messages
27 **********************************************************************/
28
29#define PEER_MSG_CHOKE          0
30#define PEER_MSG_UNCHOKE        1
31#define PEER_MSG_INTERESTED     2
32#define PEER_MSG_UNINTERESTED   3
33#define PEER_MSG_HAVE           4
34#define PEER_MSG_BITFIELD       5
35#define PEER_MSG_REQUEST        6
36#define PEER_MSG_PIECE          7
37#define PEER_MSG_CANCEL         8
38#define PEER_MSG_PORT           9
39
40static uint8_t * messagesPending( tr_peer_t * peer, int * size )
41{
42    if( peer->outBlockSending || peer->outMessagesPos < 1 )
43    {
44        return NULL;
45    }
46
47    *size = MIN( peer->outMessagesPos, 1024 );
48
49    return peer->outMessages;
50}
51
52static void messagesSent( tr_peer_t * peer, int size )
53{
54    peer->outMessagesPos -= size;
55    memmove( peer->outMessages, &peer->outMessages[size],
56             peer->outMessagesPos );
57}
58
59static uint8_t * blockPending( tr_torrent_t * tor, tr_peer_t * peer,
60                               int * size )
61{
62    if( !peer->outBlockLoaded )
63    {
64        uint8_t * p;
65        tr_request_t * r;
66
67        if( peer->amChoking || peer->outRequestCount < 1 )
68        {
69            /* No piece to send */
70            return NULL;
71        }
72
73        /* We need to load the block for the next request */
74        r = &peer->outRequests[0];
75
76        /* Sanity check */
77        if( !tr_cpPieceIsComplete( tor->completion, r->index ) )
78        {
79            /* We have been asked for something we don't have, buggy client?
80               Let's just drop this request */
81            tr_inf( "Block %d/%d/%d was requested but we don't have it",
82                    r->index, r->begin, r->length );
83            (peer->outRequestCount)--;
84            memmove( &peer->outRequests[0], &peer->outRequests[1],
85                     peer->outRequestCount * sizeof( tr_request_t ) );
86            return NULL;
87        }
88       
89        p = (uint8_t *) peer->outBlock;
90
91        TR_HTONL( 9 + r->length, p );
92        p[4] = PEER_MSG_PIECE;
93        TR_HTONL( r->index, p + 5 );
94        TR_HTONL( r->begin, p + 9 );
95
96        tr_ioRead( tor->io, r->index, r->begin, r->length, &p[13] );
97
98        if( peer->outRequestCount < 1 )
99        {
100            /* We were choked during the read */
101            return NULL;
102        }
103
104        peer_dbg( "SEND piece %d/%d (%d bytes)",
105                  r->index, r->begin, r->length );
106
107        peer->outBlockSize   = 13 + r->length;
108        peer->outBlockLoaded = 1;
109
110        (peer->outRequestCount)--;
111        memmove( &peer->outRequests[0], &peer->outRequests[1],
112                 peer->outRequestCount * sizeof( tr_request_t ) );
113    }
114
115    *size = MIN( 1024, peer->outBlockSize );
116
117    return (uint8_t *) peer->outBlock;
118}
119
120static void blockSent( tr_peer_t * peer, int size )
121{
122    peer->outBlockSize -= size;
123    memmove( peer->outBlock, &peer->outBlock[size], peer->outBlockSize );
124
125    if( peer->outBlockSize > 0 )
126    {
127        /* We can't send messages until we are done sending the block */
128        peer->outBlockSending = 1;
129    }
130    else
131    {
132        /* Block fully sent */
133        peer->outBlockSending = 0;
134        peer->outBlockLoaded  = 0;
135    }
136}
137
138static uint8_t * getMessagePointer( tr_peer_t * peer, int size, int id )
139{
140    uint8_t * p;
141    int       index = 0;
142
143    size += 4;
144    if( peer->azproto )
145    {
146        index = azmsgIdIndex( id );
147        assert( 0 <= index );
148        size += 4 + azmsgLen( index ) + 1;
149    }
150    else if( 0 <= id )
151    {
152        size++;
153    }
154
155    if( peer->outMessagesPos + size > peer->outMessagesSize )
156    {
157        peer->outMessagesSize = peer->outMessagesPos + size;
158        peer->outMessages     = realloc( peer->outMessages,
159                                         peer->outMessagesSize );
160    }
161
162    p                     = &peer->outMessages[peer->outMessagesPos];
163    peer->outMessagesPos += size;
164
165    TR_HTONL( size - 4, p );
166    p += 4;
167    if( peer->azproto )
168    {
169        TR_HTONL( azmsgLen( index ), p );
170        memcpy( p + 4, azmsgStr( index ), azmsgLen( index ) );
171        p[ 4 + azmsgLen( index ) ] = AZ_EXT_VERSION;
172        p += 4 + azmsgLen( index ) + 1;
173    }
174    else if( 0 <= id )
175    {
176        *p = id;
177        p++;
178    }
179
180    return p;
181}
182
183/***********************************************************************
184 * sendKeepAlive
185 ***********************************************************************
186 *
187 **********************************************************************/
188static void sendKeepAlive( tr_peer_t * peer )
189{
190    uint8_t * p;
191
192    p = getMessagePointer( peer, 0, AZ_MSG_BT_KEEP_ALIVE );
193
194    peer_dbg( "SEND keep-alive" );
195}
196
197
198/***********************************************************************
199 * sendChoke
200 ***********************************************************************
201 *
202 **********************************************************************/
203static void sendChoke( tr_peer_t * peer, int yes )
204{
205    uint8_t * p;
206    int       id;
207
208    id = ( yes ? PEER_MSG_CHOKE : PEER_MSG_UNCHOKE );
209    p = getMessagePointer( peer, 0, id );
210
211    peer->amChoking = yes;
212
213    if( !yes )
214    {
215        /* Drop older requests from the last time it was unchoked,
216           if any */
217        peer->outRequestCount = 0;
218    }
219
220    peer_dbg( "SEND %schoke", yes ? "" : "un" );
221}
222
223/***********************************************************************
224 * sendInterest
225 ***********************************************************************
226 *
227 **********************************************************************/
228static void sendInterest( tr_peer_t * peer, int yes )
229{
230    uint8_t * p;
231    int       id;
232
233    id = ( yes ? PEER_MSG_INTERESTED : PEER_MSG_UNINTERESTED );
234    p = getMessagePointer( peer, 0, id );
235
236    peer->amInterested = yes;
237
238    peer_dbg( "SEND %sinterested", yes ? "" : "un" );
239}
240
241/***********************************************************************
242 * sendHave
243 ***********************************************************************
244 *
245 **********************************************************************/
246static void sendHave( tr_peer_t * peer, int piece )
247{
248    uint8_t * p;
249
250    p = getMessagePointer( peer, 4, PEER_MSG_HAVE );
251
252    TR_HTONL( piece, p );
253
254    peer_dbg( "SEND have %d", piece );
255}
256
257/***********************************************************************
258 * sendBitfield
259 ***********************************************************************
260 * Builds a 'bitfield' message:
261 *  - size = 5 + X (4 bytes)
262 *  - id   = 5     (1 byte)
263 *  - bitfield     (X bytes)
264 **********************************************************************/
265static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer )
266{
267    uint8_t * p;
268    int       bitfieldSize = ( tor->info.pieceCount + 7 ) / 8;
269
270    p = getMessagePointer( peer, bitfieldSize, PEER_MSG_BITFIELD );
271
272    memcpy( p, tr_cpPieceBitfield( tor->completion ), bitfieldSize );
273
274    peer_dbg( "SEND bitfield" );
275}
276
277/***********************************************************************
278 * sendRequest
279 ***********************************************************************
280 *
281 **********************************************************************/
282static void sendRequest( tr_torrent_t * tor, tr_peer_t * peer, int block )
283{
284    tr_info_t * inf = &tor->info;
285    tr_request_t * r;
286    uint8_t * p;
287
288    /* Get the piece the block is a part of, its position in the piece
289       and its size */
290    r         = &peer->inRequests[peer->inRequestCount];
291    r->index  = block / ( inf->pieceSize / tor->blockSize );
292    r->begin  = ( block % ( inf->pieceSize / tor->blockSize ) ) *
293                    tor->blockSize;
294    r->length = tr_blockSize( block );
295    (peer->inRequestCount)++;
296
297    /* Build the "ask" message */
298    p = getMessagePointer( peer, 12, PEER_MSG_REQUEST );
299
300    TR_HTONL( r->index,  p     );
301    TR_HTONL( r->begin,  p + 4 );
302    TR_HTONL( r->length, p + 8 );
303
304    tr_cpDownloaderAdd( tor->completion, block );
305
306    peer_dbg( "SEND request %d/%d (%d bytes)",
307              r->index, r->begin, r->length );
308}
309
310/***********************************************************************
311 * sendCancel
312 ***********************************************************************
313 *
314 **********************************************************************/
315static void sendCancel( tr_torrent_t * tor, int block )
316{
317    int i, j;
318    uint8_t * p;
319    tr_peer_t * peer;
320    tr_request_t * r;
321
322    for( i = 0; i < tor->peerCount; i++ )
323    {
324        peer = tor->peers[i];
325
326        for( j = 1; j < peer->inRequestCount; j++ )
327        {
328            r = &peer->inRequests[j];
329
330            if( block != tr_block( r->index, r->begin ) )
331            {
332                continue;
333            }
334
335            p = getMessagePointer( peer, 12, PEER_MSG_CANCEL );
336       
337            /* Build the "cancel" message */
338            TR_HTONL( r->index,  p     );
339            TR_HTONL( r->begin,  p + 4 );
340            TR_HTONL( r->length, p + 8 );
341
342            peer_dbg( "SEND cancel %d/%d (%d bytes)",
343                      r->index, r->begin, r->length );
344
345            (peer->inRequestCount)--;
346            memmove( &peer->inRequests[j], &peer->inRequests[j+1],
347                     ( peer->inRequestCount - j ) * sizeof( tr_request_t ) );
348            break;
349        }
350    }
351}
352
353/***********************************************************************
354 * sendExtended
355 ***********************************************************************
356 * Builds an extended message:
357 *  - size = 6 + X (4 bytes)
358 *  - id   = 20    (1 byte)
359 *  - eid  = Y     (1 byte)
360 *  - data         (X bytes)
361 **********************************************************************/
362static int sendExtended( tr_torrent_t * tor, tr_peer_t * peer, int id )
363{
364    uint8_t * p;
365    char    * buf;
366    int       len;
367
368    buf = NULL;
369    switch( id )
370    {
371        case EXTENDED_HANDSHAKE_ID:
372            buf = makeExtendedHandshake( tor, peer, &len );
373            break;
374        case EXTENDED_PEX_ID:
375            buf = makeUTPex( tor, peer, &len );
376            break;
377        default:
378            assert( 0 );
379            break;
380    }
381    if( NULL == buf )
382    {
383        return 1;
384    }
385
386    /* add header and queue it to be sent */
387    p = getMessagePointer( peer, 1 + len, PEER_MSG_EXTENDED );
388    p[0] = id;
389    memcpy( p + 1, buf, len );
390    free( buf );
391
392    return 0;
393}
394
395/***********************************************************************
396 * sendAZPex
397 ***********************************************************************
398 *
399 **********************************************************************/
400static int sendAZPex( tr_torrent_t * tor, tr_peer_t * peer )
401{
402    uint8_t * p;
403    char    * buf;
404    int       len;
405
406    buf = makeAZPex( tor, peer, &len );
407    if( NULL == buf )
408    {
409        return 1;
410    }
411
412    /* add header and queue it to be sent */
413    p = getMessagePointer( peer, len, AZ_MSG_AZ_PEER_EXCHANGE );
414    memcpy( p, buf, len );
415    free( buf );
416
417    return 0;
418}
Note: See TracBrowser for help on using the repository browser.