From 2c2df7dd91930345a9b22a8bb61327d1dcc7e3d5 Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 28 Nov 2007 22:07:30 +0100 Subject: 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. --- dcc.c | 534 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 dcc.c (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c new file mode 100644 index 00000000..73fe0180 --- /dev/null +++ b/dcc.c @@ -0,0 +1,534 @@ +/********************************************************************\ +* BitlBee -- An IRC to other IM-networks gateway * +* * +* Copyright 2007 Uli Meis * +\********************************************************************/ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "ft.h" +#include "dcc.h" +#include +#include + +/* + * Since that might be confusing a note on naming: + * + * Generic dcc functions start with + * + * dcc_ + * + * ,methods specific to DCC SEND start with + * + * dccs_ + * + * . Since we can be on both ends of a DCC SEND, + * functions specific to one end are called + * + * dccs_send and dccs_recv + * + * ,respectively. + */ + + +/* + * used to generate a unique local transfer id the user + * can use to reject/cancel transfers + */ +unsigned int local_transfer_id=1; + +/* + * just for debugging the nr. of chunks we received from im-protocols and the total data + */ +unsigned int receivedchunks=0, receiveddata=0; + +/* + * If using DCC SEND AHEAD this value will be set before the first transfer starts. + * Not that in this case it degenerates to the maximum message size to send() and + * has nothing to do with packets. + */ +#ifdef DCC_SEND_AHEAD +int max_packet_size = DCC_PACKET_SIZE; +#else +int max_packet_size = 0; +#endif + +static void dcc_finish( file_transfer_t *file ); +static void dcc_close( file_transfer_t *file ); +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); +gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); +int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); + +/* As defined in ft.h */ +file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) +{ + user_t *u = user_findhandle( ic, handle ); + /* one could handle this more intelligent like imcb_buddy_msg. + * can't call it directly though cause it does some wrapping. + * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */ + if (!u) return NULL; + + return dccs_send_start( ic, u->nick, file_name, file_size ); +}; + +/* As defined in ft.h */ +void imcb_file_canceled( file_transfer_t *file, char *reason ) +{ + if( file->canceled ) + file->canceled( file, reason ); + + dcc_close( file ); +} + +/* As defined in ft.h */ +gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ) +{ + return dccs_send_write( file, data, data_size ); +} + +/* This is where the sending magic starts... */ +file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ) +{ + file_transfer_t *file; + dcc_file_transfer_t *df; + struct sockaddr_storage **saddr; + + if( file_size > global.conf->max_filetransfer_size ) + return NULL; + + /* alloc stuff */ + file = g_new0( file_transfer_t, 1 ); + file->priv = df = g_new0( dcc_file_transfer_t, 1); + file->file_size = file_size; + file->file_name = g_strdup( file_name ); + file->local_id = local_transfer_id++; + df->ic = ic; + df->ft = file; + + /* listen and request */ + if( !dcc_listen( df, saddr ) || + !dccs_send_request( df, user_nick, *saddr ) ) + return NULL; + + g_free( *saddr ); + + /* watch */ + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); + + df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); + + return file; +} + +/* Used pretty much everywhere in the code to abort a transfer */ +gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) +{ + file_transfer_t *file = df->ft; + va_list params; + va_start( params, reason ); + char *msg = g_strdup_vprintf( reason, params ); + va_end( params ); + + file->status |= FT_STATUS_CANCELED; + + if( file->canceled ) + file->canceled( file, msg ); + else + imcb_log( df->ic, "DCC transfer aborted: %s", msg ); + + g_free( msg ); + + dcc_close( df->ft ); + + return FALSE; +} + +/* used extensively for socket operations */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return dcc_abort( df , msg ": %s", strerror( errno ) ); + +/* Creates the "DCC SEND" line and sends it to the server */ +int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ) +{ + char ipaddr[INET6_ADDRSTRLEN]; + const void *netaddr; + int port; + char *cmd; + + if( saddr->ss_family == AF_INET ) + { + struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr; + + /* + * this is so ridiculous. We're supposed to convert the address to + * host byte order!!! Let's exclude anyone running big endian just + * for the fun of it... + */ + sprintf( ipaddr, "%d", + htonl( saddr_ipv4->sin_addr.s_addr ) ); + port = saddr_ipv4->sin_port; + } else + { + struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr; + + netaddr = &saddr_ipv6->sin6_addr.s6_addr; + port = saddr_ipv6->sin6_port; + + /* + * Didn't find docs about this, but it seems that's the way irssi does it + */ + if( !inet_ntop( saddr->ss_family, netaddr, ipaddr, sizeof( ipaddr ) ) ) + return dcc_abort( df, "inet_ntop failed: %s", strerror( errno ) ); + } + + port = ntohs( port ); + + cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001", + df->ft->file_name, ipaddr, port, df->ft->file_size ); + + if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) ) + return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick ); + + g_free( cmd ); + + /* message is sortof redundant cause the users client probably informs him about that. remove? */ + imcb_log( df->ic, "Transferring file %s: Chose local address %s for DCC connection", df->ft->file_name, ipaddr ); + + return TRUE; +} + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ) +{ + file_transfer_t *file = df->ft; + struct sockaddr_storage *saddr; + int fd; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) + return dcc_abort( df, "getaddrinfo()" ); + + saddr = g_new( struct sockaddr_storage, 1 ); + + *saddr_ptr = saddr; + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + file->status = FT_STATUS_LISTENING; + + return TRUE; +} + +/* + * After setup, the transfer itself is handled entirely by this function. + * There are basically four things to handle: connect, receive, send, and error. + */ +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *file = df->ft; + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; + short revents; + + if ( poll( &pfd, 1, 0 ) == -1 ) + { + imcb_log( df->ic, "poll() failed, weird!" ); + revents = 0; + }; + + revents = pfd.revents; + + if( revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" ); + + return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); + } + + if( revents & POLLHUP ) + return dcc_abort( df, "Remote end closed connection" ); + + if( ( revents & POLLIN ) && + ( file->status & FT_STATUS_LISTENING ) ) + { + struct sockaddr *clt_addr; + socklen_t ssize = sizeof( clt_addr ); + + /* Connect */ + + ASSERTSOCKOP( df->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + + closesocket( fd ); + fd = df->fd; + file->status = FT_STATUS_TRANSFERING; + sock_make_nonblocking( fd ); + +#ifdef DCC_SEND_AHEAD + /* + * use twice the maximum segment size as a maximum for calls to send(). + */ + if( max_packet_size == 0 ) + { + unsigned int mpslen = sizeof( max_packet_size ); + if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) + return dcc_abort( df, "getsockopt() failed" ); + max_packet_size *= 2; + } +#endif + /* IM protocol callback */ + + if( file->accept ) + file->accept( file ); + /* reschedule for reading on new fd */ + df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); + + return FALSE; + } + + if( revents & POLLIN ) + { + int bytes_received; + int ret; + + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + if( ret < 4 ) + { + imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" ); + return TRUE; + } + + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); + if( ret != 4 ) + return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); + + bytes_received = ntohl( bytes_received ); + + /* If any of this is actually happening, the receiver should buy a new IRC client */ + + if ( bytes_received > df->bytes_sent ) + return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent ); + + if ( bytes_received < file->bytes_transferred ) + return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred ); + + file->bytes_transferred = bytes_received; + + if( file->bytes_transferred >= file->file_size ) { + dcc_finish( file ); + return FALSE; + } + +#ifndef DCC_SEND_AHEAD + /* reschedule writer if neccessary */ + if( file->bytes_transferred >= df->bytes_sent && + df->watch_out == 0 && + df->queued_bytes > 0 ) { + df->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, dcc_send_proto, df ); + } +#endif + return TRUE; + } + + if( revents & POLLOUT ) + { + struct dcc_buffer *dccb; + int ret; + char *msg; + + if( df->queued_bytes == 0 ) + { + /* shouldn't happen */ + imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" ); + + df->watch_out = 0; + return FALSE; + } + + /* start where we left off */ + if( !( df->queued_buffers ) || + !( dccb = df->queued_buffers->data ) ) + return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" ); + + msg = dccb->b + df->buffer_pos; + + int msgsize = MIN( +#ifndef DCC_SEND_AHEAD + file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent, +#else + max_packet_size, +#endif + dccb->len - df->buffer_pos ); + + if ( msgsize == 0 ) + { + df->watch_out = 0; + return FALSE; + } + + ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + df->bytes_sent += ret; + df->queued_bytes -= ret; + df->buffer_pos += ret; + + if( df->buffer_pos == dccb->len ) + { + df->buffer_pos = 0; + df->queued_buffers = g_slist_remove( df->queued_buffers, dccb ); + g_free( dccb->b ); + g_free( dccb ); + } + + if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data ) + file->out_of_data( file ); + + if( df->queued_bytes > 0 ) + { + /* Who knows how long the event loop cycle will take, + * let's just try to send some more now. */ +#ifndef DCC_SEND_AHEAD + if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) ) +#endif + return dccs_send_proto( df, fd, cond ); + } + + df->watch_out = 0; + return FALSE; + } + + /* Send buffer full, come back later */ + + return TRUE; +} + +/* + * Incoming data. Note that the buffer MUST NOT be freed by the caller! + * We don't copy the buffer but put it in our queue. + * + * */ +gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ) +{ + dcc_file_transfer_t *df = file->priv; + struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 ); + + receivedchunks++; receiveddata += data_size; + + dccb->b = data; + dccb->len = data_size; + + df->queued_buffers = g_slist_append( df->queued_buffers, dccb ); + + df->queued_bytes += data_size; + + if( ( file->status & FT_STATUS_TRANSFERING ) && +#ifndef DCC_SEND_AHEAD + ( file->bytes_transferred >= df->bytes_sent ) && +#endif + ( df->watch_out == 0 ) && + ( df->queued_bytes > 0 ) ) + { + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df ); + } + + return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH; +} + +/* + * Cleans up after a transfer. + */ +static void dcc_close( file_transfer_t *file ) +{ + dcc_file_transfer_t *df = file->priv; + + if( file->free ) + file->free( file ); + + closesocket( df->fd ); + + if( df->watch_in ) + b_event_remove( df->watch_in ); + + if( df->watch_out ) + b_event_remove( df->watch_out ); + + if( df->queued_buffers ) + { + struct dcc_buffer *dccb; + GSList *gslist = df->queued_buffers; + + for( ; gslist ; gslist = g_slist_next( gslist ) ) + { + dccb = gslist->data; + g_free( dccb->b ); + g_free( dccb ); + } + g_slist_free( df->queued_buffers ); + } + + df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); + + g_free( df ); + g_free( file->file_name ); + g_free( file ); +} + +void dcc_finish( file_transfer_t *file ) +{ + file->status |= FT_STATUS_FINISHED; + + if( file->finished ) + file->finished( file ); + + dcc_close( file ); +} -- cgit v1.2.3 From 2ff20765990c756533957e8da9c7c29dd3102e79 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 3 Dec 2007 15:28:45 +0100 Subject: Intermediate commit. Sending seems to work. TODOs: * move from out_of_data to is_writable, eliminate buffers * implement "transfers reject [id]" * documentation in commands.xml * implement throughput and cummulative throughput boundaries * feature discovery before sending * implement sending over a proxy (proxy discovery, socks5 client handshake for sending, activate message) * integrate toxik-mek-ft --- dcc.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 270 insertions(+), 42 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 73fe0180..a5c77ce8 100644 --- a/dcc.c +++ b/dcc.c @@ -27,6 +27,7 @@ #include "dcc.h" #include #include +#include /* * Since that might be confusing a note on naming: @@ -75,6 +76,9 @@ static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); +gboolean dccs_recv_start( file_transfer_t *ft ); +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); +void dccs_recv_out_of_data( file_transfer_t *ft ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -103,31 +107,44 @@ gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size return dccs_send_write( file, data, data_size ); } +/* As defined in ft.h */ +gboolean imcb_file_recv_start( file_transfer_t *ft ) +{ + return dccs_recv_start( ft ); +} + +dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) +{ + file_transfer_t *file = g_new0( file_transfer_t, 1 ); + dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1); + file->file_size = file_size; + file->file_name = g_strdup( file_name ); + file->local_id = local_transfer_id++; + df->ic = ic; + df->ft = file; + + return df; +} + /* This is where the sending magic starts... */ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size ) { file_transfer_t *file; dcc_file_transfer_t *df; - struct sockaddr_storage **saddr; + struct sockaddr_storage *saddr; if( file_size > global.conf->max_filetransfer_size ) return NULL; - /* alloc stuff */ - file = g_new0( file_transfer_t, 1 ); - file->priv = df = g_new0( dcc_file_transfer_t, 1); - file->file_size = file_size; - file->file_name = g_strdup( file_name ); - file->local_id = local_transfer_id++; - df->ic = ic; - df->ft = file; - + df = dcc_alloc_transfer( file_name, file_size, ic ); + file = df->ft; + /* listen and request */ - if( !dcc_listen( df, saddr ) || - !dccs_send_request( df, user_nick, *saddr ) ) + if( !dcc_listen( df, &saddr ) || + !dccs_send_request( df, user_nick, saddr ) ) return NULL; - g_free( *saddr ); + g_free( saddr ); /* watch */ df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -260,25 +277,15 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt } /* - * After setup, the transfer itself is handled entirely by this function. - * There are basically four things to handle: connect, receive, send, and error. + * Checks poll(), same for receiving and sending */ -gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) { - dcc_file_transfer_t *df = data; - file_transfer_t *file = df->ft; struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; - short revents; - - if ( poll( &pfd, 1, 0 ) == -1 ) - { - imcb_log( df->ic, "poll() failed, weird!" ); - revents = 0; - }; - revents = pfd.revents; + ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) - if( revents & POLLERR ) + if( pfd.revents & POLLERR ) { int sockerror; socklen_t errlen = sizeof( sockerror ); @@ -289,9 +296,44 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); } - if( revents & POLLHUP ) + if( pfd.revents & POLLHUP ) return dcc_abort( df, "Remote end closed connection" ); + *revents = pfd.revents; + + return TRUE; +} + +gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) +{ +#ifdef DCC_SEND_AHEAD + /* + * use twice the maximum segment size as a maximum for calls to send(). + */ + if( max_packet_size == 0 ) + { + unsigned int mpslen = sizeof( max_packet_size ); + if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) + return dcc_abort( df, "getsockopt() failed" ); + max_packet_size *= 2; + } +#endif + return TRUE; +} + +/* + * After setup, the transfer itself is handled entirely by this function. + * There are basically four things to handle: connect, receive, send, and error. + */ +gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *file = df->ft; + short revents; + + if( !dcc_poll( df, fd, &revents) ) + return FALSE; + if( ( revents & POLLIN ) && ( file->status & FT_STATUS_LISTENING ) ) { @@ -304,21 +346,12 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) closesocket( fd ); fd = df->fd; - file->status = FT_STATUS_TRANSFERING; + file->status = FT_STATUS_TRANSFERRING; sock_make_nonblocking( fd ); -#ifdef DCC_SEND_AHEAD - /* - * use twice the maximum segment size as a maximum for calls to send(). - */ - if( max_packet_size == 0 ) - { - unsigned int mpslen = sizeof( max_packet_size ); - if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) - return dcc_abort( df, "getsockopt() failed" ); - max_packet_size *= 2; - } -#endif + if ( !dcc_check_maxseg( df, fd ) ) + return FALSE; + /* IM protocol callback */ if( file->accept ) @@ -452,6 +485,113 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return TRUE; } +gboolean dccs_recv_start( file_transfer_t *ft ) +{ + dcc_file_transfer_t *df = ft->priv; + struct sockaddr_storage *saddr = &df->saddr; + int fd; + socklen_t sa_len = saddr->ss_family == AF_INET ? + sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); + + if( !ft->write ) + return dcc_abort( df, "Protocol didn't register write()" ); + + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); + + sock_make_nonblocking( fd ); + + if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) && + ( errno != EINPROGRESS ) ) + return dcc_abort( df, "Connecting" ); + + ft->status = FT_STATUS_CONNECTING; + + /* watch */ + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); + ft->out_of_data = dccs_recv_out_of_data; + + return TRUE; +} + +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) +{ + dcc_file_transfer_t *df = data; + file_transfer_t *ft = df->ft; + short revents; + + if( !dcc_poll( df, fd, &revents ) ) + return FALSE; + + if( ( revents & POLLOUT ) && + ( ft->status & FT_STATUS_CONNECTING ) ) + { + ft->status = FT_STATUS_TRANSFERRING; + if ( !dcc_check_maxseg( df, fd ) ) + return FALSE; + + //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + + df->watch_out = 0; + return FALSE; + } + + if( revents & POLLIN ) + { + char *buffer = g_malloc( 65536 ); + int ret, done; + + ASSERTSOCKOP( ret = recv( fd, buffer, 65536, 0 ), "Receiving" ); + + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); + + buffer = g_realloc( buffer, ret ); + + df->bytes_sent += ret; + + done = df->bytes_sent >= ft->file_size; + + if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) || + done ) + { + int ack, ackret; + ack = htonl( ft->bytes_transferred = df->bytes_sent ); + + ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); + + if ( ackret != 4 ) + return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ret ); + } + + if( !ft->write( df->ft, buffer, ret ) && !done ) + { + df->watch_in = 0; + return FALSE; + } + + if( done ) + { + closesocket( fd ); + dcc_finish( ft ); + + df->watch_in = 0; + return FALSE; + } + + return TRUE; + } + + return TRUE; +} + +void dccs_recv_out_of_data( file_transfer_t *ft ) +{ + dcc_file_transfer_t *df = ft->priv; + + if( !df->watch_in ) + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); +} + /* * Incoming data. Note that the buffer MUST NOT be freed by the caller! * We don't copy the buffer but put it in our queue. @@ -471,7 +611,7 @@ gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int dat df->queued_bytes += data_size; - if( ( file->status & FT_STATUS_TRANSFERING ) && + if( ( file->status & FT_STATUS_TRANSFERRING ) && #ifndef DCC_SEND_AHEAD ( file->bytes_transferred >= df->bytes_sent ) && #endif @@ -532,3 +672,91 @@ void dcc_finish( file_transfer_t *file ) dcc_close( file ); } + +/* + * DCC SEND + * + * filename can be in "" or not. If it is, " can probably be escaped... + * IP can be an unsigned int (IPV4) or something else (IPV6) + * + */ +file_transfer_t *dcc_request( struct im_connection *ic, char *line ) +{ + char *pattern = "SEND" + " (([^\"][^ ]*)|\"([^\"]|\\\")*\")" + " (([0-9]*)|([^ ]*))" + " ([0-9]*)" + " ([0-9]*)\001"; + regmatch_t pmatch[9]; + regex_t re; + file_transfer_t *ft; + dcc_file_transfer_t *df; + + if( regcomp( &re, pattern, REG_EXTENDED ) ) + return NULL; + if( regexec( &re, line, 9, pmatch, 0 ) ) + return NULL; + + if( ( pmatch[1].rm_so > 0 ) && + ( pmatch[4].rm_so > 0 ) && + ( pmatch[7].rm_so > 0 ) && + ( pmatch[8].rm_so > 0 ) ) + { + char *input = g_strdup( line ); + char *filename, *host, *port; + size_t filesize; + struct addrinfo hints, *rp; + + /* "filename" or filename */ + if ( pmatch[2].rm_so > 0 ) + { + input[pmatch[2].rm_eo] = '\0'; + filename = input + pmatch[2].rm_so; + } else + { + input[pmatch[3].rm_eo] = '\0'; + filename = input + pmatch[3].rm_so; + } + + input[pmatch[4].rm_eo] = '\0'; + + /* number means ipv4, something else means ipv6 */ + if ( pmatch[5].rm_so > 0 ) + { + struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) }; + host = inet_ntoa( ipaddr ); + } else + { + /* Contains non-numbers, hopefully an IPV6 address */ + host = input + pmatch[6].rm_so; + } + + input[pmatch[7].rm_eo] = '\0'; + input[pmatch[8].rm_eo] = '\0'; + + port = input + pmatch[7].rm_so; + filesize = atoll( input + pmatch[8].rm_so ); + + memset( &hints, 0, sizeof ( struct addrinfo ) ); + if ( getaddrinfo( host, port, &hints, &rp ) ) + { + g_free( input ); + return NULL; + } + + df = dcc_alloc_transfer( filename, filesize, ic ); + ft = df->ft; + ft->sending = TRUE; + memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen ); + + freeaddrinfo( rp ); + g_free( input ); + + df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft ); + + return ft; + } + + return NULL; +} + -- cgit v1.2.3 From dce390357114e30a424106c99e49cef1e682e1af Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 4 Dec 2007 01:48:57 +0100 Subject: Send and receive seems to work now! Also adopted the new buffering strategy, only one buffer of 2k per transfer now. --- dcc.c | 207 ++++++++++++++++-------------------------------------------------- 1 file changed, 50 insertions(+), 157 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index a5c77ce8..24673085 100644 --- a/dcc.c +++ b/dcc.c @@ -60,16 +60,7 @@ unsigned int local_transfer_id=1; */ unsigned int receivedchunks=0, receiveddata=0; -/* - * If using DCC SEND AHEAD this value will be set before the first transfer starts. - * Not that in this case it degenerates to the maximum message size to send() and - * has nothing to do with packets. - */ -#ifdef DCC_SEND_AHEAD -int max_packet_size = DCC_PACKET_SIZE; -#else int max_packet_size = 0; -#endif static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); @@ -78,7 +69,7 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); -void dccs_recv_out_of_data( file_transfer_t *ft ); +gboolean dccs_recv_write_request( file_transfer_t *ft ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -101,12 +92,6 @@ void imcb_file_canceled( file_transfer_t *file, char *reason ) dcc_close( file ); } -/* As defined in ft.h */ -gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ) -{ - return dccs_send_write( file, data, data_size ); -} - /* As defined in ft.h */ gboolean imcb_file_recv_start( file_transfer_t *ft ) { @@ -138,6 +123,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df = dcc_alloc_transfer( file_name, file_size, ic ); file = df->ft; + file->write = dccs_send_write; + file->sending = TRUE; /* listen and request */ if( !dcc_listen( df, &saddr ) || @@ -194,13 +181,8 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc { struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr; - /* - * this is so ridiculous. We're supposed to convert the address to - * host byte order!!! Let's exclude anyone running big endian just - * for the fun of it... - */ sprintf( ipaddr, "%d", - htonl( saddr_ipv4->sin_addr.s_addr ) ); + ntohl( saddr_ipv4->sin_addr.s_addr ) ); port = saddr_ipv4->sin_port; } else { @@ -226,9 +208,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc g_free( cmd ); - /* message is sortof redundant cause the users client probably informs him about that. remove? */ - imcb_log( df->ic, "Transferring file %s: Chose local address %s for DCC connection", df->ft->file_name, ipaddr ); - return TRUE; } @@ -304,9 +283,11 @@ gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) return TRUE; } +/* + * fills max_packet_size with twice the TCP maximum segment size + */ gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) { -#ifdef DCC_SEND_AHEAD /* * use twice the maximum segment size as a maximum for calls to send(). */ @@ -317,7 +298,6 @@ gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) return dcc_abort( df, "getsockopt() failed" ); max_packet_size *= 2; } -#endif return TRUE; } @@ -353,9 +333,9 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; /* IM protocol callback */ - if( file->accept ) file->accept( file ); + /* reschedule for reading on new fd */ df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -399,89 +379,9 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } -#ifndef DCC_SEND_AHEAD - /* reschedule writer if neccessary */ - if( file->bytes_transferred >= df->bytes_sent && - df->watch_out == 0 && - df->queued_bytes > 0 ) { - df->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, dcc_send_proto, df ); - } -#endif return TRUE; } - if( revents & POLLOUT ) - { - struct dcc_buffer *dccb; - int ret; - char *msg; - - if( df->queued_bytes == 0 ) - { - /* shouldn't happen */ - imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" ); - - df->watch_out = 0; - return FALSE; - } - - /* start where we left off */ - if( !( df->queued_buffers ) || - !( dccb = df->queued_buffers->data ) ) - return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" ); - - msg = dccb->b + df->buffer_pos; - - int msgsize = MIN( -#ifndef DCC_SEND_AHEAD - file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent, -#else - max_packet_size, -#endif - dccb->len - df->buffer_pos ); - - if ( msgsize == 0 ) - { - df->watch_out = 0; - return FALSE; - } - - ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" ); - - if( ret == 0 ) - return dcc_abort( df, "Remote end closed connection" ); - - df->bytes_sent += ret; - df->queued_bytes -= ret; - df->buffer_pos += ret; - - if( df->buffer_pos == dccb->len ) - { - df->buffer_pos = 0; - df->queued_buffers = g_slist_remove( df->queued_buffers, dccb ); - g_free( dccb->b ); - g_free( dccb ); - } - - if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data ) - file->out_of_data( file ); - - if( df->queued_bytes > 0 ) - { - /* Who knows how long the event loop cycle will take, - * let's just try to send some more now. */ -#ifndef DCC_SEND_AHEAD - if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) ) -#endif - return dccs_send_proto( df, fd, cond ); - } - - df->watch_out = 0; - return FALSE; - } - - /* Send buffer full, come back later */ - return TRUE; } @@ -508,7 +408,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) /* watch */ df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); - ft->out_of_data = dccs_recv_out_of_data; + ft->write_request = dccs_recv_write_request; return TRUE; } @@ -537,16 +437,13 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) if( revents & POLLIN ) { - char *buffer = g_malloc( 65536 ); int ret, done; - ASSERTSOCKOP( ret = recv( fd, buffer, 65536, 0 ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, ft->buffer, sizeof( ft->buffer ), 0 ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); - buffer = g_realloc( buffer, ret ); - df->bytes_sent += ret; done = df->bytes_sent >= ft->file_size; @@ -560,14 +457,11 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); if ( ackret != 4 ) - return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ret ); + return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret ); } - if( !ft->write( df->ft, buffer, ret ) && !done ) - { - df->watch_in = 0; + if( !ft->write( df->ft, ft->buffer, ret ) ) return FALSE; - } if( done ) { @@ -578,50 +472,63 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) return FALSE; } - return TRUE; + df->watch_in = 0; + return FALSE; } return TRUE; } -void dccs_recv_out_of_data( file_transfer_t *ft ) +gboolean dccs_recv_write_request( file_transfer_t *ft ) { dcc_file_transfer_t *df = ft->priv; - if( !df->watch_in ) - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + if( df->watch_in ) + return dcc_abort( df, "BUG: write_request() called while watching" ); + + df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + + return TRUE; +} + +gboolean dccs_send_can_write( gpointer data, gint fd, b_input_condition cond ) +{ + struct dcc_file_transfer *df = data; + df->watch_out = 0; + + df->ft->write_request( df->ft ); + return FALSE; } /* - * Incoming data. Note that the buffer MUST NOT be freed by the caller! - * We don't copy the buffer but put it in our queue. + * Incoming data. * - * */ -gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size ) + */ +gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len ) { dcc_file_transfer_t *df = file->priv; - struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 ); + int ret; + + receivedchunks++; receiveddata += data_len; - receivedchunks++; receiveddata += data_size; + if( df->watch_out ) + return dcc_abort( df, "BUG: write() called while watching" ); - dccb->b = data; - dccb->len = data_size; + ASSERTSOCKOP( ret = send( df->fd, data, data_len, 0 ), "Sending data" ); - df->queued_buffers = g_slist_append( df->queued_buffers, dccb ); + if( ret == 0 ) + return dcc_abort( df, "Remote end closed connection" ); - df->queued_bytes += data_size; + /* TODO: this should really not be fatal */ + if( ret < data_len ) + return dcc_abort( df, "send() sent %d instead of %d", ret, data_len ); - if( ( file->status & FT_STATUS_TRANSFERRING ) && -#ifndef DCC_SEND_AHEAD - ( file->bytes_transferred >= df->bytes_sent ) && -#endif - ( df->watch_out == 0 ) && - ( df->queued_bytes > 0 ) ) - { - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df ); - } - - return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH; + df->bytes_sent += ret; + + if( df->bytes_sent < df->ft->file_size ) + df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df ); + + return TRUE; } /* @@ -642,20 +549,6 @@ static void dcc_close( file_transfer_t *file ) if( df->watch_out ) b_event_remove( df->watch_out ); - if( df->queued_buffers ) - { - struct dcc_buffer *dccb; - GSList *gslist = df->queued_buffers; - - for( ; gslist ; gslist = g_slist_next( gslist ) ) - { - dccb = gslist->data; - g_free( dccb->b ); - g_free( dccb ); - } - g_slist_free( df->queued_buffers ); - } - df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); g_free( df ); -- cgit v1.2.3 From dc0ba9c85539533349353713162f94077fb27be3 Mon Sep 17 00:00:00 2001 From: kenobi Date: Tue, 18 Dec 2007 03:07:59 +0100 Subject: sending via proxy --- dcc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 24673085..2fa3f745 100644 --- a/dcc.c +++ b/dcc.c @@ -124,7 +124,6 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df = dcc_alloc_transfer( file_name, file_size, ic ); file = df->ft; file->write = dccs_send_write; - file->sending = TRUE; /* listen and request */ if( !dcc_listen( df, &saddr ) || -- cgit v1.2.3 From 6cac643f6933e431b90fcb937dec505f989e6a53 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 14 Apr 2008 14:59:15 +0200 Subject: more verbose error logging --- dcc.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 2fa3f745..fa5a0f76 100644 --- a/dcc.c +++ b/dcc.c @@ -583,11 +583,17 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) regex_t re; file_transfer_t *ft; dcc_file_transfer_t *df; - - if( regcomp( &re, pattern, REG_EXTENDED ) ) - return NULL; - if( regexec( &re, line, 9, pmatch, 0 ) ) + char errbuf[256]; + int regerrcode, gret; + + if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) || + ( regerrcode = regexec( &re, line, 9, pmatch, 0 ) ) ) { + regerror( regerrcode,&re,errbuf,sizeof( errbuf ) ); + imcb_log( ic, + "DCC: error parsing 'DCC SEND': %s, line: %s", + errbuf, line ); return NULL; + } if( ( pmatch[1].rm_so > 0 ) && ( pmatch[4].rm_so > 0 ) && @@ -615,7 +621,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) /* number means ipv4, something else means ipv6 */ if ( pmatch[5].rm_so > 0 ) { - struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) }; + struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; host = inet_ntoa( ipaddr ); } else { @@ -630,9 +636,13 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filesize = atoll( input + pmatch[8].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); - if ( getaddrinfo( host, port, &hints, &rp ) ) + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) { g_free( input ); + imcb_log( ic, "DCC: getaddrinfo() failed with %s " + "when parsing incoming 'DCC SEND': " + "host %s, port %s", + gai_strerror( gret ), host, port ); return NULL; } @@ -649,6 +659,8 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) return ft; } + imcb_log( ic, "DCC: couldnt parse 'DCC SEND' line: %s", line ); + return NULL; } -- cgit v1.2.3 From 0cab3888888c7c6b58af9560a0ae2c74a795727f Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 14 Apr 2008 17:33:13 +0200 Subject: more verbose error reporting --- dcc.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index fa5a0f76..d93eef87 100644 --- a/dcc.c +++ b/dcc.c @@ -217,7 +217,7 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt { file_transfer_t *file = df->ft; struct sockaddr_storage *saddr; - int fd; + int fd,gret; char hostname[ HOST_NAME_MAX + 1 ]; struct addrinfo hints, *rp; socklen_t ssize = sizeof( struct sockaddr_storage ); @@ -230,8 +230,8 @@ gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_pt hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; - if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) - return dcc_abort( df, "getaddrinfo()" ); + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) ) + return dcc_abort( df, "getaddrinfo(): %s", gai_strerror( gret ) ); saddr = g_new( struct sockaddr_storage, 1 ); @@ -389,11 +389,12 @@ gboolean dccs_recv_start( file_transfer_t *ft ) dcc_file_transfer_t *df = ft->priv; struct sockaddr_storage *saddr = &df->saddr; int fd; + char ipaddr[INET6_ADDRSTRLEN]; socklen_t sa_len = saddr->ss_family == AF_INET ? sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 ); if( !ft->write ) - return dcc_abort( df, "Protocol didn't register write()" ); + return dcc_abort( df, "BUG: protocol didn't register write()" ); ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); @@ -401,7 +402,17 @@ gboolean dccs_recv_start( file_transfer_t *ft ) if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) && ( errno != EINPROGRESS ) ) - return dcc_abort( df, "Connecting" ); + return dcc_abort( df, "Connecting to %s:%d : %s", + inet_ntop( saddr->ss_family, + saddr->ss_family == AF_INET ? + ( void* ) &( ( struct sockaddr_in *) saddr )->sin_addr.s_addr : + ( void* ) &( ( struct sockaddr_in6 *) saddr )->sin6_addr.s6_addr, + ipaddr, + sizeof( ipaddr ) ), + ntohs( saddr->ss_family == AF_INET ? + ( ( struct sockaddr_in *) saddr )->sin_port : + ( ( struct sockaddr_in6 *) saddr )->sin6_port ), + strerror( errno ) ); ft->status = FT_STATUS_CONNECTING; -- cgit v1.2.3 From 4358b10c11410a27af9458c92067549cafbc4c0b Mon Sep 17 00:00:00 2001 From: ulim Date: Sun, 4 May 2008 15:32:15 +0200 Subject: ulibc support, fixes "Invalid SOCKS5 Connect message" problem --- dcc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index d93eef87..cd70c480 100644 --- a/dcc.c +++ b/dcc.c @@ -29,6 +29,20 @@ #include #include +/* Some ifdefs for ulibc (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + /* * Since that might be confusing a note on naming: * -- cgit v1.2.3 From d56ee38d444fb9a4bc0fbf7f699eaf675e019591 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 6 May 2008 02:20:11 +0200 Subject: timeout of transfers after 120 seconds of no progress (bytes received/sent). --- dcc.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index cd70c480..a6eb511c 100644 --- a/dcc.c +++ b/dcc.c @@ -84,6 +84,7 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); gboolean dccs_recv_write_request( file_transfer_t *ft ); +gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -151,6 +152,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); + df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + return file; } @@ -167,8 +170,8 @@ gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) if( file->canceled ) file->canceled( file, msg ); - else - imcb_log( df->ic, "DCC transfer aborted: %s", msg ); + + imcb_log( df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg ); g_free( msg ); @@ -177,6 +180,25 @@ gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ) return FALSE; } +gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) +{ + struct dcc_file_transfer *df = data; + + if( df->ft->bytes_transferred == df->progress_bytes_last ) + { + /* no progress. cancel */ + if( df->bytes_sent == 0 ) + return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); + else + return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->ft->bytes_transferred / 1024 ); + + } + + df->progress_bytes_last = df->ft->bytes_transferred; + + return TRUE; +} + /* used extensively for socket operations */ #define ASSERTSOCKOP(op, msg) \ if( (op) == -1 ) \ @@ -434,6 +456,8 @@ gboolean dccs_recv_start( file_transfer_t *ft ) df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); ft->write_request = dccs_recv_write_request; + df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + return TRUE; } @@ -573,6 +597,9 @@ static void dcc_close( file_transfer_t *file ) if( df->watch_out ) b_event_remove( df->watch_out ); + if( df->progress_timeout ) + b_event_remove( df->progress_timeout ); + df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file ); g_free( df ); -- cgit v1.2.3 From b043ad5e69d91730145ea49305a1fab588c83a93 Mon Sep 17 00:00:00 2001 From: ulim Date: Thu, 8 May 2008 23:20:36 +0200 Subject: allow dcc optimization in progress watcher. Apparently, irssi doesn't send any DCC ACKs if it "feels" that the sender doesn't expect any(no idea how exactly it does that). Anyway, the progress watcher used to check whether the ACKed bytes have increased which it shouldn't do if there aren't any ACKs. --- dcc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index a6eb511c..0ee894b7 100644 --- a/dcc.c +++ b/dcc.c @@ -184,17 +184,17 @@ gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) { struct dcc_file_transfer *df = data; - if( df->ft->bytes_transferred == df->progress_bytes_last ) + if( df->bytes_sent == df->progress_bytes_last ) { /* no progress. cancel */ if( df->bytes_sent == 0 ) return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); else - return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->ft->bytes_transferred / 1024 ); + return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 ); } - df->progress_bytes_last = df->ft->bytes_transferred; + df->progress_bytes_last = df->bytes_sent; return TRUE; } -- cgit v1.2.3 From 9afeeface6363ea81c5f80a7274a691f18550a21 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 22 Jul 2008 14:36:56 +0200 Subject: fixes bug in handling file names with spaces. --- dcc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 0ee894b7..d815e9b7 100644 --- a/dcc.c +++ b/dcc.c @@ -627,11 +627,11 @@ void dcc_finish( file_transfer_t *file ) file_transfer_t *dcc_request( struct im_connection *ic, char *line ) { char *pattern = "SEND" - " (([^\"][^ ]*)|\"([^\"]|\\\")*\")" + " (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")" " (([0-9]*)|([^ ]*))" " ([0-9]*)" " ([0-9]*)\001"; - regmatch_t pmatch[9]; + regmatch_t pmatch[10]; regex_t re; file_transfer_t *ft; dcc_file_transfer_t *df; @@ -639,7 +639,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) int regerrcode, gret; if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) || - ( regerrcode = regexec( &re, line, 9, pmatch, 0 ) ) ) { + ( regerrcode = regexec( &re, line, 10, pmatch, 0 ) ) ) { regerror( regerrcode,&re,errbuf,sizeof( errbuf ) ); imcb_log( ic, "DCC: error parsing 'DCC SEND': %s, line: %s", @@ -648,9 +648,9 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) } if( ( pmatch[1].rm_so > 0 ) && - ( pmatch[4].rm_so > 0 ) && - ( pmatch[7].rm_so > 0 ) && - ( pmatch[8].rm_so > 0 ) ) + ( pmatch[5].rm_so > 0 ) && + ( pmatch[8].rm_so > 0 ) && + ( pmatch[9].rm_so > 0 ) ) { char *input = g_strdup( line ); char *filename, *host, *port; @@ -668,24 +668,24 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filename = input + pmatch[3].rm_so; } - input[pmatch[4].rm_eo] = '\0'; + input[pmatch[5].rm_eo] = '\0'; /* number means ipv4, something else means ipv6 */ - if ( pmatch[5].rm_so > 0 ) + if ( pmatch[6].rm_so > 0 ) { struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; host = inet_ntoa( ipaddr ); } else { /* Contains non-numbers, hopefully an IPV6 address */ - host = input + pmatch[6].rm_so; + host = input + pmatch[7].rm_so; } - input[pmatch[7].rm_eo] = '\0'; input[pmatch[8].rm_eo] = '\0'; + input[pmatch[9].rm_eo] = '\0'; - port = input + pmatch[7].rm_so; - filesize = atoll( input + pmatch[8].rm_so ); + port = input + pmatch[8].rm_so; + filesize = atoll( input + pmatch[9].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) -- cgit v1.2.3 From 4ac647dbcef152bebde7209f7c9cbbf8a5e0fc37 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 4 Aug 2008 16:21:49 +0200 Subject: moved some stuff around in preperation for MSN merge. * both ends (proto&dcc) need to finish a transfer now for it to be finished * moved throughput calc. and some messages to dcc (no need to implement in protocols) --- dcc.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index d815e9b7..909fedad 100644 --- a/dcc.c +++ b/dcc.c @@ -113,6 +113,17 @@ gboolean imcb_file_recv_start( file_transfer_t *ft ) return dccs_recv_start( ft ); } +/* As defined in ft.h */ +void imcb_file_finished( file_transfer_t *file ) +{ + dcc_file_transfer_t *df = file->priv; + + if( file->bytes_transferred >= file->file_size ) + dcc_finish( file ); + else + df->proto_finished = TRUE; +} + dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) { file_transfer_t *file = g_new0( file_transfer_t, 1 ); @@ -154,6 +165,10 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); + imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", user_nick, file_name, file_size/1024 ); + + imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + return file; } @@ -410,7 +425,8 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->bytes_transferred = bytes_received; if( file->bytes_transferred >= file->file_size ) { - dcc_finish( file ); + if( df->proto_finished ) + dcc_finish( file ); return FALSE; } @@ -492,6 +508,9 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); + if( !ft->write( df->ft, ft->buffer, ret ) ) + return FALSE; + df->bytes_sent += ret; done = df->bytes_sent >= ft->file_size; @@ -508,13 +527,16 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret ); } - if( !ft->write( df->ft, ft->buffer, ret ) ) - return FALSE; + if( df->bytes_sent == ret ) + ft->started = time( NULL ); if( done ) { - closesocket( fd ); - dcc_finish( ft ); + if( df->watch_out ) + b_event_remove( df->watch_out ); + + if( df->proto_finished ) + dcc_finish( ft ); df->watch_in = 0; return FALSE; @@ -571,6 +593,9 @@ gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_l if( ret < data_len ) return dcc_abort( df, "send() sent %d instead of %d", ret, data_len ); + if( df->bytes_sent == 0 ) + file->started = time( NULL ); + df->bytes_sent += ret; if( df->bytes_sent < df->ft->file_size ) @@ -609,11 +634,15 @@ static void dcc_close( file_transfer_t *file ) void dcc_finish( file_transfer_t *file ) { + dcc_file_transfer_t *df = file->priv; + time_t diff = time( NULL ) - file->started ? : 1; + file->status |= FT_STATUS_FINISHED; if( file->finished ) file->finished( file ); + imcb_log( df->ic, "File %s transferred successfully at %d kb/s!" , file->file_name, (int) ( file->bytes_transferred / 1024 / diff ) ); dcc_close( file ); } -- cgit v1.2.3 From 92f4ec5d9d480247281c50c163c19b1ea438c1b3 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 11 Aug 2008 01:51:48 +0200 Subject: fixes bug on 32bit archs in DCC code. Shouldn't atoi be the same on 64 and 32bits? Strange. --- dcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 909fedad..2bb85ad4 100644 --- a/dcc.c +++ b/dcc.c @@ -702,7 +702,7 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) /* number means ipv4, something else means ipv6 */ if ( pmatch[6].rm_so > 0 ) { - struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) }; + struct in_addr ipaddr = { .s_addr = htonl( strtoul( input + pmatch[5].rm_so, NULL, 10 ) ) }; host = inet_ntoa( ipaddr ); } else { -- cgit v1.2.3 From a02f34fb047af728f991ced3688c5e804c130878 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 01:07:12 +0200 Subject: Added conf entries and lib/ftutil.[ch]. ft_listen = :;: to specify listening addresses for the bitlbee<->client connection and the bitlbee<->IM peer connection, respectively. ft_max_size should be obvious. ft_max_kbps should limit the kilobits per second per transfer (not implemented yet). --- dcc.c | 79 +++++++++++++------------------------------------------------------ 1 file changed, 15 insertions(+), 64 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 2bb85ad4..c54e9169 100644 --- a/dcc.c +++ b/dcc.c @@ -28,20 +28,7 @@ #include #include #include - -/* Some ifdefs for ulibc (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif +#include "lib/ftutil.h" /* * Since that might be confusing a note on naming: @@ -79,12 +66,12 @@ int max_packet_size = 0; static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); -gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ); int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr ); gboolean dccs_recv_start( file_transfer_t *ft ); gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond); gboolean dccs_recv_write_request( file_transfer_t *ft ); gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ); +gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... ); /* As defined in ft.h */ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size ) @@ -142,9 +129,12 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha { file_transfer_t *file; dcc_file_transfer_t *df; - struct sockaddr_storage *saddr; + struct sockaddr_storage saddr; + char *errmsg; + char host[INET6_ADDRSTRLEN]; + char port[6]; - if( file_size > global.conf->max_filetransfer_size ) + if( file_size > global.conf->ft_max_size ) return NULL; df = dcc_alloc_transfer( file_name, file_size, ic ); @@ -152,11 +142,16 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha file->write = dccs_send_write; /* listen and request */ - if( !dcc_listen( df, &saddr ) || - !dccs_send_request( df, user_nick, saddr ) ) + + if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) { + dcc_abort( df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); return NULL; + } - g_free( saddr ); + file->status = FT_STATUS_LISTENING; + + if( !dccs_send_request( df, user_nick, &saddr ) ) + return NULL; /* watch */ df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); @@ -261,50 +256,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc return TRUE; } -/* - * Creates a listening socket and returns it in saddr_ptr. - */ -gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr ) -{ - file_transfer_t *file = df->ft; - struct sockaddr_storage *saddr; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) ) - return dcc_abort( df, "getaddrinfo(): %s", gai_strerror( gret ) ); - - saddr = g_new( struct sockaddr_storage, 1 ); - - *saddr_ptr = saddr; - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - file->status = FT_STATUS_LISTENING; - - return TRUE; -} - /* * Checks poll(), same for receiving and sending */ -- cgit v1.2.3 From aac40178a6669e20855b7f5d3cc6a82cba10042e Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 13:04:37 +0200 Subject: More hints for getaddrinfo(). Hopefully solves a problem on FreeBSD. --- dcc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index c54e9169..b6345a92 100644 --- a/dcc.c +++ b/dcc.c @@ -668,6 +668,9 @@ file_transfer_t *dcc_request( struct im_connection *ic, char *line ) filesize = atoll( input + pmatch[9].rm_so ); memset( &hints, 0, sizeof ( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) ) { g_free( input ); -- cgit v1.2.3 From 1c3008ac0b2b29f7e14ec9b874af3277c511c7a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 13 Dec 2009 14:48:56 +0000 Subject: No functional changes, just some style "fixes". Although I admit I'm somewhat growing out of my own coding style, I do try to keep things consistent at least within files. Comments are now in reviewboard: http://code.bitlbee.org/rb/r/13/ --- dcc.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index b6345a92..655c98b9 100644 --- a/dcc.c +++ b/dcc.c @@ -114,7 +114,8 @@ void imcb_file_finished( file_transfer_t *file ) dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic ) { file_transfer_t *file = g_new0( file_transfer_t, 1 ); - dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1); + dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1 ); + file->file_size = file_size; file->file_name = g_strdup( file_name ); file->local_id = local_transfer_id++; @@ -143,7 +144,8 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha /* listen and request */ - if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) { + if( ( df->fd = ft_listen( &saddr, host, port, TRUE, &errmsg ) ) == -1 ) + { dcc_abort( df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg ); return NULL; } @@ -160,9 +162,10 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); - imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", user_nick, file_name, file_size/1024 ); - - imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + imcb_log( ic, "File transfer request from %s for %s (%zd kb).\n" + "Accept the file transfer if you'd like the file. If you don't, " + "issue the 'transfers reject' command.", + user_nick, file_name, file_size / 1024 ); return file; } @@ -198,7 +201,7 @@ gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond ) { /* no progress. cancel */ if( df->bytes_sent == 0 ) - return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL ); + return dcc_abort( df, "Couldn't establish transfer within %d seconds", DCC_MAX_STALL ); else return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 ); @@ -229,7 +232,8 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc sprintf( ipaddr, "%d", ntohl( saddr_ipv4->sin_addr.s_addr ) ); port = saddr_ipv4->sin_port; - } else + } + else { struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr; @@ -249,7 +253,7 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc df->ft->file_name, ipaddr, port, df->ft->file_size ); if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) ) - return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick ); + return dcc_abort( df, "Couldn't send `DCC SEND' message to %s.", user_nick ); g_free( cmd ); @@ -312,7 +316,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file_transfer_t *file = df->ft; short revents; - if( !dcc_poll( df, fd, &revents) ) + if( !dcc_poll( df, fd, &revents ) ) return FALSE; if( ( revents & POLLIN ) && @@ -348,18 +352,18 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) int bytes_received; int ret; - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); if( ret < 4 ) { - imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" ); + imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only %d bytes instead of 4, shouldn't happen too often!", ret ); return TRUE; } - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); if( ret != 4 ) return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); @@ -399,7 +403,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) if( !ft->write ) return dcc_abort( df, "BUG: protocol didn't register write()" ); - ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" ); + ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening Socket" ); sock_make_nonblocking( fd ); @@ -428,7 +432,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) return TRUE; } -gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond) +gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; -- cgit v1.2.3 From 60e4df367e5c3af0eb1aada19f9c39ef7079e8e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:23:27 +0000 Subject: Small cleanup. The max_packet_size variable doesn't seem to be read anywhere, and reworked string handling in ft_listen() a little bit. --- dcc.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index 655c98b9..d28c3b34 100644 --- a/dcc.c +++ b/dcc.c @@ -61,8 +61,6 @@ unsigned int local_transfer_id=1; */ unsigned int receivedchunks=0, receiveddata=0; -int max_packet_size = 0; - static void dcc_finish( file_transfer_t *file ); static void dcc_close( file_transfer_t *file ); gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ); @@ -132,7 +130,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha dcc_file_transfer_t *df; struct sockaddr_storage saddr; char *errmsg; - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX]; char port[6]; if( file_size > global.conf->ft_max_size ) @@ -288,24 +286,6 @@ gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) return TRUE; } -/* - * fills max_packet_size with twice the TCP maximum segment size - */ -gboolean dcc_check_maxseg( dcc_file_transfer_t *df, int fd ) -{ - /* - * use twice the maximum segment size as a maximum for calls to send(). - */ - if( max_packet_size == 0 ) - { - unsigned int mpslen = sizeof( max_packet_size ); - if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) ) - return dcc_abort( df, "getsockopt() failed" ); - max_packet_size *= 2; - } - return TRUE; -} - /* * After setup, the transfer itself is handled entirely by this function. * There are basically four things to handle: connect, receive, send, and error. @@ -334,9 +314,6 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->status = FT_STATUS_TRANSFERRING; sock_make_nonblocking( fd ); - if ( !dcc_check_maxseg( df, fd ) ) - return FALSE; - /* IM protocol callback */ if( file->accept ) file->accept( file ); @@ -445,8 +422,6 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; - if ( !dcc_check_maxseg( df, fd ) ) - return FALSE; //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); -- cgit v1.2.3 From 2e89256f2265676d5d37b92433352ceccf079a7a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 13:20:20 +0000 Subject: Remove dcc_poll() and just use the cond variable passed to I/O events. --- dcc.c | 45 ++++----------------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index d28c3b34..dcfeb190 100644 --- a/dcc.c +++ b/dcc.c @@ -25,7 +25,6 @@ #include "bitlbee.h" #include "ft.h" #include "dcc.h" -#include #include #include #include "lib/ftutil.h" @@ -258,34 +257,6 @@ int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct soc return TRUE; } -/* - * Checks poll(), same for receiving and sending - */ -gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents ) -{ - struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT }; - - ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) - - if( pfd.revents & POLLERR ) - { - int sockerror; - socklen_t errlen = sizeof( sockerror ); - - if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) - return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" ); - - return dcc_abort( df, "Socket error: %s", strerror( sockerror ) ); - } - - if( pfd.revents & POLLHUP ) - return dcc_abort( df, "Remote end closed connection" ); - - *revents = pfd.revents; - - return TRUE; -} - /* * After setup, the transfer itself is handled entirely by this function. * There are basically four things to handle: connect, receive, send, and error. @@ -294,12 +265,8 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *file = df->ft; - short revents; - if( !dcc_poll( df, fd, &revents ) ) - return FALSE; - - if( ( revents & POLLIN ) && + if( ( cond & GAIM_INPUT_READ ) && ( file->status & FT_STATUS_LISTENING ) ) { struct sockaddr *clt_addr; @@ -324,7 +291,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } - if( revents & POLLIN ) + if( cond & GAIM_INPUT_READ ) { int bytes_received; int ret; @@ -413,12 +380,8 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) { dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; - short revents; - if( !dcc_poll( df, fd, &revents ) ) - return FALSE; - - if( ( revents & POLLOUT ) && + if( ( cond & GAIM_INPUT_WRITE ) && ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; @@ -429,7 +392,7 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) return FALSE; } - if( revents & POLLIN ) + if( cond & GAIM_INPUT_READ ) { int ret, done; -- cgit v1.2.3 From 4ed9c8c11adb8d1010ed4bb7b3ff9af962f91237 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 15:10:07 +0000 Subject: Fixed 100% CPU usage bug in dcc.c. --- dcc.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'dcc.c') diff --git a/dcc.c b/dcc.c index dcfeb190..558d923a 100644 --- a/dcc.c +++ b/dcc.c @@ -293,35 +293,30 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) if( cond & GAIM_INPUT_READ ) { - int bytes_received; int ret; - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), MSG_PEEK ), "Receiving" ); + ASSERTSOCKOP( ret = recv( fd, ( (char*) &df->acked ) + df->acked_len, + sizeof( df->acked ) - df->acked_len, 0 ), "Receiving" ); if( ret == 0 ) return dcc_abort( df, "Remote end closed connection" ); - - if( ret < 4 ) - { - imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only %d bytes instead of 4, shouldn't happen too often!", ret ); + + /* How likely is it that a 32-bit integer gets split accross + packet boundaries? Chances are rarely 0 so let's be sure. */ + if( ( df->acked_len = ( df->acked_len + ret ) % 4 ) > 0 ) return TRUE; - } - - ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received ), 0 ), "Receiving" ); - if( ret != 4 ) - return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret ); - bytes_received = ntohl( bytes_received ); + df->acked = ntohl( df->acked ); /* If any of this is actually happening, the receiver should buy a new IRC client */ - if ( bytes_received > df->bytes_sent ) - return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent ); + if ( df->acked > df->bytes_sent ) + return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", df->acked, df->bytes_sent ); - if ( bytes_received < file->bytes_transferred ) - return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred ); + if ( df->acked < file->bytes_transferred ) + return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", df->acked, file->bytes_transferred ); - file->bytes_transferred = bytes_received; + file->bytes_transferred = df->acked; if( file->bytes_transferred >= file->file_size ) { if( df->proto_finished ) @@ -411,8 +406,8 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) || done ) { - int ack, ackret; - ack = htonl( ft->bytes_transferred = df->bytes_sent ); + guint32 ack = htonl( ft->bytes_transferred = df->bytes_sent ); + int ackret; ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" ); -- cgit v1.2.3