WIP Unify error codes returned to the connection handler

Error argument in the connection handler is not always set and in some
cases provides meaningless values. For example, every TLS module returns
its specific error codes.

Current solution unifies error codes and allows to distinguish between
different types of disconnection.

Fixes #123.
This commit is contained in:
Dmitry Podgorny
2020-09-16 00:09:29 +03:00
parent 9ee08f8f2a
commit 070a64d9d5
7 changed files with 95 additions and 58 deletions

View File

@@ -38,7 +38,25 @@ void conn_handler(xmpp_conn_t *const conn,
secured ? "secured" : "NOT secured");
xmpp_disconnect(conn);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
switch (error) {
case XMPP_EOK:
fprintf(stderr, "DEBUG: disconnected successfully.\n");
break;
case XMPP_EINVOP:
/*
* This may happen when the requested functionality is not
* supported or some data is not provided when it is required (e.g.
* resource part).
*/
fprintf(stderr, "DEBUG: disconnected, invalid operation.\n");
break;
case XMPP_ECERT:
/*
* In this case, application can reconnect with
* XMPP_CONN_FLAG_TRUST_TLS if user permits.
*/
fprintf(stderr, "DEBUG: disconnected, invalid TLS certificate.\n");
}
xmpp_stop(ctx);
}
}

View File

