source: protocols/jabber/s5bytestream.c @ e8c8d00

Last change on this file since e8c8d00 was a02f34f, checked in by ulim <a.sporto+bee@…>, at 2008-08-11T23:07:12Z

Added conf entries and lib/ftutil.[ch].

ft_listen = <IP-A>:<Port-A>;<IP-B>:<Port-B> to specify listening addresses for
the bitlbee<->client connection and the bitlbee<->IM peer connection,
respectively.

ft_max_size should be obvious. ft_max_kbps should limit the kilobits per second
per transfer (not implemented yet).

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