source: protocols/jabber/s5bytestream.c @ 429a9b1

Last change on this file since 429a9b1 was f1f7b5e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-24T22:50:23Z

Take the local address from the IM/IRC connection when setting up a listening
socket for file transfers.

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