source: protocols/jabber/s5bytestream.c @ 664bac3

Last change on this file since 664bac3 was 8256ad5, checked in by dequis <dx@…>, at 2014-10-27T06:36:05Z

Some more g_source_remove warning fixes

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