source: protocols/jabber/s5bytestream.c @ 2625d6d

Last change on this file since 2625d6d was 2625d6d, checked in by ulim <a.sporto+bee@…>, at 2008-02-20T20:55:38Z

some fixes related to connection timeout

  • Property mode set to 100644
File size: 31.1 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - SOCKS5 Bytestreams ( XEP-0065 )                          *
5*                                                                           *
6*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
25#include "sha1.h"
26#include <poll.h>
27
28struct bs_transfer {
29
30        struct jabber_transfer *tf;
31
32        jabber_streamhost_t *sh;
33        GSList *streamhosts;
34
35        enum 
36        { 
37                BS_PHASE_CONNECT, 
38                BS_PHASE_CONNECTED, 
39                BS_PHASE_REQUEST, 
40                BS_PHASE_REPLY
41        } phase;
42
43        /* SHA1( SID + Initiator JID + Target JID) */
44        char *pseudoadr;
45
46        gint connect_timeout;
47};
48
49struct socks5_message
50{
51        unsigned char ver;
52        union
53        {
54                unsigned char cmd;
55                unsigned char rep;
56        } cmdrep;
57        unsigned char rsv;
58        unsigned char atyp;
59        unsigned char addrlen;
60        unsigned char address[40];
61        in_port_t port;
62} __attribute__ ((packed)); 
63
64/* connect() timeout in seconds. */
65#define JABBER_BS_CONTIMEOUT 15
66/* listen timeout */
67#define JABBER_BS_LISTEN_TIMEOUT  90
68
69/* very useful */
70#define ASSERTSOCKOP(op, msg) \
71        if( (op) == -1 ) \
72                return jabber_bs_abort( bt , msg ": %s", strerror( errno ) );
73
74gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... );
75void jabber_bs_canceled( file_transfer_t *ft , char *reason );
76void jabber_bs_free_transfer( file_transfer_t *ft );
77gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond );
78gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents );
79gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen );
80
81void jabber_bs_recv_answer_request( struct bs_transfer *bt );
82gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond );
83gboolean jabber_bs_recv_write_request( file_transfer_t *ft );
84gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond );
85gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error );
86int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode );
87
88gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error );
89gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts );
90gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond );
91gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port );
92static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
93void jabber_bs_send_activate( struct bs_transfer *bt );
94
95/*
96 * Frees a bs_transfer struct and calls the SI free function
97 */
98void jabber_bs_free_transfer( file_transfer_t *ft) {
99        struct jabber_transfer *tf = ft->data;
100        struct bs_transfer *bt = tf->streamhandle;
101        jabber_streamhost_t *sh;
102
103        if ( bt->connect_timeout )
104        {
105                b_event_remove( bt->connect_timeout );
106                bt->connect_timeout = 0;
107        }
108
109        if ( tf->watch_in )
110                b_event_remove( tf->watch_in );
111       
112        if( tf->watch_out )
113                b_event_remove( tf->watch_out );
114       
115        g_free( bt->pseudoadr );
116
117        while( bt->streamhosts )
118        {
119                sh = bt->streamhosts->data;
120                bt->streamhosts = g_slist_remove( bt->streamhosts, sh );
121                g_free( sh->jid );
122                g_free( sh->host );
123                g_free( sh );
124        }
125       
126        g_free( bt );
127
128        jabber_si_free_transfer( ft );
129}
130
131/*
132 * Checks if buflen data is available on the socket and
133 * writes it to buffer if that's the case.
134 */
135gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen )
136{
137        int ret;
138        int fd = bt->tf->fd;
139
140        ASSERTSOCKOP( ret = recv( fd, buffer, buflen, MSG_PEEK ), "MSG_PEEK'ing" );
141
142        if( ret == 0 )
143                return jabber_bs_abort( bt, "Remote end closed connection" );
144               
145        if( ret < buflen )
146                return ret;
147
148        ASSERTSOCKOP( ret = recv( fd, buffer, buflen, 0 ), "Dequeuing after MSG_PEEK" );
149
150        if( ret != buflen )
151                return jabber_bs_abort( bt, "recv returned less than previous recv with MSG_PEEK" );
152       
153        return ret;
154}
155
156
157/*
158 * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect().
159 */
160gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond )
161{
162        struct bs_transfer *bt = data;
163
164        bt->connect_timeout = 0;
165
166        jabber_bs_abort( bt, "no connection after %d seconds", bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT );
167
168        return FALSE;
169}
170
171/*
172 * Polls the socket, checks for errors and removes a connect timer
173 * if there is one.
174 */
175gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents )
176{
177        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR };
178       
179        if ( bt->connect_timeout )
180        {
181                b_event_remove( bt->connect_timeout );
182                bt->connect_timeout = 0;
183        }
184
185        ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" )
186
187        if( pfd.revents & POLLERR )
188        {
189                int sockerror;
190                socklen_t errlen = sizeof( sockerror );
191
192                if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
193                        return jabber_bs_abort( bt, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" );
194
195                if ( bt->phase == BS_PHASE_CONNECTED )
196                        return jabber_bs_abort( bt, "connect failed: %s", strerror( sockerror ) );
197
198                return jabber_bs_abort( bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) );
199        }
200
201        if( pfd.revents & POLLHUP )
202                return jabber_bs_abort( bt, "Remote end closed connection" );
203       
204        *revents = pfd.revents;
205       
206        return TRUE;
207}
208
209/*
210 * Used for receive and send path.
211 */
212gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... )
213{
214        va_list params;
215        va_start( params, format );
216        char error[128];
217
218        if( vsnprintf( error, 128, format, params ) < 0 )
219                sprintf( error, "internal error parsing error string (BUG)" );
220        va_end( params );
221        if( bt->tf->ft->sending )
222                return jabber_bs_send_handshake_abort( bt, error );
223        else
224                return jabber_bs_recv_handshake_abort( bt, error );
225}
226
227/* Bad luck */
228void jabber_bs_canceled( file_transfer_t *ft , char *reason )
229{
230        struct jabber_transfer *tf = ft->data;
231
232        imcb_log( tf->ic, "File transfer aborted: %s", reason );
233}
234
235/*
236 * Parses an incoming bytestream request and calls jabber_bs_handshake on success.
237 */
238int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode)
239{
240        char *sid, *ini_jid, *tgt_jid, *mode, *iq_id;
241        struct jabber_data *jd = ic->proto_data;
242        struct jabber_transfer *tf = NULL;
243        GSList *tflist;
244        struct bs_transfer *bt;
245        GSList *shlist=NULL;
246        struct xt_node *shnode;
247
248        sha1_state_t sha;
249        char hash_hex[41];
250        unsigned char hash[20];
251        int i;
252       
253        if( !(iq_id   = xt_find_attr( node, "id" ) ) ||
254            !(ini_jid = xt_find_attr( node, "from" ) ) ||
255            !(tgt_jid = xt_find_attr( node, "to" ) ) ||
256            !(sid     = xt_find_attr( qnode, "sid" ) ) )
257        {
258                imcb_log( ic, "WARNING: Received incomplete SI bytestream request");
259                return XT_HANDLED;
260        }
261
262        if( ( mode = xt_find_attr( qnode, "mode" ) ) &&
263              ( strcmp( mode, "tcp" ) != 0 ) ) 
264        {
265                imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) );
266                return XT_HANDLED;
267        }
268
269        shnode = qnode->children;
270        while( ( shnode = xt_find_node( shnode, "streamhost" ) ) )
271        {
272                char *jid, *host;
273                int port;
274                if( ( jid = xt_find_attr( shnode, "jid" ) ) &&
275                    ( host = xt_find_attr( shnode, "host" ) ) &&
276                    ( ( port = atoi( xt_find_attr( shnode, "port" ) ) ) ) )
277                {
278                        jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 );
279                        sh->jid = g_strdup(jid);
280                        sh->host = g_strdup(host);
281                        sprintf( sh->port, "%u", port );
282                        shlist = g_slist_append( shlist, sh );
283                }
284                shnode = shnode->next;
285        }
286       
287        if( !shlist )
288        {
289                imcb_log( ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries");
290                return XT_HANDLED;
291        }
292
293        /* Let's see if we can find out what this bytestream should be for... */
294
295        for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) )
296        {
297                struct jabber_transfer *tft = tflist->data;
298                if( ( strcmp( tft->sid, sid ) == 0 ) &&
299                    ( strcmp( tft->ini_jid, ini_jid ) == 0 ) &&
300                    ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) )
301                {
302                        tf = tft;
303                        break;
304                }
305        }
306
307        if (!tf) 
308        {
309                imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid );
310                return XT_HANDLED;
311        }
312
313        /* iq_id and canceled can be reused since SI is done */
314        g_free( tf->iq_id );
315        tf->iq_id = g_strdup( iq_id );
316
317        tf->ft->canceled = jabber_bs_canceled;
318
319        /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */
320        sha1_init( &sha );
321        sha1_append( &sha, (unsigned char*) sid, strlen( sid ) );
322        sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) );
323        sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) );
324        sha1_finish( &sha, hash );
325       
326        for( i = 0; i < 20; i ++ )
327                sprintf( hash_hex + i * 2, "%02x", hash[i] );
328               
329        bt = g_new0( struct bs_transfer, 1 );
330        bt->tf = tf;
331        bt->streamhosts = shlist;
332        bt->sh = shlist->data;
333        bt->phase = BS_PHASE_CONNECT;
334        bt->pseudoadr = g_strdup( hash_hex );
335        tf->streamhandle = bt;
336        tf->ft->free = jabber_bs_free_transfer;
337
338        jabber_bs_recv_handshake( bt, 0, 0 ); 
339
340        return XT_HANDLED;
341}
342
343/*
344 * This is what a protocol handshake can look like in cooperative multitasking :)
345 * Might be confusing at first because it's called from different places and is recursing.
346 * (places being the event thread, bs_request, bs_handshake_abort, and itself)
347 *
348 * All in all, it turned out quite nice :)
349 */
350gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond )
351{
352
353        struct bs_transfer *bt = data;
354        short revents;
355
356        if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) )
357                return FALSE;
358       
359        switch( bt->phase ) 
360        {
361        case BS_PHASE_CONNECT:
362                {
363                        struct addrinfo hints, *rp;
364
365                        memset( &hints, 0, sizeof( struct addrinfo ) );
366                        hints.ai_socktype = SOCK_STREAM;
367
368                        if ( getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) != 0 )
369                                return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) );
370
371                        ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" );
372
373                        sock_make_nonblocking( fd );
374
375                        imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host, bt->sh->port );
376
377                        if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) &&
378                            ( errno != EINPROGRESS ) )
379                                return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) );
380
381                        freeaddrinfo( rp );
382
383                        bt->phase = BS_PHASE_CONNECTED;
384                       
385                        bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt );
386
387                        /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */
388                        bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt );
389
390                        bt->tf->watch_in = 0;
391                        return FALSE;
392                }
393        case BS_PHASE_CONNECTED:
394                {
395                        struct {
396                                unsigned char ver;
397                                unsigned char nmethods;
398                                unsigned char method;
399                        } socks5_hello = {
400                                .ver = 5,
401                                .nmethods = 1,
402                                .method = 0x00 /* no auth */
403                                /* one could also implement username/password. If you know
404                                 * a jabber client or proxy that actually does it, tell me.
405                                 */
406                        };
407                       
408                        ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" );
409
410                        bt->phase = BS_PHASE_REQUEST;
411
412                        bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt );
413
414                        bt->tf->watch_out = 0;
415                        return FALSE;
416                }
417        case BS_PHASE_REQUEST:
418                {
419                        struct socks5_message socks5_connect = 
420                        {
421                                .ver = 5,
422                                .cmdrep.cmd = 0x01,
423                                .rsv = 0,
424                                .atyp = 0x03,
425                                .addrlen = strlen( bt->pseudoadr ),
426                                .port = 0
427                        };
428                        int ret;
429                        char buf[2];
430
431                        /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */
432                        ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" );
433
434                        if( !( ret == 2 ) ||
435                            !( buf[0] == 5 ) ||
436                            !( buf[1] == 0 ) )
437                                return jabber_bs_abort( bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)",
438                                                                        ret, buf[0], buf[1] );
439
440                        /* copy hash into connect message */
441                        memcpy( socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen );
442
443                        ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_message ), 0 ) , "Sending SOCKS5 Connect" );
444
445                        bt->phase = BS_PHASE_REPLY;
446
447                        return TRUE;
448                }
449        case BS_PHASE_REPLY:
450                {
451                        struct socks5_message socks5_reply;
452                        int ret;
453
454                        if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) )
455                                return FALSE;
456
457                        if ( ret < sizeof( socks5_reply ) )
458                                return TRUE;
459
460                        if( !( socks5_reply.ver == 5 ) ||
461                            !( socks5_reply.cmdrep.rep == 0 ) ||
462                            !( socks5_reply.atyp == 3 ) ||
463                            !( socks5_reply.addrlen == 40 ) )
464                                return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", 
465                                        socks5_reply.ver,
466                                        socks5_reply.cmdrep.rep,
467                                        socks5_reply.atyp,
468                                        socks5_reply.addrlen);
469
470                        if( bt->tf->ft->sending )
471                                jabber_bs_send_activate( bt );
472                        else
473                                jabber_bs_recv_answer_request( bt );
474
475                        return FALSE;
476                }
477        default:
478                /* BUG */
479                imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" );
480
481                bt->tf->watch_in = 0;
482                return FALSE;
483        }
484}
485
486/*
487 * If the handshake failed we can try the next streamhost, if there is one.
488 * An intelligent sender would probably specify himself as the first streamhost and
489 * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
490 * slow proxy is only used if neccessary. This of course also means, that the timeout
491 * per streamhost should be kept short. If one or two firewalled adresses are specified,
492 * they have to timeout first before a proxy is tried.
493 */
494gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error )
495{
496        struct jabber_transfer *tf = bt->tf;
497        struct xt_node *reply, *iqnode;
498        GSList *shlist;
499
500        imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", 
501                  tf->ft->file_name, 
502                  bt->sh->host,
503                  bt->sh->port,
504                  error );
505
506        /* Alright, this streamhost failed, let's try the next... */
507        bt->phase = BS_PHASE_CONNECT;
508        shlist = g_slist_find( bt->streamhosts, bt->sh );
509        if( shlist && shlist->next )
510        {
511                bt->sh = shlist->next->data;
512                return jabber_bs_recv_handshake( bt, 0, 0 );
513        }
514
515
516        /* out of stream hosts */
517
518        iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL );
519        reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" );
520        xt_free_node( iqnode );
521
522        xt_add_attr( reply, "id", tf->iq_id );
523               
524        if( !jabber_write_packet( tf->ic, reply ) )
525                imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" );
526        xt_free_node( reply );
527
528        imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" );
529
530        bt->tf->watch_in = 0;
531        return FALSE;
532}
533
534/*
535 * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose.
536 * If he is the streamhost himself, he might already know that. However, if it's a proxy,
537 * the initiator will have to make a connection himself.
538 */
539void jabber_bs_recv_answer_request( struct bs_transfer *bt )
540{
541        struct jabber_transfer *tf = bt->tf;
542        struct xt_node *reply;
543
544        imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s", 
545                  tf->ft->file_name, 
546                  bt->sh->host,
547                  bt->sh->port );
548
549        tf->ft->data = tf;
550        tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt );
551        tf->ft->write_request = jabber_bs_recv_write_request;
552
553        reply = xt_new_node( "streamhost-used", NULL, NULL );
554        xt_add_attr( reply, "jid", bt->sh->jid );
555
556        reply = xt_new_node( "query", NULL, reply );
557        xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS );
558
559        reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply );
560
561        xt_add_attr( reply, "id", tf->iq_id );
562               
563        if( !jabber_write_packet( tf->ic, reply ) )
564                imcb_file_canceled( tf->ft, "Error transmitting bytestream response" );
565        xt_free_node( reply );
566}
567
568/*
569 * This function is called from write_request directly. If no data is available, it will install itself
570 * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again.
571 */
572gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond )
573{
574        int ret;
575        struct bs_transfer *bt = data;
576        struct jabber_transfer *tf = bt->tf;
577
578        if( fd != 0 ) /* called via event thread */
579        {
580                tf->watch_in = 0;
581                ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" );
582        }
583        else
584        {
585                /* called directly. There might not be any data available. */
586                if( ( ( ret = recv( tf->fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) ) == -1 ) &&
587                    ( errno != EAGAIN ) )
588                    return jabber_bs_abort( bt, "Receiving: %s", strerror( errno ) );
589
590                if( ( ret == -1 ) && ( errno == EAGAIN ) )
591                {
592                        tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt );
593                        return FALSE;
594                }
595        }
596
597        /* shouldn't happen since we know the file size */
598        if( ret == 0 )
599                return jabber_bs_abort( bt, "Remote end closed connection" );
600       
601        if( tf->bytesread == 0 )
602                tf->ft->started = time( NULL );
603
604        tf->bytesread += ret;
605
606        tf->ft->write( tf->ft, tf->ft->buffer, ret );   
607
608        return FALSE;
609}
610
611/*
612 * imc callback that is invoked when it is ready to receive some data.
613 */
614gboolean jabber_bs_recv_write_request( file_transfer_t *ft )
615{
616        struct jabber_transfer *tf = ft->data;
617
618        if( tf->watch_in )
619        {
620                imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" );
621                return FALSE;
622        }
623       
624        jabber_bs_recv_read( tf->streamhandle, 0 , 0 );
625
626        return TRUE;
627}
628
629/*
630 * Issues a write_request to imc.
631 * */
632gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond )
633{
634        struct bs_transfer *bt = data;
635
636        bt->tf->watch_out = 0;
637
638        bt->tf->ft->write_request( bt->tf->ft );
639
640        return FALSE;
641}
642
643/*
644 * This should only be called if we can write, so just do it.
645 * Add a write watch so we can write more during the next cycle (if possible).
646 */
647gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len )
648{
649        struct jabber_transfer *tf = ft->data;
650        struct bs_transfer *bt = tf->streamhandle;
651        int ret;
652
653        if( tf->watch_out )
654                return jabber_bs_abort( bt, "BUG: write() called while watching " );
655       
656        /* TODO: catch broken pipe */
657        ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" );
658
659        if( tf->byteswritten == 0 )
660                tf->ft->started = time( NULL );
661
662        tf->byteswritten += ret;
663       
664        /* TODO: this should really not be fatal */
665        if( ret < len )
666                return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len );
667
668        bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt );
669               
670        return TRUE;
671}
672
673/*
674 * Handles the reply by the receiver containing the used streamhost.
675 */
676static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) {
677        struct jabber_transfer *tf = NULL;
678        struct jabber_data *jd = ic->proto_data;
679        struct bs_transfer *bt;
680        GSList *tflist;
681        struct xt_node *c;
682        char *sid, *jid;
683
684        if( !( c = xt_find_node( node->children, "query" ) ) ||
685            !( c = xt_find_node( c->children, "streamhost-used" ) ) ||
686            !( jid = xt_find_attr( c, "jid" ) ) )
687
688        {
689                imcb_log( ic, "WARNING: Received incomplete bytestream reply" );
690                return XT_HANDLED;
691        }
692       
693        if( !( c = xt_find_node( orig->children, "query" ) ) ||
694            !( sid = xt_find_attr( c, "sid" ) ) )
695        {
696                imcb_log( ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply" );
697                return XT_HANDLED;
698        }
699
700        /* Let's see if we can find out what this bytestream should be for... */
701
702        for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) )
703        {
704                struct jabber_transfer *tft = tflist->data;
705                if( ( strcmp( tft->sid, sid ) == 0 ) )
706                {
707                        tf = tft;
708                        break;
709                }
710        }
711
712        if( !tf )
713        {
714                imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply to unknown request" );
715                return XT_HANDLED;
716        }
717
718        bt = tf->streamhandle;
719
720        tf->accepted = TRUE;
721
722        if( strcmp( jid, tf->ini_jid ) == 0 )
723        {
724                /* we're streamhost and target */
725                if( bt->phase == BS_PHASE_REPLY )
726                {
727                        /* handshake went through, let's start transferring */
728                        tf->ft->write_request( tf->ft );
729                }
730        } else
731        {
732                /* using a proxy, abort listen */
733
734                closesocket( tf->fd );
735                tf->fd = 0;
736
737                if ( bt->connect_timeout )
738                {
739                        b_event_remove( bt->connect_timeout );
740                        bt->connect_timeout = 0;
741                }
742
743                GSList *shlist;
744                for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) )
745                {
746                        jabber_streamhost_t *sh = shlist->data;
747                        if( strcmp( sh->jid, jid ) == 0 )
748                        {
749                                bt->sh = sh;
750                                jabber_bs_recv_handshake( bt, 0, 0 );
751                                return XT_HANDLED;
752                        }
753                }
754
755                imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid );
756        }
757
758        return XT_HANDLED;
759}
760
761/*
762 * Tell the proxy to activate the stream. Looks like this:
763 *
764 * <iq type=set>
765 *      <query xmlns=bs sid=sid>
766 *              <activate>tgt_jid</activate>
767 *      </query>
768 * </iq>
769 */
770void jabber_bs_send_activate( struct bs_transfer *bt )
771{
772        struct xt_node *node;
773
774        node = xt_new_node( "activate", bt->tf->tgt_jid, NULL );
775        node = xt_new_node( "query", NULL, node );
776        xt_add_attr( node, "xmlns", XMLNS_BYTESTREAMS );
777        xt_add_attr( node, "sid", bt->tf->sid );
778        node = jabber_make_packet( "iq", "set", bt->sh->jid, node );
779
780        jabber_cache_add( bt->tf->ic, node, jabber_bs_send_handle_activate );
781
782        jabber_write_packet( bt->tf->ic, node );
783}
784
785/*
786 * The proxy has activated the bytestream.
787 * We can finally start pushing some data out.
788 */
789static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
790{
791        char *sid;
792        GSList *tflist;
793        struct jabber_transfer *tf;
794        struct xt_node *query;
795        struct jabber_data *jd = ic->proto_data;
796
797        query = xt_find_node( orig->children, "query" );
798        sid = xt_find_attr( query, "sid" );
799
800        for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) )
801        {
802                struct jabber_transfer *tft = tflist->data;
803                if( ( strcmp( tft->sid, sid ) == 0 ) )
804                {
805                        tf = tft;
806                        break;
807                }
808        }
809
810        if( !tf )
811        {
812                imcb_log( ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream" );
813                return XT_HANDLED;
814        }
815
816        /* handshake went through, let's start transferring */
817        tf->ft->write_request( tf->ft );
818
819        return XT_HANDLED;
820}
821
822/*
823 * Starts a bytestream.
824 */
825gboolean jabber_bs_send_start( struct jabber_transfer *tf )
826{
827        char host[INET6_ADDRSTRLEN];
828        struct bs_transfer *bt;
829        sha1_state_t sha;
830        char hash_hex[41];
831        unsigned char hash[20];
832        int i,ret;
833        struct jabber_data *jd = tf->ic->proto_data;
834        jabber_streamhost_t sh;
835        GSList *streamhosts = jd->streamhosts;
836
837        /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */
838        sha1_init( &sha );
839        sha1_append( &sha, (unsigned char*) tf->sid, strlen( tf->sid ) );
840        sha1_append( &sha, (unsigned char*) tf->ini_jid, strlen( tf->ini_jid ) );
841        sha1_append( &sha, (unsigned char*) tf->tgt_jid, strlen( tf->tgt_jid ) );
842        sha1_finish( &sha, hash );
843       
844        for( i = 0; i < 20; i ++ )
845                sprintf( hash_hex + i * 2, "%02x", hash[i] );
846               
847        bt = g_new0( struct bs_transfer, 1 );
848        bt->tf = tf;
849        bt->phase = BS_PHASE_CONNECT;
850        bt->pseudoadr = g_strdup( hash_hex );
851        tf->streamhandle = bt;
852        tf->ft->free = jabber_bs_free_transfer;
853        tf->ft->canceled = jabber_bs_canceled;
854
855        if ( !jabber_bs_send_listen( bt, &tf->saddr, sh.host = host, sh.port ) )
856                return FALSE;
857
858        bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt );
859        bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt );
860
861        sh.jid = tf->ini_jid;
862
863        /* temporarily add listen address to streamhosts, send the request and remove it */
864        streamhosts = g_slist_prepend( streamhosts, &sh );
865        ret = jabber_bs_send_request( tf, streamhosts);
866        streamhosts = g_slist_remove( streamhosts, &sh );
867
868        return ret;
869}
870
871gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts )
872{
873        struct xt_node *shnode, *query, *iq;
874
875        query = xt_new_node( "query", NULL, NULL );
876        xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS );
877        xt_add_attr( query, "sid", tf->sid );
878        xt_add_attr( query, "mode", "tcp" );
879
880        while( streamhosts ) {
881                jabber_streamhost_t *sh = streamhosts->data;
882                shnode = xt_new_node( "streamhost", NULL, NULL );
883                xt_add_attr( shnode, "jid", sh->jid );
884                xt_add_attr( shnode, "host", sh->host );
885                xt_add_attr( shnode, "port", sh->port );
886
887                xt_add_child( query, shnode );
888
889                streamhosts = g_slist_next( streamhosts );
890        }
891
892
893        iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query );
894        xt_add_attr( iq, "from", tf->ini_jid );
895
896        jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply );
897
898        if( !jabber_write_packet( tf->ic, iq ) )
899                imcb_file_canceled( tf->ft, "Error transmitting bytestream request" );
900        return TRUE;
901}
902
903gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error )
904{
905        struct jabber_transfer *tf = bt->tf;
906        struct jabber_data *jd = tf->ic->proto_data;
907
908        /* TODO: did the receiver get here somehow??? */
909        imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", 
910                  tf->ft->file_name, 
911                  error );
912
913        if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */
914                imcb_file_canceled( tf->ft, error );
915
916        return FALSE;
917}
918
919/*
920 * Creates a listening socket and returns it in saddr_ptr.
921 */
922gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port )
923{
924        struct jabber_transfer *tf = bt->tf;
925        int fd;
926        char hostname[ HOST_NAME_MAX + 1 ];
927        struct addrinfo hints, *rp;
928        socklen_t ssize = sizeof( struct sockaddr_storage );
929
930        /* won't be long till someone asks for this to be configurable :) */
931
932        ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" );
933
934        memset( &hints, 0, sizeof( struct addrinfo ) );
935        hints.ai_socktype = SOCK_STREAM;
936        hints.ai_flags = AI_NUMERICSERV;
937
938        if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 )
939                return jabber_bs_abort( bt, "getaddrinfo()" );
940
941        memcpy( saddr, rp->ai_addr, rp->ai_addrlen );
942
943        ASSERTSOCKOP( fd = tf->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" );
944
945        ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" );
946       
947        freeaddrinfo( rp );
948
949        ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" );
950
951        if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ?
952                        ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr
953                        , host, INET6_ADDRSTRLEN ) )
954                return jabber_bs_abort( bt, "inet_ntop failed on listening socket" );
955
956        ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" );
957
958        if( saddr->ss_family == AF_INET )
959                sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) );
960        else
961                sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) );
962
963        return TRUE;
964}
965
966/*
967 * SOCKS5BYTESTREAM protocol for the sender
968 */
969gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond )
970{
971        struct bs_transfer *bt = data;
972        struct jabber_transfer *tf = bt->tf;
973        short revents;
974
975        if ( !jabber_bs_poll( bt, fd, &revents ) )
976                return FALSE;
977       
978        switch( bt->phase ) 
979        {
980        case BS_PHASE_CONNECT:
981                {
982                        struct sockaddr_storage clt_addr;
983                        socklen_t ssize = sizeof( clt_addr );
984                       
985                        /* Connect */
986
987                        ASSERTSOCKOP( tf->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" );
988
989                        closesocket( fd );
990                        fd = tf->fd;
991                        sock_make_nonblocking( fd );
992                       
993                        bt->phase = BS_PHASE_CONNECTED;
994
995                        bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt );
996                        return FALSE;
997                }
998        case BS_PHASE_CONNECTED:
999                {
1000                        int ret, have_noauth=FALSE;
1001                        struct {
1002                                unsigned char ver;
1003                                unsigned char method;
1004                        } socks5_auth_reply = { .ver = 5, .method = 0 };
1005                        struct {
1006                                unsigned char ver;
1007                                unsigned char nmethods;
1008                                unsigned char method;
1009                        } socks5_hello;
1010
1011                        if( !( ret = jabber_bs_peek( bt, &socks5_hello, sizeof( socks5_hello ) ) ) )
1012                                return FALSE;
1013
1014                        if( ret < sizeof( socks5_hello ) )
1015                                return TRUE;
1016
1017                        if( !( socks5_hello.ver == 5 ) ||
1018                            !( socks5_hello.nmethods >= 1 ) ||
1019                            !( socks5_hello.nmethods < 32 ) )
1020                                return jabber_bs_abort( bt, "Invalid auth request ver=%d nmethods=%d method=%d", socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method );
1021
1022                        have_noauth = socks5_hello.method == 0;
1023
1024                        if( socks5_hello.nmethods > 1 )
1025                        {
1026                                char mbuf[32];
1027                                int i;
1028                                ASSERTSOCKOP( ret = recv( fd, mbuf, socks5_hello.nmethods - 1, 0 ) , "Receiving auth methods" );
1029                                if( ret < ( socks5_hello.nmethods - 1 ) )
1030                                        return jabber_bs_abort( bt, "Partial auth request");
1031                                for( i = 0 ; !have_noauth && ( i < socks5_hello.nmethods - 1 ) ; i ++ )
1032                                        if( mbuf[i] == 0 )
1033                                                have_noauth = TRUE;
1034                        }
1035                       
1036                        if( !have_noauth )
1037                                return jabber_bs_abort( bt, "Auth request didn't include no authentication" );
1038
1039                        ASSERTSOCKOP( send( fd, &socks5_auth_reply, sizeof( socks5_auth_reply ) , 0 ), "Sending auth reply" );
1040
1041                        bt->phase = BS_PHASE_REQUEST;
1042
1043                        return TRUE;
1044                }
1045        case BS_PHASE_REQUEST:
1046                {
1047                        struct socks5_message socks5_connect;
1048                        int msgsize = sizeof( struct socks5_message );
1049
1050                        if( !jabber_bs_peek( bt, &socks5_connect, msgsize ) )
1051                                return FALSE;
1052
1053                        if( !( socks5_connect.ver == 5) ||
1054                            !( socks5_connect.cmdrep.cmd == 1 ) ||
1055                            !( socks5_connect.atyp == 3 ) ||
1056                            !(socks5_connect.addrlen == 40 ) )
1057                                return jabber_bs_abort( bt, "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, socks5_connect.atyp );
1058                        if( !( memcmp( socks5_connect.address, bt->pseudoadr, 40 ) == 0 ) )
1059                                return jabber_bs_abort( bt, "SOCKS5 Connect message contained wrong digest");
1060
1061                        socks5_connect.cmdrep.rep = 0;
1062
1063                        ASSERTSOCKOP( send( fd, &socks5_connect, msgsize, 0 ), "Sending connect reply" );
1064
1065                        bt->phase = BS_PHASE_REPLY;
1066
1067                        imcb_log( tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name );
1068
1069                        if( tf->accepted )
1070                        {
1071                                /* streamhost-used message came already in(possible?), let's start sending */
1072                                tf->ft->write_request( tf->ft );
1073                        }
1074
1075                        tf->watch_in = 0;
1076                        return FALSE;
1077
1078                }
1079        default:
1080                /* BUG */
1081                imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" );
1082
1083                bt->tf->watch_in = 0;
1084                return FALSE;
1085        }
1086}
1087#undef ASSERTSOCKOP
Note: See TracBrowser for help on using the repository browser.