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

Last change on this file since 0cab388 was 6cac643, checked in by ulim <a.sporto+bee@…>, at 2008-04-14T12:59:15Z

more verbose error logging

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