source: protocols/jabber/s5bytestream.c @ 29c1456

Last change on this file since 29c1456 was 29c1456, checked in by ulim <a.sporto+bee@…>, at 2008-05-06T00:13:37Z

SOCKS5 bytestream related changes.

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