source: protocols/jabber/io.c @ 42127dc

Last change on this file since 42127dc was 42127dc, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-09-24T11:57:45Z

Added support for SSL- and TLS-connections. Checking of the "tls" user
setting has to be finished, plus an ssl_starttls() function for the other
SSL libraries (this code will only compile with GnuTLS for now).

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