@@ -349,7 +349,7 @@ static int _handle_sasl_result(xmpp_conn_t *const conn,
"Got unexpected reply to SASL %s"
"authentication.",
(char *)userdata);
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
}
return 0;
@@ -644,7 +644,7 @@ static void _auth(xmpp_conn_t *const conn)
xmpp_error(conn->ctx, "xmpp",
"TLS is not supported, but set as "
"mandatory for this connection");
conn_disconnect(conn);
disconnect_with_error(conn, XMPP_EINVOP);
return;
}
@@ -667,7 +667,7 @@ static void _auth(xmpp_conn_t *const conn)
} else if (anonjid) {
xmpp_error(conn->ctx, "auth",
"No node in JID, and SASL ANONYMOUS unsupported.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINVOP);
} else if (conn->sasl_support & SASL_MASK_SCRAM) {
scram_ctx = xmpp_alloc(conn->ctx, sizeof(*scram_ctx));
if (conn->sasl_support & SASL_MASK_SCRAMSHA512)
@@ -780,7 +780,7 @@ static void _auth(xmpp_conn_t *const conn)
_auth_legacy(conn);
} else {
xmpp_error(conn->ctx, "auth", "Cannot authenticate with known methods");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EAUTH);
}
}
@@ -930,7 +930,7 @@ static int _handle_features_sasl(xmpp_conn_t *const conn,
xmpp_error(conn->ctx, "xmpp",
"Stream features does not allow "
"resource bind.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
}
return 0;
@@ -944,7 +944,7 @@ static int _handle_missing_features_sasl(xmpp_conn_t *const conn,
xmpp_error(conn->ctx, "xmpp",
"Did not receive stream features "
"after SASL authentication.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_ETIMEDOUT);
return 0;
}
@@ -964,7 +964,7 @@ static int _handle_bind(xmpp_conn_t *const conn,
type = xmpp_stanza_get_type(stanza);
if (type && strcmp(type, "error") == 0) {
xmpp_error(conn->ctx, "xmpp", "Binding failed.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
} else if (type && strcmp(type, "result") == 0) {
xmpp_stanza_t *binding = xmpp_stanza_get_child_by_name(stanza, "bind");
xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
@@ -1016,7 +1016,7 @@ static int _handle_bind(xmpp_conn_t *const conn,
}
} else {
xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
}
return 0;
@@ -1027,7 +1027,7 @@ static int _handle_missing_bind(xmpp_conn_t *const conn, void *const userdata)
UNUSED(userdata);
xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_ETIMEDOUT);
return 0;
}
@@ -1046,7 +1046,7 @@ static int _handle_session(xmpp_conn_t *const conn,
type = xmpp_stanza_get_type(stanza);
if (type && strcmp(type, "error") == 0) {
xmpp_error(conn->ctx, "xmpp", "Session establishment failed.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
} else if (type && strcmp(type, "result") == 0) {
xmpp_debug(conn->ctx, "xmpp", "Session establishment successful.");
@@ -1056,7 +1056,7 @@ static int _handle_session(xmpp_conn_t *const conn,
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);
disconnect_with_error(conn, XMPP_EINT);
}
return 0;
@@ -1068,7 +1068,7 @@ static int _handle_missing_session(xmpp_conn_t *const conn,
UNUSED(userdata);
xmpp_error(conn->ctx, "xmpp", "Server did not reply to session request.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_ETIMEDOUT);
return 0;
}
@@ -1079,7 +1079,7 @@ static int _handle_missing_legacy(xmpp_conn_t *const conn, void *const userdata)
xmpp_error(conn->ctx, "xmpp",
"Server did not reply to legacy "
"authentication request.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_ETIMEDOUT);
return 0;
}
@@ -1102,11 +1102,11 @@ static int _handle_legacy(xmpp_conn_t *const conn,
xmpp_error(conn->ctx, "xmpp",
"Server sent us an unexpected response "
"to legacy authentication request.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
} else if (strcmp(type, "error") == 0) {
/* legacy client auth failed, no more fallbacks */
xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EAUTH);
} else if (strcmp(type, "result") == 0) {
/* auth succeeded */
xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
@@ -1117,7 +1117,7 @@ static int _handle_legacy(xmpp_conn_t *const conn,
xmpp_error(conn->ctx, "xmpp",
"Server sent us a legacy authentication "
"response with a bad type.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINT);
}
return 0;
@@ -1197,7 +1197,7 @@ static void _auth_legacy(xmpp_conn_t *conn)
xmpp_stanza_release(authdata);
xmpp_stanza_release(iq);
xmpp_error(conn->ctx, "auth", "Cannot authenticate without resource");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EINVOP);
return;
}
xmpp_stanza_add_child(child, authdata);
@@ -1231,7 +1231,7 @@ void auth_handle_component_open(xmpp_conn_t *const conn)
rc = _handle_component_auth(conn);
if (rc != 0) {
xmpp_error(conn->ctx, "auth", "Component authentication failed.");
xmpp_disconnect(conn);
disconnect_with_error(conn, rc);
}
}
@@ -1298,15 +1298,8 @@ int _handle_component_hs_response(xmpp_conn_t *const conn,
name = xmpp_stanza_get_name(stanza);
if (strcmp(name, "handshake") != 0) {
char *msg;
size_t msg_size;
xmpp_stanza_to_text(stanza, &msg, &msg_size);
if (msg) {
xmpp_debug(conn->ctx, "auth", "Handshake failed: %s", msg);
xmpp_free(conn->ctx, msg);
}
xmpp_disconnect(conn);
return XMPP_EINT;
xmpp_debug(conn->ctx, "auth", "Handshake failed.");
disconnect_with_error(conn, XMPP_EINT);
} else {
conn->authenticated = 1;
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
@@ -1323,7 +1316,7 @@ int _handle_missing_handshake(xmpp_conn_t *const conn, void *const userdata)
UNUSED(userdata);
xmpp_error(conn->ctx, "xmpp", "Server did not reply to handshake request.");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_ETIMEDOUT);
return 0;
}

View File

@@ -276,6 +276,7 @@ void handler_add(xmpp_conn_t *const conn,
void handler_system_delete_all(xmpp_conn_t *conn);
/* utility functions */
void disconnect_with_error(xmpp_conn_t *const conn, int error);
void disconnect_mem_error(xmpp_conn_t *const conn);
/* auth functions */

View File

@@ -918,8 +918,8 @@ int conn_tls_start(xmpp_conn_t *const conn)
if (tls_start(conn->tls)) {
conn->secured = 1;
} else {
rc = XMPP_EINT;
conn->error = tls_error(conn->tls);
/* TODO: distinguish invalid certificates and other errors */
rc = XMPP_ECERT;
tls_free(conn->tls);
conn->tls = NULL;
conn->tls_failed = 1;
@@ -927,9 +927,9 @@ int conn_tls_start(xmpp_conn_t *const conn)
}
if (rc != 0) {
xmpp_debug(conn->ctx, "conn",
"Couldn't start TLS! "
"error %d tls_error %d",
rc, conn->error);
"Couldn't start TLS! error %d tls_error %d", rc,
conn->tls == NULL ? 0 : tls_error(conn->tls));
conn->error = rc;
}
return rc;
}
@@ -1069,6 +1069,7 @@ static int _disconnect_cleanup(xmpp_conn_t *const conn, void *const userdata)
xmpp_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
conn->error = conn->error ?: XMPP_ETIMEDOUT;
conn_disconnect(conn);
return 0;
@@ -1195,7 +1196,7 @@ static void _handle_stream_start(char *name, char **attrs, void *const userdata)
{
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
char *id;
int failed = 0;
int ret = 0;
if (conn->stream_id)
xmpp_free(conn->ctx, conn->stream_id);
@@ -1209,20 +1210,21 @@ static void _handle_stream_start(char *name, char **attrs, void *const userdata)
if (id && !conn->stream_id) {
xmpp_error(conn->ctx, "conn", "Memory allocation failed.");
failed = 1;
ret = XMPP_EMEM;
}
} else {
xmpp_error(conn->ctx, "conn",
"Server did not open valid stream."
" name = %s.",
name);
failed = 1;
ret = XMPP_EINT;
}
if (!failed) {
if (ret == 0) {
/* call stream open handler */
conn->open_handler(conn);
} else {
conn->error = ret;
conn_disconnect(conn);
}
}

View File

@@ -41,15 +41,6 @@
#define _sleep(x) usleep((x)*1000)
#else
#include <winsock2.h>
#ifndef ETIMEDOUT
#define ETIMEDOUT WSAETIMEDOUT
#endif
#ifndef ECONNRESET
#define ECONNRESET WSAECONNRESET
#endif
#ifndef ECONNABORTED
#define ECONNABORTED WSAECONNABORTED
#endif
#define _sleep(x) Sleep(x)
#endif
@@ -107,25 +98,23 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
/* an error occurred */
xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
conn->error = XMPP_EIO;
}
}
/* write all data from the send queue to the socket */
sq = conn->send_queue_head;
while (sq) {
while (!conn->error && sq) {
towrite = sq->len - sq->written;
if (conn->tls) {
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls)))
conn->error = tls_error(conn->tls);
conn->error = XMPP_EIO;
} else {
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
if (ret < 0 && !sock_is_recoverable(sock_error()))
conn->error = sock_error();
conn->error = XMPP_EIO;
}
if (ret > 0 && ret < towrite)
sq->written += ret; /* not all data could be sent now */
@@ -151,7 +140,6 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* FIXME: need to tear down send queues and random other things
* maybe this should be abstracted */
xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
}
@@ -190,8 +178,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
conn->connect_timeout)
FD_SET(conn->sock, &wfds);
else {
conn->error = ETIMEDOUT;
xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
conn->error = XMPP_ETIMEDOUT;
conn_disconnect(conn);
}
break;
@@ -252,6 +240,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
if (ret != 0) {
/* connection failed */
xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret);
conn->error = XMPP_EIO;
conn_disconnect(conn);
break;
}
@@ -285,14 +274,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
xmpp_debug(ctx, "xmpp",
"Unrecoverable TLS error, %d.",
tls_error(conn->tls));
conn->error = tls_error(conn->tls);
conn->error = XMPP_EIO;
conn_disconnect(conn);
}
} else {
/* return of 0 means socket closed by server */
xmpp_debug(ctx, "xmpp",
"Socket closed by remote host.");
conn->error = ECONNRESET;
conn->error = ret == 0 ? XMPP_ERESET : XMPP_EIO;
conn_disconnect(conn);
}
}

