source: protocols/jabber/s5bytestream.c @ 2ff2076

Last change on this file since 2ff2076 was 2ff2076, checked in by ulim <a.sporto+bee@…>, at 2007-12-03T14:28:45Z

Intermediate commit. Sending seems to work. TODOs:

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