source: protocols/jabber/s5bytestream.c @ b8a491d

Last change on this file since b8a491d was b8a491d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-18T00:30:38Z

Some compiler warning fixes and renamed "transfers" command to just
"transfer" since all root commands are singular so far.

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