View File

@@ -128,6 +128,20 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
return (uint64_t)(t2 - t1);
}
/** Disconnect the stream with the error.
* This is a convenience function used internally by various parts of
* the Strophe library for terminating the connection because of an error.
*
* @param conn a Strophe connection object
* @param error an error code
*/
void disconnect_with_error(xmpp_conn_t *const conn, int error)
{
xmpp_error(conn->ctx, "xmpp", "Disconnecting with error %d", error);
conn->error = conn->error ?: error;
xmpp_disconnect(conn);
}
/** Disconnect the stream with a memory error.
* This is a convenience function used internally by various parts of
* the Strophe library for terminating the connection because of a
@@ -138,5 +152,5 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
void disconnect_mem_error(xmpp_conn_t *const conn)
{
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
xmpp_disconnect(conn);
disconnect_with_error(conn, XMPP_EMEM);
}

View File

@@ -103,6 +103,26 @@ extern "C" {
* Internal failure error code.
*/
#define XMPP_EINT -3
/** @def XMPP_ECERT
* Certificate verification failed during TLS establishment.
*/
#define XMPP_ECERT -4
/** @def XMPP_ETIMEDOUT
* Operation timed out error code.
*/
#define XMPP_ETIMEDOUT -5
/** @def XMPP_EIO
* I/O error code.
*/
#define XMPP_EIO -6
/** @def XMPP_ERESET
* Error code for situation when connection closed by remote side.
*/
#define XMPP_ERESET -7
/** @def XMPP_EAUTH
* Authentication failed error code.
*/
#define XMPP_EAUTH -8
/* initialization and shutdown */
void xmpp_initialize(void);