From b9921339871ac9ac51bf2d71293e51f401fcdc79 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 18 Oct 2005 07:58:38 +0000 Subject: [PATCH] Implement session establishment, which is required if the server advertises it. Fixes bugs relating to not-authorized errors on XMPP compliant servers. --- src/auth.c | 134 +++++++++++++++++++++++++++++++++++++++++++++------ src/common.h | 4 ++ src/conn.c | 3 ++ 3 files changed, 125 insertions(+), 16 deletions(-) diff --git a/src/auth.c b/src/auth.c index 922bc65..d187071 100644 --- a/src/auth.c +++ b/src/auth.c @@ -24,8 +24,10 @@ #define strcasecmp stricmp #endif +/* FIXME: these should be configurable */ #define FEATURES_TIMEOUT 2000 /* 2 seconds */ #define BIND_TIMEOUT 2000 /* 2 seconds */ +#define SESSION_TIMEOUT 2000 /* 2 seconds */ #define LEGACY_TIMEOUT 2000 /* 2 seconds */ static void _auth(xmpp_conn_t * const conn); @@ -55,6 +57,11 @@ static int _handle_missing_bind(xmpp_conn_t * const conn, static int _handle_bind(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); +static int _handle_session(xmpp_conn_t * const conn, + xmpp_stanza_t * const stanza, + void * const userdata); +static int _handle_missing_session(xmpp_conn_t * const conn, + void * const userdata); /* stream:features handlers */ @@ -324,6 +331,7 @@ static void _auth(xmpp_conn_t * const conn) if (!auth) { disconnect_mem_error(conn); return; + } handler_add(conn, _handle_digestmd5_challenge, @@ -504,15 +512,29 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { - xmpp_stanza_t *bind, *iq, *res, *text; + xmpp_stanza_t *bind, *session, *iq, *res, *text; char *resource; /* remove missing features handler */ xmpp_timed_handler_delete(conn, _handle_missing_features_sasl); - /* we are expecting */ + /* we are expecting and since this is a + XMPP style connection */ + bind = xmpp_stanza_get_child_by_name(stanza, "bind"); if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) { + /* resource binding is required */ + conn->bind_required = 1; + } + + session = xmpp_stanza_get_child_by_name(stanza, "session"); + if (session && strcmp(xmpp_stanza_get_ns(session), XMPP_NS_SESSION) == 0) { + /* session establishment required */ + conn->session_required = 1; + } + + /* if bind is required, go ahead and start it */ + if (conn->bind_required) { /* bind resource */ /* setup response handlers */ @@ -520,10 +542,7 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, handler_add_timed(conn, _handle_missing_bind, BIND_TIMEOUT, NULL); - /* send bind response */ - /* FIXME: this should detect whether a resource is present in - conn->jid and bind that instead. right now we let the server bind - us */ + /* send bind request */ iq = xmpp_stanza_new(conn->ctx); if (!iq) { disconnect_mem_error(conn); @@ -536,6 +555,7 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, bind = xmpp_stanza_copy(bind); if (!bind) { + xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } @@ -547,16 +567,23 @@ static int _handle_features_sasl(xmpp_conn_t * const conn, xmpp_free(conn->ctx, resource); resource = NULL; } + + /* if we have a resource to request, do it. otherwise the + server will assign us one */ if (resource) { - /* request a specific resource */ res = xmpp_stanza_new(conn->ctx); if (!res) { + xmpp_stanza_release(bind); + xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } xmpp_stanza_set_name(res, "resource"); text = xmpp_stanza_new(conn->ctx); if (!text) { + xmpp_stanza_release(res); + xmpp_stanza_release(bind); + xmpp_stanza_release(iq); disconnect_mem_error(conn); return 0; } @@ -596,25 +623,63 @@ static int _handle_bind(xmpp_conn_t * const conn, void * const userdata) { char *type; + xmpp_stanza_t *iq, *session; /* delete missing bind handler */ xmpp_timed_handler_delete(conn, _handle_missing_bind); /* server has replied to bind request */ type = xmpp_stanza_get_type(stanza); - if (!type) { - xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply."); - xmpp_disconnect(conn); - } else if (strcmp(type, "error") == 0) { + if (type && strcmp(type, "error") == 0) { xmpp_error(conn->ctx, "xmpp", "Binding failed."); xmpp_disconnect(conn); - } else if (strcmp(type, "result") == 0) { + } else if (type && strcmp(type, "result") == 0) { /* TODO: extract resource if present */ xmpp_debug(conn->ctx, "xmpp", "Bind successful."); - conn->authenticated = 1; - - /* call connection handler */ - conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata); + + /* establish a session if required */ + if (conn->session_required) { + /* setup response handlers */ + handler_add_id(conn, _handle_session, "_xmpp_session1", NULL); + handler_add_timed(conn, _handle_missing_session, + SESSION_TIMEOUT, NULL); + + /* send session request */ + iq = xmpp_stanza_new(conn->ctx); + if (!iq) { + disconnect_mem_error(conn); + return 0; + } + + xmpp_stanza_set_name(iq, "iq"); + xmpp_stanza_set_type(iq, "set"); + xmpp_stanza_set_id(iq, "_xmpp_session1"); + + session = xmpp_stanza_new(conn->ctx); + if (!session) { + xmpp_stanza_release(iq); + disconnect_mem_error(conn); + } + + xmpp_stanza_set_name(session, "session"); + xmpp_stanza_set_ns(session, XMPP_NS_SESSION); + + xmpp_stanza_add_child(iq, session); + xmpp_stanza_release(session); + + /* send session establishment request */ + xmpp_send(conn, iq); + xmpp_stanza_release(iq); + } else { + conn->authenticated = 1; + + /* call connection handler */ + conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, + conn->userdata); + } + } else { + xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply."); + xmpp_disconnect(conn); } return 0; @@ -628,6 +693,43 @@ static int _handle_missing_bind(xmpp_conn_t * const conn, return 0; } +static int _handle_session(xmpp_conn_t * const conn, + xmpp_stanza_t * const stanza, + void * const userdata) +{ + char *type; + + /* delete missing session handler */ + xmpp_timed_handler_delete(conn, _handle_missing_bind); + + /* server has replied to the session request */ + type = xmpp_stanza_get_type(stanza); + if (type && strcmp(type, "error") == 0) { + xmpp_error(conn->ctx, "xmpp", "Session establishment failed."); + xmpp_disconnect(conn); + } else if (type && strcmp(type, "result") == 0) { + xmpp_debug(conn->ctx, "xmpp", "Session establishment successful."); + + conn->authenticated = 1; + + /* call connection handler */ + conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata); + } else { + xmpp_error(conn->ctx, "xmpp", "Server sent malformed session reply."); + xmpp_disconnect(conn); + } + + return 0; +} + +static int _handle_missing_session(xmpp_conn_t * const conn, + void * const userdata) +{ + xmpp_error(conn->ctx, "xmpp", "Server did not reply to session request."); + xmpp_disconnect(conn); + return 0; +} + static int _handle_legacy(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) diff --git a/src/common.h b/src/common.h index 8f33989..978afe3 100644 --- a/src/common.h +++ b/src/common.h @@ -161,6 +161,10 @@ struct _xmpp_conn_t { int sasl_support; /* if true, field is a bitfield of supported mechanisms */ + /* if server returns or we must do them */ + int bind_required; + int session_required; + char *lang; char *domain; char *jid; diff --git a/src/conn.c b/src/conn.c index a5fbc39..8c832bc 100644 --- a/src/conn.c +++ b/src/conn.c @@ -66,6 +66,9 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx) conn->tls_support = 0; conn->sasl_support = 0; + conn->bind_required = 0; + conn->session_required = 0; + conn->parser = NULL; conn->stanza = NULL; parser_prepare_reset(conn, auth_handle_open);