source: protocols/jabber/s5bytestream.c @ 0fbd3a6d

Last change on this file since 0fbd3a6d was dc0ba9c, checked in by kenobi <kenobi@…>, at 2007-12-18T02:07:59Z

sending via proxy

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