Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • dcc.c

    rdce3903 r2c2df7d  
    2828#include <poll.h>
    2929#include <netinet/tcp.h>
    30 #include <regex.h>
    3130
    3231/*
     
    6160unsigned int receivedchunks=0, receiveddata=0;
    6261
     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
    6370int max_packet_size = 0;
     71#endif
    6472
    6573static void dcc_finish( file_transfer_t *file );
     
    6876gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr );
    6977int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
    70 gboolean dccs_recv_start( file_transfer_t *ft );
    71 gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
    72 gboolean dccs_recv_write_request( file_transfer_t *ft );
    7378
    7479/* As defined in ft.h */
     
    9499
    95100/* As defined in ft.h */
    96 gboolean imcb_file_recv_start( file_transfer_t *ft )
    97 {
    98         return dccs_recv_start( ft );
    99 }
    100 
    101 dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic )
    102 {
    103         file_transfer_t *file = g_new0( file_transfer_t, 1 );
    104         dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1);
     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);
    105119        file->file_size = file_size;
    106120        file->file_name = g_strdup( file_name );
     
    109123        df->ft = file;
    110124       
    111         return df;
    112 }
    113 
    114 /* This is where the sending magic starts... */
    115 file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size )
    116 {
    117         file_transfer_t *file;
    118         dcc_file_transfer_t *df;
    119         struct sockaddr_storage *saddr;
    120 
    121         if( file_size > global.conf->max_filetransfer_size )
     125        /* listen and request */
     126        if( !dcc_listen( df, saddr ) ||
     127            !dccs_send_request( df, user_nick, *saddr ) )
    122128                return NULL;
    123        
    124         df = dcc_alloc_transfer( file_name, file_size, ic );
    125         file = df->ft;
    126         file->write = dccs_send_write;
    127         file->sending = TRUE;
    128 
    129         /* listen and request */
    130         if( !dcc_listen( df, &saddr ) ||
    131             !dccs_send_request( df, user_nick, saddr ) )
    132                 return NULL;
    133 
    134         g_free( saddr );
     129
     130        g_free( *saddr );
    135131
    136132        /* watch */
     
    182178                struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
    183179
     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                 */
    184185                sprintf( ipaddr, "%d",
    185                          ntohl( saddr_ipv4->sin_addr.s_addr ) );
     186                         htonl( saddr_ipv4->sin_addr.s_addr ) );
    186187                port = saddr_ipv4->sin_port;
    187188        } else
     
    208209
    209210        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 );
    210214
    211215        return TRUE;
     
    257261
    258262/*
    259  * Checks poll(), same for receiving and sending
    260  */
    261 gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents )
    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;
    263270        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
    264 
    265         ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" )
    266 
    267         if( pfd.revents & POLLERR )
     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 )
    268282        {
    269283                int sockerror;
     
    276290        }
    277291       
    278         if( pfd.revents & POLLHUP )
     292        if( revents & POLLHUP )
    279293                return dcc_abort( df, "Remote end closed connection" );
    280294       
    281         *revents = pfd.revents;
    282 
    283         return TRUE;
    284 }
    285 
    286 /*
    287  * fills max_packet_size with twice the TCP maximum segment size
    288  */
    289 gboolean  dcc_check_maxseg( dcc_file_transfer_t *df, int fd )
    290 {
    291         /*
    292          * use twice the maximum segment size as a maximum for calls to send().
    293          */
    294         if( max_packet_size == 0 )
    295         {
    296                 unsigned int mpslen = sizeof( max_packet_size );
    297                 if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) )
    298                         return dcc_abort( df, "getsockopt() failed" );
    299                 max_packet_size *= 2;
    300         }
    301         return TRUE;
    302 }
    303 
    304 /*
    305  * After setup, the transfer itself is handled entirely by this function.
    306  * There are basically four things to handle: connect, receive, send, and error.
    307  */
    308 gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond )
    309 {
    310         dcc_file_transfer_t *df = data;
    311         file_transfer_t *file = df->ft;
    312         short revents;
    313        
    314         if( !dcc_poll( df, fd, &revents) )
    315                 return FALSE;
    316 
    317295        if( ( revents & POLLIN ) &&
    318296            ( file->status & FT_STATUS_LISTENING ) )
     
    327305                closesocket( fd );
    328306                fd = df->fd;
    329                 file->status = FT_STATUS_TRANSFERRING;
     307                file->status = FT_STATUS_TRANSFERING;
    330308                sock_make_nonblocking( fd );
    331309
    332                 if ( !dcc_check_maxseg( df, fd ) )
    333                         return FALSE;
    334 
     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
    335322                /* IM protocol callback */
     323
    336324                if( file->accept )
    337325                        file->accept( file );
    338 
    339326                /* reschedule for reading on new fd */
    340327                df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df );
     
    380367                }
    381368       
     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
    382377                return TRUE;
    383378        }
    384379
    385         return TRUE;
    386 }
    387 
    388 gboolean dccs_recv_start( file_transfer_t *ft )
    389 {
    390         dcc_file_transfer_t *df = ft->priv;
    391         struct sockaddr_storage *saddr = &df->saddr;
    392         int fd;
    393         socklen_t sa_len = saddr->ss_family == AF_INET ?
    394                 sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
    395        
    396         if( !ft->write )
    397                 return dcc_abort( df, "Protocol didn't register write()" );
    398        
    399         ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" );
    400 
    401         sock_make_nonblocking( fd );
    402 
    403         if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) &&
    404             ( errno != EINPROGRESS ) )
    405                 return dcc_abort( df, "Connecting" );
    406 
    407         ft->status = FT_STATUS_CONNECTING;
    408 
    409         /* watch */
    410         df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df );
    411         ft->write_request = dccs_recv_write_request;
    412 
    413         return TRUE;
    414 }
    415 
    416 gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond)
    417 {
    418         dcc_file_transfer_t *df = data;
    419         file_transfer_t *ft = df->ft;
    420         short revents;
    421 
    422         if( !dcc_poll( df, fd, &revents ) )
    423                 return FALSE;
    424        
    425         if( ( revents & POLLOUT ) &&
    426             ( ft->status & FT_STATUS_CONNECTING ) )
    427         {
    428                 ft->status = FT_STATUS_TRANSFERRING;
    429                 if ( !dcc_check_maxseg( df, fd ) )
     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;
    430392                        return FALSE;
    431 
    432                 //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
     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                }
    433445
    434446                df->watch_out = 0;
     
    436448        }
    437449
    438         if( revents & POLLIN )
    439         {
    440                 int ret, done;
    441 
    442                 ASSERTSOCKOP( ret = recv( fd, ft->buffer, sizeof( ft->buffer ), 0 ), "Receiving" );
    443 
    444                 if( ret == 0 )
    445                         return dcc_abort( df, "Remote end closed connection" );
    446 
    447                 df->bytes_sent += ret;
    448 
    449                 done = df->bytes_sent >= ft->file_size;
    450 
    451                 if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) ||
    452                     done )
    453                 {
    454                         int ack, ackret;
    455                         ack = htonl( ft->bytes_transferred = df->bytes_sent );
    456 
    457                         ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" );
    458                        
    459                         if ( ackret != 4 )
    460                                 return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret );
    461                 }
    462                
    463                 if( !ft->write( df->ft, ft->buffer, ret ) )
    464                         return FALSE;
    465 
    466                 if( done )
    467                 {
    468                         closesocket( fd );
    469                         dcc_finish( ft );
    470 
    471                         df->watch_in = 0;
    472                         return FALSE;
    473                 }
    474 
    475                 df->watch_in = 0;
    476                 return FALSE;
    477         }
     450        /* Send buffer full, come back later */
    478451
    479452        return TRUE;
    480453}
    481454
    482 gboolean dccs_recv_write_request( file_transfer_t *ft )
    483 {
    484         dcc_file_transfer_t *df = ft->priv;
    485 
    486         if( df->watch_in )
    487                 return dcc_abort( df, "BUG: write_request() called while watching" );
    488 
    489         df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
    490 
    491         return TRUE;
    492 }
    493 
    494 gboolean dccs_send_can_write( gpointer data, gint fd, b_input_condition cond )
    495 {
    496         struct dcc_file_transfer *df = data;
    497         df->watch_out = 0;
    498 
    499         df->ft->write_request( df->ft );
    500         return FALSE;
    501 }
    502 
    503455/*
    504  * Incoming data.
     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.
    505458 *
    506  */
    507 gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len )
     459 * */
     460gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size )
    508461{
    509462        dcc_file_transfer_t *df = file->priv;
    510         int ret;
    511 
    512         receivedchunks++; receiveddata += data_len;
    513 
    514         if( df->watch_out )
    515                 return dcc_abort( df, "BUG: write() called while watching" );
    516 
    517         ASSERTSOCKOP( ret = send( df->fd, data, data_len, 0 ), "Sending data" );
    518 
    519         if( ret == 0 )
    520                 return dcc_abort( df, "Remote end closed connection" );
    521 
    522         /* TODO: this should really not be fatal */
    523         if( ret < data_len )
    524                 return dcc_abort( df, "send() sent %d instead of %d", ret, data_len );
    525 
    526         df->bytes_sent += ret;
    527 
    528         if( df->bytes_sent < df->ft->file_size )
    529                 df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df );
    530 
    531         return TRUE;
     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;
    532485}
    533486
     
    550503                b_event_remove( df->watch_out );
    551504       
     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
    552519        df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file );
    553520       
     
    566533        dcc_close( file );
    567534}
    568 
    569 /*
    570  * DCC SEND <filename> <IP> <port> <filesize>
    571  *
    572  * filename can be in "" or not. If it is, " can probably be escaped...
    573  * IP can be an unsigned int (IPV4) or something else (IPV6)
    574  *
    575  */
    576 file_transfer_t *dcc_request( struct im_connection *ic, char *line )
    577 {
    578         char *pattern = "SEND"
    579                 " (([^\"][^ ]*)|\"([^\"]|\\\")*\")"
    580                 " (([0-9]*)|([^ ]*))"
    581                 " ([0-9]*)"
    582                 " ([0-9]*)\001";
    583         regmatch_t pmatch[9];
    584         regex_t re;
    585         file_transfer_t *ft;
    586         dcc_file_transfer_t *df;
    587 
    588         if( regcomp( &re, pattern, REG_EXTENDED ) )
    589                 return NULL;
    590         if( regexec( &re, line, 9, pmatch, 0 ) )
    591                 return NULL;
    592 
    593         if( ( pmatch[1].rm_so > 0 ) &&
    594             ( pmatch[4].rm_so > 0 ) &&
    595             ( pmatch[7].rm_so > 0 ) &&
    596             ( pmatch[8].rm_so > 0 ) )
    597         {
    598                 char *input = g_strdup( line );
    599                 char *filename, *host, *port;
    600                 size_t filesize;
    601                 struct addrinfo hints, *rp;
    602 
    603                 /* "filename" or filename */
    604                 if ( pmatch[2].rm_so > 0 )
    605                 {
    606                         input[pmatch[2].rm_eo] = '\0';
    607                         filename = input + pmatch[2].rm_so;
    608                 } else
    609                 {
    610                         input[pmatch[3].rm_eo] = '\0';
    611                         filename = input + pmatch[3].rm_so;
    612                 }
    613                        
    614                 input[pmatch[4].rm_eo] = '\0';
    615 
    616                 /* number means ipv4, something else means ipv6 */
    617                 if ( pmatch[5].rm_so > 0 )
    618                 {
    619                         struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) };
    620                         host = inet_ntoa( ipaddr );
    621                 } else
    622                 {
    623                         /* Contains non-numbers, hopefully an IPV6 address */
    624                         host = input + pmatch[6].rm_so;
    625                 }
    626 
    627                 input[pmatch[7].rm_eo] = '\0';
    628                 input[pmatch[8].rm_eo] = '\0';
    629 
    630                 port = input + pmatch[7].rm_so;
    631                 filesize = atoll( input + pmatch[8].rm_so );
    632 
    633                 memset( &hints, 0, sizeof ( struct addrinfo ) );
    634                 if ( getaddrinfo( host, port, &hints, &rp ) )
    635                 {
    636                         g_free( input );
    637                         return NULL;
    638                 }
    639 
    640                 df = dcc_alloc_transfer( filename, filesize, ic );
    641                 ft = df->ft;
    642                 ft->sending = TRUE;
    643                 memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen );
    644 
    645                 freeaddrinfo( rp );
    646                 g_free( input );
    647 
    648                 df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft );
    649 
    650                 return ft;
    651         }
    652 
    653         return NULL;
    654 }
    655 
Note: See TracChangeset for help on using the changeset viewer.