Changeset 2ff2076 for dcc.c


Ignore:
Timestamp:
2007-12-03T14:28:45Z (16 years ago)
Author:
ulim <a.sporto+bee@…>
Branches:
master
Children:
dce3903
Parents:
2c2df7d
Message:

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
File:
1 edited

Legend:

Unmodified
Added
Removed
  • dcc.c

    r2c2df7d r2ff2076  
    2828#include <poll.h>
    2929#include <netinet/tcp.h>
     30#include <regex.h>
    3031
    3132/*
     
    7677gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr );
    7778int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
     79gboolean dccs_recv_start( file_transfer_t *ft );
     80gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
     81void dccs_recv_out_of_data( file_transfer_t *ft );
    7882
    7983/* As defined in ft.h */
     
    104108}
    105109
    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);
     110/* As defined in ft.h */
     111gboolean imcb_file_recv_start( file_transfer_t *ft )
     112{
     113        return dccs_recv_start( ft );
     114}
     115
     116dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic )
     117{
     118        file_transfer_t *file = g_new0( file_transfer_t, 1 );
     119        dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1);
    119120        file->file_size = file_size;
    120121        file->file_name = g_strdup( file_name );
     
    123124        df->ft = file;
    124125       
     126        return df;
     127}
     128
     129/* This is where the sending magic starts... */
     130file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size )
     131{
     132        file_transfer_t *file;
     133        dcc_file_transfer_t *df;
     134        struct sockaddr_storage *saddr;
     135
     136        if( file_size > global.conf->max_filetransfer_size )
     137                return NULL;
     138       
     139        df = dcc_alloc_transfer( file_name, file_size, ic );
     140        file = df->ft;
     141
    125142        /* listen and request */
    126         if( !dcc_listen( df, saddr ) ||
    127             !dccs_send_request( df, user_nick, *saddr ) )
     143        if( !dcc_listen( df, &saddr ) ||
     144            !dccs_send_request( df, user_nick, saddr ) )
    128145                return NULL;
    129146
    130         g_free( *saddr );
     147        g_free( saddr );
    131148
    132149        /* watch */
     
    261278
    262279/*
     280 * Checks poll(), same for receiving and sending
     281 */
     282gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents )
     283{
     284        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
     285
     286        ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" )
     287
     288        if( pfd.revents & POLLERR )
     289        {
     290                int sockerror;
     291                socklen_t errlen = sizeof( sockerror );
     292
     293                if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
     294                        return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" );
     295
     296                return dcc_abort( df, "Socket error: %s", strerror( sockerror ) );
     297        }
     298       
     299        if( pfd.revents & POLLHUP )
     300                return dcc_abort( df, "Remote end closed connection" );
     301       
     302        *revents = pfd.revents;
     303
     304        return TRUE;
     305}
     306
     307gboolean  dcc_check_maxseg( dcc_file_transfer_t *df, int fd )
     308{
     309#ifdef DCC_SEND_AHEAD
     310        /*
     311         * use twice the maximum segment size as a maximum for calls to send().
     312         */
     313        if( max_packet_size == 0 )
     314        {
     315                unsigned int mpslen = sizeof( max_packet_size );
     316                if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) )
     317                        return dcc_abort( df, "getsockopt() failed" );
     318                max_packet_size *= 2;
     319        }
     320#endif
     321        return TRUE;
     322}
     323
     324/*
    263325 * After setup, the transfer itself is handled entirely by this function.
    264326 * There are basically four things to handle: connect, receive, send, and error.
     
    268330        dcc_file_transfer_t *df = data;
    269331        file_transfer_t *file = df->ft;
    270         struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
    271332        short revents;
    272333       
    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        
     334        if( !dcc_poll( df, fd, &revents) )
     335                return FALSE;
     336
    295337        if( ( revents & POLLIN ) &&
    296338            ( file->status & FT_STATUS_LISTENING ) )
     
    305347                closesocket( fd );
    306348                fd = df->fd;
    307                 file->status = FT_STATUS_TRANSFERING;
     349                file->status = FT_STATUS_TRANSFERRING;
    308350                sock_make_nonblocking( fd );
    309351
    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
     352                if ( !dcc_check_maxseg( df, fd ) )
     353                        return FALSE;
     354
    322355                /* IM protocol callback */
    323356
     
    453486}
    454487
     488gboolean dccs_recv_start( file_transfer_t *ft )
     489{
     490        dcc_file_transfer_t *df = ft->priv;
     491        struct sockaddr_storage *saddr = &df->saddr;
     492        int fd;
     493        socklen_t sa_len = saddr->ss_family == AF_INET ?
     494                sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
     495       
     496        if( !ft->write )
     497                return dcc_abort( df, "Protocol didn't register write()" );
     498       
     499        ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" );
     500
     501        sock_make_nonblocking( fd );
     502
     503        if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) &&
     504            ( errno != EINPROGRESS ) )
     505                return dcc_abort( df, "Connecting" );
     506
     507        ft->status = FT_STATUS_CONNECTING;
     508
     509        /* watch */
     510        df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df );
     511        ft->out_of_data = dccs_recv_out_of_data;
     512
     513        return TRUE;
     514}
     515
     516gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond)
     517{
     518        dcc_file_transfer_t *df = data;
     519        file_transfer_t *ft = df->ft;
     520        short revents;
     521
     522        if( !dcc_poll( df, fd, &revents ) )
     523                return FALSE;
     524       
     525        if( ( revents & POLLOUT ) &&
     526            ( ft->status & FT_STATUS_CONNECTING ) )
     527        {
     528                ft->status = FT_STATUS_TRANSFERRING;
     529                if ( !dcc_check_maxseg( df, fd ) )
     530                        return FALSE;
     531
     532                //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
     533
     534                df->watch_out = 0;
     535                return FALSE;
     536        }
     537
     538        if( revents & POLLIN )
     539        {
     540                char *buffer = g_malloc( 65536 );
     541                int ret, done;
     542
     543                ASSERTSOCKOP( ret = recv( fd, buffer, 65536, 0 ), "Receiving" );
     544
     545                if( ret == 0 )
     546                        return dcc_abort( df, "Remote end closed connection" );
     547
     548                buffer = g_realloc( buffer, ret );
     549
     550                df->bytes_sent += ret;
     551
     552                done = df->bytes_sent >= ft->file_size;
     553
     554                if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) ||
     555                    done )
     556                {
     557                        int ack, ackret;
     558                        ack = htonl( ft->bytes_transferred = df->bytes_sent );
     559
     560                        ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" );
     561                       
     562                        if ( ackret != 4 )
     563                                return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ret );
     564                }
     565               
     566                if( !ft->write( df->ft, buffer, ret ) && !done )
     567                {
     568                        df->watch_in = 0;
     569                        return FALSE;
     570                }
     571
     572                if( done )
     573                {
     574                        closesocket( fd );
     575                        dcc_finish( ft );
     576
     577                        df->watch_in = 0;
     578                        return FALSE;
     579                }
     580
     581                return TRUE;
     582        }
     583
     584        return TRUE;
     585}
     586
     587void dccs_recv_out_of_data( file_transfer_t *ft )
     588{
     589        dcc_file_transfer_t *df = ft->priv;
     590
     591        if( !df->watch_in )
     592                df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
     593}
     594
    455595/*
    456596 * Incoming data. Note that the buffer MUST NOT be freed by the caller!
     
    472612        df->queued_bytes += data_size;
    473613
    474         if( ( file->status & FT_STATUS_TRANSFERING ) &&
     614        if( ( file->status & FT_STATUS_TRANSFERRING ) &&
    475615#ifndef DCC_SEND_AHEAD
    476616            ( file->bytes_transferred >= df->bytes_sent ) &&
     
    533673        dcc_close( file );
    534674}
     675
     676/*
     677 * DCC SEND <filename> <IP> <port> <filesize>
     678 *
     679 * filename can be in "" or not. If it is, " can probably be escaped...
     680 * IP can be an unsigned int (IPV4) or something else (IPV6)
     681 *
     682 */
     683file_transfer_t *dcc_request( struct im_connection *ic, char *line )
     684{
     685        char *pattern = "SEND"
     686                " (([^\"][^ ]*)|\"([^\"]|\\\")*\")"
     687                " (([0-9]*)|([^ ]*))"
     688                " ([0-9]*)"
     689                " ([0-9]*)\001";
     690        regmatch_t pmatch[9];
     691        regex_t re;
     692        file_transfer_t *ft;
     693        dcc_file_transfer_t *df;
     694
     695        if( regcomp( &re, pattern, REG_EXTENDED ) )
     696                return NULL;
     697        if( regexec( &re, line, 9, pmatch, 0 ) )
     698                return NULL;
     699
     700        if( ( pmatch[1].rm_so > 0 ) &&
     701            ( pmatch[4].rm_so > 0 ) &&
     702            ( pmatch[7].rm_so > 0 ) &&
     703            ( pmatch[8].rm_so > 0 ) )
     704        {
     705                char *input = g_strdup( line );
     706                char *filename, *host, *port;
     707                size_t filesize;
     708                struct addrinfo hints, *rp;
     709
     710                /* "filename" or filename */
     711                if ( pmatch[2].rm_so > 0 )
     712                {
     713                        input[pmatch[2].rm_eo] = '\0';
     714                        filename = input + pmatch[2].rm_so;
     715                } else
     716                {
     717                        input[pmatch[3].rm_eo] = '\0';
     718                        filename = input + pmatch[3].rm_so;
     719                }
     720                       
     721                input[pmatch[4].rm_eo] = '\0';
     722
     723                /* number means ipv4, something else means ipv6 */
     724                if ( pmatch[5].rm_so > 0 )
     725                {
     726                        struct in_addr ipaddr = { htonl( atoi( input + pmatch[5].rm_so ) ) };
     727                        host = inet_ntoa( ipaddr );
     728                } else
     729                {
     730                        /* Contains non-numbers, hopefully an IPV6 address */
     731                        host = input + pmatch[6].rm_so;
     732                }
     733
     734                input[pmatch[7].rm_eo] = '\0';
     735                input[pmatch[8].rm_eo] = '\0';
     736
     737                port = input + pmatch[7].rm_so;
     738                filesize = atoll( input + pmatch[8].rm_so );
     739
     740                memset( &hints, 0, sizeof ( struct addrinfo ) );
     741                if ( getaddrinfo( host, port, &hints, &rp ) )
     742                {
     743                        g_free( input );
     744                        return NULL;
     745                }
     746
     747                df = dcc_alloc_transfer( filename, filesize, ic );
     748                ft = df->ft;
     749                ft->sending = TRUE;
     750                memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen );
     751
     752                freeaddrinfo( rp );
     753                g_free( input );
     754
     755                df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft );
     756
     757                return ft;
     758        }
     759
     760        return NULL;
     761}
     762
Note: See TracChangeset for help on using the changeset viewer.