source: dcc.c @ 2c2df7d

Last change on this file since 2c2df7d was 2c2df7d, checked in by ulim <a.sporto+bee@…>, at 2007-11-28T21:07:30Z

Initial import of jabber file receive and DCC send support. This introduces
only a few changes to bitlbees code, mainly the addition of the "transfers"
command.

This is known to work with Kopete, Psi, and Pidgin (formerly known as gaim).
At least with Pidgin also over a proxy. DCC has only been tested with irssi.
IPV6 is untested but should work.

Currently, only receiving via SOCKS5BYTESREAMS is implemented. I'm not sure if
the alternative(in-band bytestreams IBB) is worth implementing since I didn't
see a client yet that can do it. Additionally, it is probably very slow and
needs support by the server as well.

  • Property mode set to 100644
File size: 14.1 KB
Line 
1/********************************************************************\
2* BitlBee -- An IRC to other IM-networks gateway                     *
3*                                                                    *
4* Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                   *
5\********************************************************************/
6
7/*
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License with
19  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
20  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21  Suite 330, Boston, MA  02111-1307  USA
22*/
23
24#define BITLBEE_CORE
25#include "bitlbee.h"
26#include "ft.h"
27#include "dcc.h"
28#include <poll.h>
29#include <netinet/tcp.h>
30
31/*
32 * Since that might be confusing a note on naming:
33 *
34 * Generic dcc functions start with
35 *
36 *      dcc_
37 *
38 * ,methods specific to DCC SEND start with
39 *
40 *      dccs_
41 *
42 * . Since we can be on both ends of a DCC SEND,
43 * functions specific to one end are called
44 *
45 *      dccs_send and dccs_recv
46 *
47 * ,respectively.
48 */
49
50
51/*
52 * used to generate a unique local transfer id the user
53 * can use to reject/cancel transfers
54 */
55unsigned int local_transfer_id=1;
56
57/*
58 * just for debugging the nr. of chunks we received from im-protocols and the total data
59 */
60unsigned int receivedchunks=0, receiveddata=0;
61
62/*
63 * If using DCC SEND AHEAD this value will be set before the first transfer starts.
64 * Not that in this case it degenerates to the maximum message size to send() and
65 * has nothing to do with packets.
66 */
67#ifdef DCC_SEND_AHEAD
68int max_packet_size = DCC_PACKET_SIZE;
69#else
70int max_packet_size = 0;
71#endif
72
73static void dcc_finish( file_transfer_t *file );
74static void dcc_close( file_transfer_t *file );
75gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond );
76gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr );
77int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
78
79/* As defined in ft.h */
80file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size )
81{
82        user_t *u = user_findhandle( ic, handle );
83        /* one could handle this more intelligent like imcb_buddy_msg.
84         * can't call it directly though cause it does some wrapping.
85         * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */
86        if (!u) return NULL;
87
88        return dccs_send_start( ic, u->nick, file_name, file_size );
89};
90
91/* As defined in ft.h */
92void imcb_file_canceled( file_transfer_t *file, char *reason )
93{
94        if( file->canceled )
95                file->canceled( file, reason );
96
97        dcc_close( file );
98}
99
100/* As defined in ft.h */
101gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size )
102{
103        return dccs_send_write( file, data, data_size );
104}
105
106/* This is where the sending magic starts... */
107file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size )
108{
109        file_transfer_t *file;
110        dcc_file_transfer_t *df;
111        struct sockaddr_storage **saddr;
112
113        if( file_size > global.conf->max_filetransfer_size )
114                return NULL;
115       
116        /* alloc stuff */
117        file = g_new0( file_transfer_t, 1 );
118        file->priv = df = g_new0( dcc_file_transfer_t, 1);
119        file->file_size = file_size;
120        file->file_name = g_strdup( file_name );
121        file->local_id = local_transfer_id++;
122        df->ic = ic;
123        df->ft = file;
124       
125        /* listen and request */
126        if( !dcc_listen( df, saddr ) ||
127            !dccs_send_request( df, user_nick, *saddr ) )
128                return NULL;
129
130        g_free( *saddr );
131
132        /* watch */
133        df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df );
134
135        df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file );
136
137        return file;
138}
139
140/* Used pretty much everywhere in the code to abort a transfer */
141gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... )
142{
143        file_transfer_t *file = df->ft;
144        va_list params;
145        va_start( params, reason );
146        char *msg = g_strdup_vprintf( reason, params );
147        va_end( params );
148       
149        file->status |= FT_STATUS_CANCELED;
150       
151        if( file->canceled )
152                file->canceled( file, msg );
153        else 
154                imcb_log( df->ic, "DCC transfer aborted: %s", msg );
155
156        g_free( msg );
157
158        dcc_close( df->ft );
159
160        return FALSE;
161}
162
163/* used extensively for socket operations */
164#define ASSERTSOCKOP(op, msg) \
165        if( (op) == -1 ) \
166                return dcc_abort( df , msg ": %s", strerror( errno ) );
167
168/* Creates the "DCC SEND" line and sends it to the server */
169int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr )
170{
171        char ipaddr[INET6_ADDRSTRLEN]; 
172        const void *netaddr;
173        int port;
174        char *cmd;
175
176        if( saddr->ss_family == AF_INET )
177        {
178                struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
179
180                /*
181                 * this is so ridiculous. We're supposed to convert the address to
182                 * host byte order!!! Let's exclude anyone running big endian just
183                 * for the fun of it...
184                 */
185                sprintf( ipaddr, "%d", 
186                         htonl( saddr_ipv4->sin_addr.s_addr ) );
187                port = saddr_ipv4->sin_port;
188        } else 
189        {
190                struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr;
191
192                netaddr = &saddr_ipv6->sin6_addr.s6_addr;
193                port = saddr_ipv6->sin6_port;
194
195                /*
196                 * Didn't find docs about this, but it seems that's the way irssi does it
197                 */
198                if( !inet_ntop( saddr->ss_family, netaddr, ipaddr, sizeof( ipaddr ) ) )
199                        return dcc_abort( df, "inet_ntop failed: %s", strerror( errno ) );
200        }
201
202        port = ntohs( port );
203
204        cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001",
205                                df->ft->file_name, ipaddr, port, df->ft->file_size );
206       
207        if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) )
208                return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick );
209
210        g_free( cmd );
211
212        /* message is sortof redundant cause the users client probably informs him about that. remove? */
213        imcb_log( df->ic, "Transferring file %s: Chose local address %s for DCC connection", df->ft->file_name, ipaddr );
214
215        return TRUE;
216}
217
218/*
219 * Creates a listening socket and returns it in saddr_ptr.
220 */
221gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr )
222{
223        file_transfer_t *file = df->ft;
224        struct sockaddr_storage *saddr;
225        int fd;
226        char hostname[ HOST_NAME_MAX + 1 ];
227        struct addrinfo hints, *rp;
228        socklen_t ssize = sizeof( struct sockaddr_storage );
229
230        /* won't be long till someone asks for this to be configurable :) */
231
232        ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" );
233
234        memset( &hints, 0, sizeof( struct addrinfo ) );
235        hints.ai_socktype = SOCK_STREAM;
236        hints.ai_flags = AI_NUMERICSERV;
237
238        if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 )
239                return dcc_abort( df, "getaddrinfo()" );
240
241        saddr = g_new( struct sockaddr_storage, 1 );
242
243        *saddr_ptr = saddr;
244
245        memcpy( saddr, rp->ai_addr, rp->ai_addrlen );
246
247        ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" );
248
249        ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" );
250       
251        freeaddrinfo( rp );
252
253        ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" );
254
255        ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" );
256
257        file->status = FT_STATUS_LISTENING;
258
259        return TRUE;
260}
261
262/*
263 * After setup, the transfer itself is handled entirely by this function.
264 * There are basically four things to handle: connect, receive, send, and error.
265 */
266gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond )
267{
268        dcc_file_transfer_t *df = data;
269        file_transfer_t *file = df->ft;
270        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
271        short revents;
272       
273        if ( poll( &pfd, 1, 0 ) == -1 )
274        {
275                imcb_log( df->ic, "poll() failed, weird!" );
276                revents = 0;
277        };
278
279        revents = pfd.revents;
280
281        if( revents & POLLERR )
282        {
283                int sockerror;
284                socklen_t errlen = sizeof( sockerror );
285
286                if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
287                        return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" );
288
289                return dcc_abort( df, "Socket error: %s", strerror( sockerror ) );
290        }
291       
292        if( revents & POLLHUP ) 
293                return dcc_abort( df, "Remote end closed connection" );
294       
295        if( ( revents & POLLIN ) &&
296            ( file->status & FT_STATUS_LISTENING ) )
297        {       
298                struct sockaddr *clt_addr;
299                socklen_t ssize = sizeof( clt_addr );
300
301                /* Connect */
302
303                ASSERTSOCKOP( df->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" );
304
305                closesocket( fd );
306                fd = df->fd;
307                file->status = FT_STATUS_TRANSFERING;
308                sock_make_nonblocking( fd );
309
310#ifdef DCC_SEND_AHEAD
311                /*
312                 * use twice the maximum segment size as a maximum for calls to send().
313                 */
314                if( max_packet_size == 0 )
315                {
316                        unsigned int mpslen = sizeof( max_packet_size );
317                        if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) )
318                                return dcc_abort( df, "getsockopt() failed" );
319                        max_packet_size *= 2;
320                }
321#endif
322                /* IM protocol callback */
323
324                if( file->accept )
325                        file->accept( file );
326                /* reschedule for reading on new fd */
327                df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df );
328
329                return FALSE;
330        }
331
332        if( revents & POLLIN ) 
333        {
334                int bytes_received;
335                int ret;
336               
337                ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received  ), MSG_PEEK ), "Receiving" );
338
339                if( ret == 0 )
340                        return dcc_abort( df, "Remote end closed connection" );
341                       
342                if( ret < 4 )
343                {
344                        imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" );
345                        return TRUE;
346                }
347
348                ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received  ), 0 ), "Receiving" );
349                if( ret != 4 )
350                        return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret );
351
352                bytes_received = ntohl( bytes_received );
353
354                /* If any of this is actually happening, the receiver should buy a new IRC client */
355
356                if ( bytes_received > df->bytes_sent )
357                        return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent );
358
359                if ( bytes_received < file->bytes_transferred )
360                        return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred );
361               
362                file->bytes_transferred = bytes_received;
363       
364                if( file->bytes_transferred >= file->file_size ) {
365                        dcc_finish( file );
366                        return FALSE;
367                }
368       
369#ifndef DCC_SEND_AHEAD
370                /* reschedule writer if neccessary */
371                if( file->bytes_transferred >= df->bytes_sent && 
372                    df->watch_out == 0 && 
373                    df->queued_bytes > 0 ) {
374                        df->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, dcc_send_proto, df );
375                }
376#endif
377                return TRUE;
378        }
379
380        if( revents & POLLOUT )
381        {
382                struct dcc_buffer *dccb;
383                int ret;
384                char *msg;
385
386                if( df->queued_bytes == 0 )
387                {
388                        /* shouldn't happen */
389                        imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" );
390
391                        df->watch_out = 0;
392                        return FALSE;
393                }
394
395                /* start where we left off */
396                if( !( df->queued_buffers ) ||
397                    !( dccb = df->queued_buffers->data ) )
398                        return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" );
399
400                msg = dccb->b + df->buffer_pos;
401
402                int msgsize = MIN( 
403#ifndef DCC_SEND_AHEAD
404                                  file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent,
405#else
406                                  max_packet_size,
407#endif
408                                  dccb->len - df->buffer_pos );
409
410                if ( msgsize == 0 )
411                {
412                        df->watch_out = 0;
413                        return FALSE;
414                }
415
416                ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" );
417
418                if( ret == 0 )
419                        return dcc_abort( df, "Remote end closed connection" );
420
421                df->bytes_sent += ret;
422                df->queued_bytes -= ret;
423                df->buffer_pos += ret;
424
425                if( df->buffer_pos == dccb->len )
426                {
427                        df->buffer_pos = 0;
428                        df->queued_buffers = g_slist_remove( df->queued_buffers, dccb );
429                        g_free( dccb->b );
430                        g_free( dccb );
431                }
432
433                if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data )
434                        file->out_of_data( file );
435       
436                if( df->queued_bytes > 0 )
437                {
438                        /* Who knows how long the event loop cycle will take,
439                         * let's just try to send some more now. */
440#ifndef DCC_SEND_AHEAD
441                        if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) )
442#endif
443                                return dccs_send_proto( df, fd, cond );
444                }
445
446                df->watch_out = 0;
447                return FALSE;
448        }
449
450        /* Send buffer full, come back later */
451
452        return TRUE;
453}
454
455/*
456 * Incoming data. Note that the buffer MUST NOT be freed by the caller!
457 * We don't copy the buffer but put it in our queue.
458 *
459 * */
460gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size )
461{
462        dcc_file_transfer_t *df = file->priv;
463        struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 );
464
465        receivedchunks++; receiveddata += data_size;
466
467        dccb->b = data;
468        dccb->len = data_size;
469
470        df->queued_buffers = g_slist_append( df->queued_buffers, dccb );
471
472        df->queued_bytes += data_size;
473
474        if( ( file->status & FT_STATUS_TRANSFERING ) && 
475#ifndef DCC_SEND_AHEAD
476            ( file->bytes_transferred >= df->bytes_sent ) && 
477#endif
478            ( df->watch_out == 0 ) && 
479            ( df->queued_bytes > 0 ) )
480        {
481                df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df );
482        }
483       
484        return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH;
485}
486
487/*
488 * Cleans up after a transfer.
489 */
490static void dcc_close( file_transfer_t *file )
491{
492        dcc_file_transfer_t *df = file->priv;
493
494        if( file->free )
495                file->free( file );
496       
497        closesocket( df->fd );
498
499        if( df->watch_in )
500                b_event_remove( df->watch_in );
501
502        if( df->watch_out )
503                b_event_remove( df->watch_out );
504       
505        if( df->queued_buffers )
506        {
507                struct dcc_buffer *dccb;
508                GSList *gslist = df->queued_buffers;
509
510                for( ; gslist ; gslist = g_slist_next( gslist ) )
511                {
512                        dccb = gslist->data;
513                        g_free( dccb->b );
514                        g_free( dccb );
515                }
516                g_slist_free( df->queued_buffers );
517        }
518
519        df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file );
520       
521        g_free( df );
522        g_free( file->file_name );
523        g_free( file );
524}
525
526void dcc_finish( file_transfer_t *file )
527{
528        file->status |= FT_STATUS_FINISHED;
529       
530        if( file->finished )
531                file->finished( file );
532
533        dcc_close( file );
534}
Note: See TracBrowser for help on using the repository browser.