Changeset fa30fa5 for dcc.c


Ignore:
Timestamp:
2007-12-04T00:53:04Z (16 years ago)
Author:
ulim <a.sporto+bee@…>
Branches:
master
Children:
08135df
Parents:
2c2df7d (diff), dce3903 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Jabber file transfer now also with sending! You can't use a proxy yet when
sending, that's my next task. You can use proxies when receiving though!

I also changed the buffering strategy. Previously receiving continued till some
buffer limit was reached, now only one message is received and receiving stops
till it is delivered. This keeps the buffering space per file transfer to a
minimum(currently 4k). Makes sense when used on a public server. For public
servers a throughput maximum would also be interesting...

File:
1 edited

Legend:

Unmodified
Added
Removed
  • dcc.c

    r2c2df7d rfa30fa5  
    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.