Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • dcc.c

    r2c2df7d rdce3903  
    2828#include <poll.h>
    2929#include <netinet/tcp.h>
     30#include <regex.h>
    3031
    3132/*
     
    6061unsigned int receivedchunks=0, receiveddata=0;
    6162
    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
    68 int max_packet_size = DCC_PACKET_SIZE;
    69 #else
    7063int max_packet_size = 0;
    71 #endif
    7264
    7365static void dcc_finish( file_transfer_t *file );
     
    7668gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr );
    7769int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
     70gboolean dccs_recv_start( file_transfer_t *ft );
     71gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
     72gboolean dccs_recv_write_request( file_transfer_t *ft );
    7873
    7974/* As defined in ft.h */
     
    9994
    10095/* As defined in ft.h */
    101 gboolean 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... */
    107 file_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);
     96gboolean imcb_file_recv_start( file_transfer_t *ft )
     97{
     98        return dccs_recv_start( ft );
     99}
     100
     101dcc_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);
    119105        file->file_size = file_size;
    120106        file->file_name = g_strdup( file_name );
     
    123109        df->ft = file;
    124110       
     111        return df;
     112}
     113
     114/* This is where the sending magic starts... */
     115file_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 )
     122                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
    125129        /* listen and request */
    126         if( !dcc_listen( df, saddr ) ||
    127             !dccs_send_request( df, user_nick, *saddr ) )
     130        if( !dcc_listen( df, &saddr ) ||
     131            !dccs_send_request( df, user_nick, saddr ) )
    128132                return NULL;
    129133
    130         g_free( *saddr );
     134        g_free( saddr );
    131135
    132136        /* watch */
     
    178182                struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
    179183
    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                  */
    185184                sprintf( ipaddr, "%d",
    186                          htonl( saddr_ipv4->sin_addr.s_addr ) );
     185                         ntohl( saddr_ipv4->sin_addr.s_addr ) );
    187186                port = saddr_ipv4->sin_port;
    188187        } else
     
    209208
    210209        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 );
    214210
    215211        return TRUE;
     
    261257
    262258/*
     259 * Checks poll(), same for receiving and sending
     260 */
     261gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents )
     262{
     263        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
     264
     265        ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" )
     266
     267        if( pfd.revents & POLLERR )
     268        {
     269                int sockerror;
     270                socklen_t errlen = sizeof( sockerror );
     271
     272                if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
     273                        return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" );
     274
     275                return dcc_abort( df, "Socket error: %s", strerror( sockerror ) );
     276        }
     277       
     278        if( pfd.revents & POLLHUP )
     279                return dcc_abort( df, "Remote end closed connection" );
     280       
     281        *revents = pfd.revents;
     282
     283        return TRUE;
     284}
     285
     286/*
     287 * fills max_packet_size with twice the TCP maximum segment size
     288 */
     289gboolean  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/*
    263305 * After setup, the transfer itself is handled entirely by this function.
    264306 * There are basically four things to handle: connect, receive, send, and error.
     
    268310        dcc_file_transfer_t *df = data;
    269311        file_transfer_t *file = df->ft;
    270         struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
    271312        short revents;
    272313       
    273         if ( poll( &pfd, 1, 0 ) == -1 )
    274         {
    275                 imcb_log( df->ic, "poll() failed, weird!" );
    276                 revents = 0;
    277         };
    278 
    279         revents = pfd.revents;
    280 
    281         if( revents & POLLERR )
    282         {
    283                 int sockerror;
    284                 socklen_t errlen = sizeof( sockerror );
    285 
    286                 if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
    287                         return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" );
    288 
    289                 return dcc_abort( df, "Socket error: %s", strerror( sockerror ) );
    290         }
    291        
    292         if( revents & POLLHUP )
    293                 return dcc_abort( df, "Remote end closed connection" );
    294        
     314        if( !dcc_poll( df, fd, &revents) )
     315                return FALSE;
     316
    295317        if( ( revents & POLLIN ) &&
    296318            ( file->status & FT_STATUS_LISTENING ) )
     
    305327                closesocket( fd );
    306328                fd = df->fd;
    307                 file->status = FT_STATUS_TRANSFERING;
     329                file->status = FT_STATUS_TRANSFERRING;
    308330                sock_make_nonblocking( fd );
    309331
    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
     332                if ( !dcc_check_maxseg( df, fd ) )
     333                        return FALSE;
     334
    322335                /* IM protocol callback */
    323 
    324336                if( file->accept )
    325337                        file->accept( file );
     338
    326339                /* reschedule for reading on new fd */
    327340                df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df );
     
    367380                }
    368381       
    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 );
     382                return TRUE;
     383        }
     384
     385        return TRUE;
     386}
     387
     388gboolean 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
     416gboolean 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 ) )
     430                        return FALSE;
     431
     432                //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
     433
     434                df->watch_out = 0;
     435                return FALSE;
     436        }
     437
     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 );
    375461                }
    376 #endif
    377                 return TRUE;
    378         }
    379 
    380         if( revents & POLLOUT )
    381         {
    382                 struct dcc_buffer *dccb;
    383                 int ret;
    384                 char *msg;
    385 
    386                 if( df->queued_bytes == 0 )
    387                 {
    388                         /* shouldn't happen */
    389                         imcb_log( df->ic, "WARNING: DCC SEND: write called with empty queue" );
    390 
    391                         df->watch_out = 0;
     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;
    392472                        return FALSE;
    393473                }
    394474
    395                 /* start where we left off */
    396                 if( !( df->queued_buffers ) ||
    397                     !( dccb = df->queued_buffers->data ) )
    398                         return dcc_abort( df, "BUG in DCC SEND: queued data but no buffers" );
    399 
    400                 msg = dccb->b + df->buffer_pos;
    401 
    402                 int msgsize = MIN(
    403 #ifndef DCC_SEND_AHEAD
    404                                   file->bytes_transferred + MAX_PACKET_SIZE - df->bytes_sent,
    405 #else
    406                                   max_packet_size,
    407 #endif
    408                                   dccb->len - df->buffer_pos );
    409 
    410                 if ( msgsize == 0 )
    411                 {
    412                         df->watch_out = 0;
    413                         return FALSE;
    414                 }
    415 
    416                 ASSERTSOCKOP( ret = send( fd, msg, msgsize, 0 ), "Sending data" );
    417 
    418                 if( ret == 0 )
    419                         return dcc_abort( df, "Remote end closed connection" );
    420 
    421                 df->bytes_sent += ret;
    422                 df->queued_bytes -= ret;
    423                 df->buffer_pos += ret;
    424 
    425                 if( df->buffer_pos == dccb->len )
    426                 {
    427                         df->buffer_pos = 0;
    428                         df->queued_buffers = g_slist_remove( df->queued_buffers, dccb );
    429                         g_free( dccb->b );
    430                         g_free( dccb );
    431                 }
    432 
    433                 if( ( df->queued_bytes < DCC_QUEUE_THRESHOLD_LOW ) && file->out_of_data )
    434                         file->out_of_data( file );
    435        
    436                 if( df->queued_bytes > 0 )
    437                 {
    438                         /* Who knows how long the event loop cycle will take,
    439                          * let's just try to send some more now. */
    440 #ifndef DCC_SEND_AHEAD
    441                         if( df->bytes_sent < ( file->bytes_transferred + max_packet_size ) )
    442 #endif
    443                                 return dccs_send_proto( df, fd, cond );
    444                 }
    445 
    446                 df->watch_out = 0;
     475                df->watch_in = 0;
    447476                return FALSE;
    448477        }
    449478
    450         /* Send buffer full, come back later */
    451 
    452         return TRUE;
     479        return TRUE;
     480}
     481
     482gboolean 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
     494gboolean 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;
    453501}
    454502
    455503/*
    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.
     504 * Incoming data.
    458505 *
    459  * */
    460 gboolean dccs_send_write( file_transfer_t *file, gpointer data, unsigned int data_size )
     506 */
     507gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len )
    461508{
    462509        dcc_file_transfer_t *df = file->priv;
    463         struct dcc_buffer *dccb = g_new0( struct dcc_buffer, 1 );
    464 
    465         receivedchunks++; receiveddata += data_size;
    466 
    467         dccb->b = data;
    468         dccb->len = data_size;
    469 
    470         df->queued_buffers = g_slist_append( df->queued_buffers, dccb );
    471 
    472         df->queued_bytes += data_size;
    473 
    474         if( ( file->status & FT_STATUS_TRANSFERING ) &&
    475 #ifndef DCC_SEND_AHEAD
    476             ( file->bytes_transferred >= df->bytes_sent ) &&
    477 #endif
    478             ( df->watch_out == 0 ) &&
    479             ( df->queued_bytes > 0 ) )
    480         {
    481                 df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_proto, df );
    482         }
    483        
    484         return df->queued_bytes > DCC_QUEUE_THRESHOLD_HIGH;
     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;
    485532}
    486533
     
    503550                b_event_remove( df->watch_out );
    504551       
    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 
    519552        df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file );
    520553       
     
    533566        dcc_close( file );
    534567}
     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 */
     576file_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.