source: protocols/jabber/io.c @ 995913b

Last change on this file since 995913b was 501b4e0, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-02T16:42:32Z

Added a useful error message for SASL negotiation failures and turned off
the little hack in jabber_write() for now because it breaks error handling.

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - I/O stuff (plain, SSL), queues, etc                      *
5*                                                                           *
6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
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 "ssl_client.h"
26
27static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond );
28
29int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node )
30{
31        char *buf;
32        int st;
33       
34        buf = xt_to_string( node );
35        st = jabber_write( gc, buf, strlen( buf ) );
36        g_free( buf );
37       
38        return st;
39}
40
41int jabber_write( struct gaim_connection *gc, char *buf, int len )
42{
43        struct jabber_data *jd = gc->proto_data;
44       
45        if( jd->tx_len == 0 )
46        {
47                /* If the queue is empty, allocate a new buffer. */
48                jd->tx_len = len;
49                jd->txq = g_memdup( buf, len );
50               
51                /* Try if we can write it immediately so we don't have to do
52                   it via the event handler. If not, add the handler. (In
53                   most cases it probably won't be necessary.) */
54                /* Disabling this trick for now because there's really not
55                   a good way to catch errors yet. :-(
56                if( jabber_write_callback( gc, jd->fd, GAIM_INPUT_WRITE ) )
57                */
58                        jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, gc );
59        }
60        else
61        {
62                /* Just add it to the buffer if it's already filled. The
63                   event handler is already set. */
64                jd->txq = g_renew( char, jd->txq, jd->tx_len + len );
65                memcpy( jd->txq + jd->tx_len, buf, len );
66                jd->tx_len += len;
67        }
68       
69        /* FIXME: write_callback could've generated a real error! Have to
70           find a way to find out if we have to return 0 here. Looking for
71           ourselves in the linked list of connections is a possibility,
72           but it'd be too time-consuming... */
73        return 1;
74}
75
76static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond )
77{
78        struct gaim_connection *gc = data;
79        struct jabber_data *jd = gc->proto_data;
80        int st;
81       
82        if( jd->fd == -1 )
83                return FALSE;
84       
85        if( jd->ssl )
86                st = ssl_write( jd->ssl, jd->txq, jd->tx_len );
87        else
88                st = write( jd->fd, jd->txq, jd->tx_len );
89       
90//      if( st > 0 ) write( 1, jd->txq, st );
91       
92        if( st == jd->tx_len )
93        {
94                /* We wrote everything, clear the buffer. */
95                g_free( jd->txq );
96                jd->txq = NULL;
97                jd->tx_len = 0;
98               
99                return FALSE;
100        }
101        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
102        {
103                /* Set fd to -1 to make sure we won't write to it anymore. */
104                closesocket( jd->fd );  /* Shouldn't be necessary after errors? */
105                jd->fd = -1;
106               
107                hide_login_progress_error( gc, "Short write() to server" );
108                signoff( gc );
109                return FALSE;
110        }
111        else if( st > 0 )
112        {
113                char *s;
114               
115                s = g_memdup( jd->txq + st, jd->tx_len - st );
116                jd->tx_len -= st;
117                g_free( jd->txq );
118                jd->txq = s;
119               
120                return TRUE;
121        }
122        else
123        {
124                /* Just in case we had EINPROGRESS/EAGAIN: */
125               
126                return TRUE;
127        }
128}
129
130static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
131{
132        struct gaim_connection *gc = data;
133        struct jabber_data *jd = gc->proto_data;
134        char buf[512];
135        int st;
136       
137        if( jd->fd == -1 )
138                return FALSE;
139       
140        if( jd->ssl )
141                st = ssl_read( jd->ssl, buf, sizeof( buf ) );
142        else
143                st = read( jd->fd, buf, sizeof( buf ) );
144       
145//      if( st > 0 ) write( 1, buf, st );
146       
147        if( st > 0 )
148        {
149                /* Parse. */
150                if( !xt_feed( jd->xt, buf, st ) )
151                {
152                        hide_login_progress_error( gc, "XML stream error" );
153                        signoff( gc );
154                        return FALSE;
155                }
156               
157                /* Execute all handlers. */
158                if( !xt_handle( jd->xt, NULL ) )
159                {
160                        /* Don't do anything, the handlers should have
161                           aborted the connection already... Or not? FIXME */
162                        return FALSE;
163                }
164               
165                if( jd->flags & JFLAG_STREAM_RESTART )
166                {
167                        jd->flags &= ~JFLAG_STREAM_RESTART;
168                        jabber_start_stream( gc );
169                }
170               
171                /* Garbage collection. */
172                xt_cleanup( jd->xt, NULL );
173               
174                /* This is a bit hackish, unfortunately. Although xmltree
175                   has nifty event handler stuff, it only calls handlers
176                   when nodes are complete. Since the server should only
177                   send an opening <stream:stream> tag, we have to check
178                   this by hand. :-( */
179                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
180                {
181                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
182                        {
183                                jd->flags |= JFLAG_STREAM_STARTED;
184                               
185                                /* If there's no version attribute, assume
186                                   this is an old server that can't do SASL
187                                   authentication. */
188                                if( !sasl_supported( gc ) )
189                                        return jabber_start_iq_auth( gc );
190                        }
191                        else
192                        {
193                                hide_login_progress_error( gc, "XML stream error" );
194                                signoff( gc );
195                                return FALSE;
196                        }
197                }
198        }
199        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
200        {
201                closesocket( jd->fd );
202                jd->fd = -1;
203               
204                hide_login_progress_error( gc, "Error while reading from server" );
205                signoff( gc );
206                return FALSE;
207        }
208       
209        /* EAGAIN/etc or a successful read. */
210        return TRUE;
211}
212
213gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
214{
215        struct gaim_connection *gc = data;
216       
217        if( source == -1 )
218        {
219                hide_login_progress( gc, "Could not connect to server" );
220                signoff( gc );
221                return FALSE;
222        }
223       
224        set_login_progress( gc, 1, "Connected to server, logging in" );
225       
226        return jabber_start_stream( gc );
227}
228
229gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond )
230{
231        struct gaim_connection *gc = data;
232        struct jabber_data *jd = gc->proto_data;
233       
234        if( source == NULL )
235        {
236                /* The SSL connection will be cleaned up by the SSL lib
237                   already, set it to NULL here to prevent a double cleanup: */
238                jd->ssl = NULL;
239               
240                hide_login_progress( gc, "Could not connect to server" );
241                signoff( gc );
242                return FALSE;
243        }
244       
245        set_login_progress( gc, 1, "Connected to server, logging in" );
246       
247        return jabber_start_stream( gc );
248}
249
250static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
251{
252        return XT_ABORT;
253}
254
255static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
256{
257        struct gaim_connection *gc = data;
258        struct jabber_data *jd = gc->proto_data;
259        struct xt_node *c, *reply;
260        int trytls;
261       
262        trytls = g_strcasecmp( set_getstr( &gc->acc->set, "tls" ), "try" ) == 0;
263        c = xt_find_node( node->children, "starttls" );
264        if( c && !jd->ssl )
265        {
266                /* If the server advertises the STARTTLS feature and if we're
267                   not in a secure connection already: */
268               
269                c = xt_find_node( c->children, "required" );
270               
271                if( c && ( !trytls && !set_getbool( &gc->acc->set, "tls" ) ) )
272                {
273                        hide_login_progress( gc, "Server requires TLS connections, but TLS is turned off for this account" );
274                        signoff( gc );
275                       
276                        return XT_ABORT;
277                }
278               
279                /* Only run this if the tls setting is set to true or try: */
280                if( ( trytls || set_getbool( &gc->acc->set, "tls" ) ) )
281                {
282                        reply = xt_new_node( "starttls", NULL, NULL );
283                        xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-tls" );
284                        if( !jabber_write_packet( gc, reply ) )
285                        {
286                                xt_free_node( reply );
287                                return XT_ABORT;
288                        }
289                        xt_free_node( reply );
290                       
291                        return XT_HANDLED;
292                }
293        }
294        else if( !c && !jd->ssl )
295        {
296                /* If the server does not advertise the STARTTLS feature and
297                   we're not in a secure connection already: (Servers have a
298                   habit of not advertising <starttls/> anymore when already
299                   using SSL/TLS. */
300               
301                if( !trytls && set_getbool( &gc->acc->set, "tls" ) )
302                {
303                        hide_login_progress( gc, "TLS is turned on for this account, but is not supported by this server" );
304                        signoff( gc );
305                       
306                        return XT_ABORT;
307                }
308        }
309       
310        /* This one used to be in jabber_handlers[], but it has to be done
311           from here to make sure the TLS session will be initialized
312           properly before we attempt SASL authentication. */
313        if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
314        {
315                if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
316                        return XT_ABORT;
317        }
318        /* If the server *SEEMS* to support SASL authentication but doesn't
319           support it after all, we should try to do authentication the
320           other way. jabber.com doesn't seem to do SASL while it pretends
321           to be XMPP 1.0 compliant! */
322        else if( !( jd->flags & JFLAG_AUTHENTICATED ) && sasl_supported( gc ) )
323        {
324                if( !jabber_start_iq_auth( gc ) )
325                        return XT_ABORT;
326        }
327       
328        if( ( c = xt_find_node( node->children, "bind" ) ) )
329        {
330                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
331                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind" );
332                reply = jabber_make_packet( "iq", "set", NULL, reply );
333                jabber_cache_packet( gc, reply );
334               
335                if( !jabber_write_packet( gc, reply ) )
336                        return XT_ABORT;
337               
338                jd->flags |= JFLAG_WAIT_BIND;
339        }
340       
341        if( ( c = xt_find_node( node->children, "session" ) ) )
342        {
343                reply = xt_new_node( "session", NULL, NULL );
344                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-session" );
345                reply = jabber_make_packet( "iq", "set", NULL, reply );
346                jabber_cache_packet( gc, reply );
347               
348                if( !jabber_write_packet( gc, reply ) )
349                        return XT_ABORT;
350               
351                jd->flags |= JFLAG_WAIT_SESSION;
352        }
353       
354        /* This flag is already set if we authenticated via SASL, so now
355           we can resume the session in the new stream, if we don't have
356           to bind/initialize the session. */
357        if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
358        {
359                if( !jabber_get_roster( gc ) || !jabber_get_privacy( gc ) )
360                        return XT_ABORT;
361        }
362       
363        return XT_HANDLED;
364}
365
366static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
367{
368        struct gaim_connection *gc = data;
369        struct jabber_data *jd = gc->proto_data;
370        char *xmlns;
371       
372        xmlns = xt_find_attr( node, "xmlns" );
373       
374        /* Just ignore it when it doesn't seem to be TLS-related (is that at
375           all possible??). */
376        if( !xmlns || strcmp( xmlns, "urn:ietf:params:xml:ns:xmpp-tls" ) != 0 )
377                return XT_HANDLED;
378       
379        /* We don't want event handlers to touch our TLS session while it's
380           still initializing! */
381        b_event_remove( jd->r_inpa );
382        if( jd->tx_len > 0 )
383        {
384                /* Actually the write queue should be empty here, but just
385                   to be sure... */
386                b_event_remove( jd->w_inpa );
387                g_free( jd->txq );
388                jd->txq = NULL;
389                jd->tx_len = 0;
390        }
391        jd->w_inpa = jd->r_inpa = 0;
392       
393        set_login_progress( gc, 1, "Converting stream to TLS" );
394       
395        jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, gc );
396       
397        return XT_HANDLED;
398}
399
400static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
401{
402        printf( "Received unknown packet:\n" );
403        xt_print( node );
404       
405        return XT_HANDLED;
406}
407
408static const struct xt_handler_entry jabber_handlers[] = {
409        { "stream:stream",      "<root>",               jabber_end_of_stream },
410        { "message",            "stream:stream",        jabber_pkt_message },
411        { "presence",           "stream:stream",        jabber_pkt_presence },
412        { "iq",                 "stream:stream",        jabber_pkt_iq },
413        { "stream:features",    "stream:stream",        jabber_pkt_features },
414        { "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
415        { "challenge",          "stream:stream",        sasl_pkt_challenge },
416        { "success",            "stream:stream",        sasl_pkt_result },
417        { "failure",            "stream:stream",        sasl_pkt_result },
418        { NULL,                 "stream:stream",        jabber_pkt_misc },
419        { NULL,                 NULL,                   NULL }
420};
421
422gboolean jabber_start_stream( struct gaim_connection *gc )
423{
424        struct jabber_data *jd = gc->proto_data;
425        int st;
426        char *greet;
427       
428        /* We'll start our stream now, so prepare everything to receive one
429           from the server too. */
430        xt_free( jd->xt );      /* In case we're RE-starting. */
431        jd->xt = xt_new( gc );
432        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
433       
434        if( jd->r_inpa <= 0 )
435                jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
436       
437        greet = g_strdup_printf( "<?xml version='1.0' ?>"
438                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
439                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
440       
441        st = jabber_write( gc, greet, strlen( greet ) );
442       
443        g_free( greet );
444       
445        return st;
446}
447
448void jabber_end_stream( struct gaim_connection *gc )
449{
450        struct jabber_data *jd = gc->proto_data;
451       
452        /* Let's only do this if the queue is currently empty, otherwise it'd
453           take too long anyway. */
454        if( jd->tx_len == 0 )
455        {
456                char eos[] = "</stream:stream>";
457                struct xt_node *node;
458                int st = 1;
459               
460                if( gc->flags & OPT_LOGGED_IN )
461                {
462                        node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
463                        st = jabber_write_packet( gc, node );
464                        xt_free_node( node );
465                }
466               
467                if( st )
468                        jabber_write( gc, eos, strlen( eos ) );
469        }
470}
Note: See TracBrowser for help on using the repository browser.