source: protocols/jabber/s5bytestream.c @ 4ac647d

Last change on this file since 4ac647d was 4ac647d, checked in by ulim <a.sporto+bee@…>, at 2008-08-04T14:21:49Z

moved some stuff around in preperation for MSN merge.

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