source: protocols/jabber/s5bytestream.c @ 08135df

Last change on this file since 08135df was dce3903, checked in by ulim <a.sporto+bee@…>, at 2007-12-04T00:48:57Z

Send and receive seems to work now! Also adopted the new buffering strategy,
only one buffer of 2k per transfer now.

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