source: branches/daemon/libtransmission/peermessages.h @ 1712

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

Merge libT revs 1616:1711 from trunk to daemon branch.

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