diff --git a/examples/basic.c b/examples/basic.c index fb9299d..bd0ab22 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -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); } } diff --git a/src/auth.c b/src/auth.c index 31732ae..fa8441d 100644 --- a/src/auth.c +++ b/src/auth.c @@ -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; } diff --git a/src/common.h b/src/common.h index ddc30fd..2070b78 100644 --- a/src/common.h +++ b/src/common.h @@ -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 */ diff --git a/src/conn.c b/src/conn.c index 9c145bf..f7dfb7d 100644 --- a/src/conn.c +++ b/src/conn.c @@ -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); } } diff --git a/src/event.c b/src/event.c index b13845b..3be5c49 100644 --- a/src/event.c +++ b/src/event.c @@ -41,15 +41,6 @@ #define _sleep(x) usleep((x)*1000) #else #include -#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); } } diff --git a/src/util.c b/src/util.c index 66f44aa..c085041 100644 --- a/src/util.c +++ b/src/util.c @@ -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); } diff --git a/strophe.h b/strophe.h index daa244a..7e2c5dc 100644 --- a/strophe.h +++ b/strophe.h @@ -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);