13 Commits

Author SHA1 Message Date
Steffen Jaeckel
f13ea6e556 Release libstrophe-0.12.3
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-02 15:42:54 +02:00
Steffen Jaeckel
29f68c0dd7 Update AUTHORS file
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-02 15:42:54 +02:00
Steffen Jaeckel
3c0966d74e Run CI also on next branch
Like that we can test stuff without opening a PR, before it necessarily
lands on `master`.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-02 15:42:27 +02:00
Steffen Jaeckel
c90128779f Fix xmpp_stanza_release() for cloned child-stanzas.
Break the linked-list of children before releasing a child.

Before this patch it was possible, when a child is cloned and stored
for longer than the lifetime of its parent, that its `next` pointer
points to invalid memory that was already free'd when the parent stanza
was released.

This issue exists already since the initial version of
`xmpp_stanza_release()`.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-07-28 15:36:54 +02:00
Steffen Jaeckel
acb8e0f629 Handle missing <bind>
This patch handles the case where the server sends its list of features,
but the `<bind>` feature is missing.
A server doing so is violating RFC6120 (c.f. [0]), but it happened in [1].
Previously we ended up in a segfault, now we terminate the connection.

Reproducing this was done with netcat and profanity:

```
shell1    $ nc -l -p 5222
profanity $ /connect foo@127.0.0.1 tls disable

-> nc receives: '<?xml version="1.0"?><stream:stream to="127.0.0.1" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">'

nc send: <?xml version="1.0"?>
<stream:stream from="127.0.0.1" id="foobarbaz" lang="en" version="1.0" xmlns:stream="http://etherx.jabber.org/streams">
<features xmlns="http://etherx.jabber.org/streams"><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></features>

-> nc receives: '<auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">XXXXXXXXXX</auth>'

nc send: <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

-> nc receives: '<?xml version="1.0"?><stream:stream to="127.0.0.1" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams"><stream:stream from="127.0.0.1" id="foobarbaz" lang="en" version="1.0" xmlns:stream="http://etherx.jabber.org/streams">'

nc send: <?xml version="1.0"?>
<stream:stream from="127.0.0.1" id="foobarbaz" lang="en" version="1.0" xmlns:stream="http://etherx.jabber.org/streams">
<features xmlns="http://etherx.jabber.org/streams"/>

-> pre-patch this lead to a segfault of profanity, now the stream gets closed.
```

[0] https://datatracker.ietf.org/doc/html/rfc6120#section-7.2
[1] https://github.com/profanity-im/profanity/issues/1849

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-07-28 15:36:54 +02:00
Dmitry Podgorny
f3460460e9 sock: Introduce xmpp_sock_t abstraction
libstrophe uses non-blocking sockets and the connect() syscall may
return before a TCP connection is established. This doesn't allow
to catch all possible errors synchronously and some of the errors
are handled in the event handler.

In a scenario with multiple SRV records and/or multiple IP addresses
resolution, we need to repeat connection attempt on a failure.

xmpp_sock_t resolves the above problem. It keeps resolved records and
addresses to repeat connect attempt asynchronously.
2023-06-16 20:33:00 +03:00
Steffen Jaeckel
37555868f6 improve JID parsing according to RFC7622
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-03-10 11:11:44 +01:00
Steffen Jaeckel
b0855f75f5 don't return RR list on error
The resolver maybe returned an error code but allocated an RR list. Free
this list now in the resolver instead of depending on the user to free it
even though there was an error.

This API is only library internal, but still it makes more sense like that.

This issue was discovered in CI job [0].

[0] https://github.com/strophe/libstrophe/actions/runs/3874980845/jobs/6776501943

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-01-19 10:30:08 +01:00
Steffen Jaeckel
977ef75b14 fix potential memory leak
If functions `xmpp_conn_set_cafile()` or `xmpp_conn_set_capath()` are
called twice they leaked the previously allocated memory.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-01-09 15:55:23 +01:00
Steffen Jaeckel
d7d7435f21 less calls to xmpp_stanza_release()
Instead of using the "cloning version" `xmpp_stanza_add_child()` use the
version that takes ownership, so we don't have to release directly
afterwards.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-01-09 15:55:23 +01:00
Steffen Jaeckel
d22558e0e3 send_stanza() now takes ownership of the stanza
Instead of always releasing the stanza after calling `send_stanza()`, this
is done inside the function now.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-01-09 15:55:23 +01:00
Anna “CyberTailor”
96ece6fbdb test configure script with dash in CI 2022-11-12 13:23:20 +01:00
Anna “CyberTailor”
a60d2ea19d configure: fix non-portable "==" tests 2022-11-12 12:58:35 +01:00
18 changed files with 493 additions and 314 deletions

View File

@@ -2,7 +2,9 @@ name: CI
on:
push:
branches: [master]
branches:
- master
- next
pull_request:
branches: [master]
@@ -95,13 +97,18 @@ jobs:
name: Check if release would work
steps:
- uses: actions/checkout@v2
- name: install dependencies
- name: install dependencies & bootstrap
run: |
sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev
- name: Setup the library
run: |
sudo apt install -y libtool pkg-config libexpat1-dev dash
./bootstrap.sh
- name: Check if configure works with non-bash shells
# https://github.com/actions/runner/issues/241 requires us to use this following line...
shell: 'script --return --quiet --command "bash {0}"'
run: |
[ "`CONFIG_SHELL=/bin/dash ./configure 2>&1 1>/dev/null | tee /dev/tty | wc -l`" = "0" ]
- name: Re-run configure with the default shell
run: |
./configure
- name: Try release & tests
run: |

View File

@@ -1 +1,8 @@
Current maintainer:
Steffen Jaeckel <gh@jaeckel.eu>
Previous maintainers:
Dmitry Podgorny <pasis.ua@gmail.com>
Jack Moffit <jack@metajack.im>
Other contributors can be extracted from the Git log.

View File

@@ -1,3 +1,12 @@
0.12.3
- Improve TCP-connection establishment (#221)
- Handle case where the server doesn't provide the `bind` feature (#224)
- Fix configure script for non-bash shells (#218)
- Parse JID's according to RFC7622 (#219)
- Fix potential memory leak in internal DNS resolver (#219)
- Fix potential memory leaks in `xmpp_conn_set_cafile()` and `xmpp_conn_set_capath()` (#219)
- Internal improvements (#219)
0.12.2
- Fix reconnect issues when Stream Management is enabled (#211)
- Fix resolver ... this time for real hopefully (fixup of #200) (#214)

View File

@@ -1,6 +1,6 @@
m4_define([v_maj], [0])
m4_define([v_min], [12])
m4_define([v_patch], [2])
m4_define([v_patch], [3])
m4_define([project_version], [v_maj.v_min.v_patch])
m4_define([lt_cur], m4_eval(v_maj + v_min))
@@ -123,7 +123,7 @@ if test x"$have_va_copy$have___va_copy" = x"nono"; then
AC_DEFINE([VA_LIST_IS_ARRAY], [1],[Define if va_list is an array type])])
fi
if test "x$enable_tls" != xno -a "x$with_gnutls" == xyes; then
if test "x$enable_tls" != xno -a "x$with_gnutls" = xyes; then
PKG_CHECK_MODULES([gnutls], [gnutls],
[PC_REQUIRES="gnutls ${PC_REQUIRES}"],
[AC_CHECK_HEADER([gnutls/gnutls.h],
@@ -133,7 +133,7 @@ if test "x$enable_tls" != xno -a "x$with_gnutls" == xyes; then
],
[AC_MSG_ERROR([gnutls not found; gnutls required])]
)])
elif test "x$enable_tls" != xno -a "x$with_schannel" == xyes; then
elif test "x$enable_tls" != xno -a "x$with_schannel" = xyes; then
if test "x$PLATFORM" != xwin32; then
AC_MSG_ERROR([schannel is only supported on Windows])
fi

View File

@@ -391,14 +391,12 @@ static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
xmpp_stanza_set_text(authdata, response);
strophe_free(conn->ctx, response);
xmpp_stanza_add_child(auth, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(auth, authdata, 0);
handler_add(conn, _handle_digestmd5_rspauth, XMPP_NS_SASL, NULL, NULL,
NULL);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
} else {
return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
@@ -432,7 +430,6 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
xmpp_stanza_set_name(auth, "response");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
} else {
return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
}
@@ -491,11 +488,9 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
xmpp_stanza_set_text(authdata, response);
strophe_free(conn->ctx, response);
xmpp_stanza_add_child(auth, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(auth, authdata, 0);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
rc = 1; /* Keep handler */
} else {
@@ -625,7 +620,6 @@ static void _auth(xmpp_conn_t *conn)
NULL);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* TLS was tried, unset flag */
conn->tls_support = 0;
@@ -653,7 +647,6 @@ static void _auth(xmpp_conn_t *conn)
"ANONYMOUS");
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* SASL ANONYMOUS was tried, unset flag */
conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
@@ -689,14 +682,12 @@ static void _auth(xmpp_conn_t *conn)
}
strophe_free(conn->ctx, str);
xmpp_stanza_add_child(auth, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(auth, authdata, 0);
handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
"EXTERNAL");
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* SASL EXTERNAL was tried, unset flag */
conn->sasl_support &= ~SASL_MASK_EXTERNAL;
@@ -754,14 +745,12 @@ static void _auth(xmpp_conn_t *conn)
}
xmpp_stanza_set_text(authdata, str);
strophe_free(conn->ctx, str);
xmpp_stanza_add_child(auth, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(auth, authdata, 0);
handler_add(conn, _handle_scram_challenge, XMPP_NS_SASL, NULL, NULL,
(void *)scram_ctx);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* SASL SCRAM-SHA-1 was tried, unset flag */
conn->sasl_support &= ~scram_ctx->alg->mask;
@@ -776,7 +765,6 @@ static void _auth(xmpp_conn_t *conn)
NULL);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* SASL DIGEST-MD5 was tried, unset flag */
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
@@ -805,14 +793,12 @@ static void _auth(xmpp_conn_t *conn)
strophe_free(conn->ctx, str);
strophe_free(conn->ctx, authid);
xmpp_stanza_add_child(auth, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(auth, authdata, 0);
handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
"PLAIN");
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(auth);
/* SASL PLAIN was tried */
conn->sasl_support &= ~SASL_MASK_PLAIN;
@@ -923,19 +909,15 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
return 0;
}
xmpp_stanza_set_text(text, resource);
xmpp_stanza_add_child(res, text);
xmpp_stanza_release(text);
xmpp_stanza_add_child(bind, res);
xmpp_stanza_release(res);
xmpp_stanza_add_child_ex(res, text, 0);
xmpp_stanza_add_child_ex(bind, res, 0);
strophe_free(conn->ctx, resource);
}
xmpp_stanza_add_child(iq, bind);
xmpp_stanza_release(bind);
xmpp_stanza_add_child_ex(iq, bind, 0);
/* send bind request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(iq);
return 0;
}
@@ -957,6 +939,13 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
if (bind) {
ns = xmpp_stanza_get_ns(bind);
conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
bind = xmpp_stanza_copy(bind);
if (!bind) {
disconnect_mem_error(conn);
return 0;
}
} else {
conn->bind_required = 0;
}
/* check whether session establishment is required */
@@ -974,12 +963,6 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
conn->sm_state->sm_support = 1;
}
bind = xmpp_stanza_copy(bind);
if (!bind) {
disconnect_mem_error(conn);
return 0;
}
/* we are expecting either <bind/> and <session/> since this is a
XMPP style connection or we <resume/> the previous session */
@@ -999,7 +982,6 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
xmpp_stanza_set_attribute(resume, "h", h);
send_stanza(conn, resume, XMPP_QUEUE_SM_STROPHE);
xmpp_stanza_release(resume);
handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
}
/* if bind is required, go ahead and start it */
@@ -1008,7 +990,9 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
_do_bind(conn, bind);
} else {
/* can't bind, disconnect */
xmpp_stanza_release(bind);
if (bind) {
xmpp_stanza_release(bind);
}
strophe_error(conn->ctx, "xmpp",
"Stream features does not allow "
"resource bind.");
@@ -1080,12 +1064,10 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_stanza_set_name(session, "session");
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
xmpp_stanza_add_child(iq, session);
xmpp_stanza_release(session);
xmpp_stanza_add_child_ex(iq, session, 0);
/* send session establishment request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(iq);
}
if (conn->sm_state->sm_support && !conn->sm_disable) {
@@ -1100,7 +1082,6 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_stanza_set_attribute(enable, "resume", "true");
handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
xmpp_stanza_release(enable);
}
if (!conn->session_required) {
@@ -1359,15 +1340,13 @@ static void _auth_legacy(xmpp_conn_t *conn)
goto err_free;
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
xmpp_stanza_add_child(iq, query);
xmpp_stanza_release(query);
xmpp_stanza_add_child_ex(iq, query, 0);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "username");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(query, child, 0);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
@@ -1379,29 +1358,25 @@ static void _auth_legacy(xmpp_conn_t *conn)
}
xmpp_stanza_set_text(authdata, str);
strophe_free(conn->ctx, str);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(child, authdata, 0);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "password");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(query, child, 0);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
goto err_free;
xmpp_stanza_set_text(authdata, conn->pass);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(child, authdata, 0);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "resource");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(query, child, 0);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
@@ -1418,14 +1393,12 @@ static void _auth_legacy(xmpp_conn_t *conn)
xmpp_disconnect(conn);
return;
}
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
xmpp_stanza_add_child_ex(child, authdata, 0);
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL);
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(iq);
return;
err_free:

View File

@@ -198,6 +198,7 @@ struct _xmpp_conn_t {
int error;
xmpp_stream_error_t *stream_error;
xmpp_sock_t *xsock;
sock_t sock;
int ka_timeout; /* TCP keepalive timeout */
int ka_interval; /* TCP keepalive interval */

View File

@@ -29,7 +29,6 @@
#include "common.h"
#include "util.h"
#include "parser.h"
#include "resolver.h"
#ifndef DEFAULT_SEND_QUEUE_MAX
/** @def DEFAULT_SEND_QUEUE_MAX
@@ -103,8 +102,6 @@ static unsigned short _conn_default_port(xmpp_conn_t *conn,
static void _conn_reset(xmpp_conn_t *conn);
static int _conn_connect(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port,
xmpp_conn_type_t type,
xmpp_conn_handler callback,
void *userdata);
@@ -123,8 +120,6 @@ void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text)
xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
send_stanza(conn, error, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(error);
}
/** Create a new Strophe connection object.
@@ -150,7 +145,8 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
conn->type = XMPP_UNKNOWN;
conn->state = XMPP_STATE_DISCONNECTED;
conn->sock = -1;
conn->xsock = NULL;
conn->sock = INVALID_SOCKET;
conn->ka_timeout = KEEPALIVE_TIMEOUT;
conn->ka_interval = KEEPALIVE_INTERVAL;
conn->ka_count = KEEPALIVE_COUNT;
@@ -404,6 +400,7 @@ int xmpp_conn_release(xmpp_conn_t *conn)
if (conn->sm_state)
xmpp_free_sm_state(conn->sm_state);
tls_clear_password_cache(conn);
sock_free(conn->xsock);
strophe_free(ctx, conn);
released = 1;
}
@@ -483,6 +480,8 @@ void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
*/
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
{
if (conn->tls_cafile)
strophe_free(conn->ctx, conn->tls_cafile);
conn->tls_cafile = strophe_strdup(conn->ctx, path);
}
@@ -495,6 +494,8 @@ void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
*/
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
{
if (conn->tls_capath)
strophe_free(conn->ctx, conn->tls_capath);
conn->tls_capath = strophe_strdup(conn->ctx, path);
}
@@ -703,12 +704,7 @@ int xmpp_connect_client(xmpp_conn_t *conn,
xmpp_conn_handler callback,
void *userdata)
{
resolver_srv_rr_t *srv_rr_list = NULL;
resolver_srv_rr_t *rr;
char *domain;
const char *host = NULL;
unsigned short port = 0;
int found = XMPP_DOMAIN_NOT_FOUND;
int rc;
if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) {
@@ -737,48 +733,35 @@ int xmpp_connect_client(xmpp_conn_t *conn,
if (!conn->sm_state) {
conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
if (!conn->sm_state)
return XMPP_EMEM;
goto err_mem;
memset(conn->sm_state, 0, sizeof(*conn->sm_state));
conn->sm_state->ctx = conn->ctx;
}
if (altdomain != NULL) {
strophe_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
host = altdomain;
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
found = XMPP_DOMAIN_ALTDOMAIN;
if (altdomain != NULL)
strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
if (conn->tls_legacy_ssl && !altdomain) {
/* SSL tunneled connection on 5223 port is legacy and doesn't
* have an SRV record. */
} else if (!conn->tls_legacy_ssl) {
found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain,
&srv_rr_list);
altdomain = domain;
}
altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
if (XMPP_DOMAIN_NOT_FOUND == found) {
strophe_debug(conn->ctx, "xmpp",
"SRV lookup failed, "
"connecting via domain.");
host = domain;
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
found = XMPP_DOMAIN_ALTDOMAIN;
}
rr = srv_rr_list;
do {
if (XMPP_DOMAIN_FOUND == found && rr != NULL) {
host = rr->target;
port = rr->port;
rr = rr->next;
}
rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, callback,
userdata);
} while (rc != 0 && rr != NULL);
if (conn->xsock)
sock_free(conn->xsock);
conn->xsock = sock_new(conn, domain, altdomain, altport);
if (!conn->xsock)
goto err_mem;
rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
strophe_free(conn->ctx, domain);
resolver_srv_free(conn->ctx, srv_rr_list);
return rc;
err_mem:
strophe_free(conn->ctx, domain);
return XMPP_EMEM;
}
/** Initiate a component connection to server.
@@ -824,10 +807,15 @@ int xmpp_connect_component(xmpp_conn_t *conn,
}
port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
if (conn->xsock)
sock_free(conn->xsock);
conn->xsock = sock_new(conn, NULL, server, port);
if (!conn->xsock)
return XMPP_EMEM;
/* JID serves as an identifier here and will be used as "to" attribute
of the stream */
return _conn_connect(conn, conn->jid, server, port, XMPP_COMPONENT,
callback, userdata);
return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
}
/** Initiate a raw connection to the XMPP server.
@@ -975,7 +963,8 @@ void conn_disconnect(xmpp_conn_t *conn)
tls_free(conn->tls);
conn->tls = NULL;
}
sock_close(conn->sock);
if (conn->sock != INVALID_SOCKET)
sock_close(conn->sock);
_reset_sm_state_for_reconnect(conn);
/* fire off connection handler */
@@ -1073,7 +1062,7 @@ void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len)
*/
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
{
send_stanza(conn, stanza, XMPP_QUEUE_USER);
send_stanza(conn, xmpp_stanza_clone(stanza), XMPP_QUEUE_USER);
}
/** Send the opening &lt;stream:stream&gt; tag to the server.
@@ -1738,7 +1727,6 @@ static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
xmpp_stanza_set_attribute(a, "h", h);
send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
xmpp_stanza_release(a);
} else if (strcmp(name, "a") == 0) {
attr_h = xmpp_stanza_get_attribute(stanza, "h");
if (!attr_h) {
@@ -1844,8 +1832,6 @@ static void _conn_reset(xmpp_conn_t *conn)
static int _conn_connect(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port,
xmpp_conn_type_t type,
xmpp_conn_handler callback,
void *userdata)
@@ -1856,8 +1842,6 @@ static int _conn_connect(xmpp_conn_t *conn,
return XMPP_EINVOP;
if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
return XMPP_EINVOP;
if (host == NULL || port == 0)
return XMPP_EINT;
_conn_reset(conn);
@@ -1866,10 +1850,8 @@ static int _conn_connect(xmpp_conn_t *conn,
if (!conn->domain)
return XMPP_EMEM;
conn->sock = sock_connect(conn, host, port);
strophe_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d",
host, port, conn->sock);
if (conn->sock == -1)
conn->sock = sock_connect(conn->xsock);
if (conn->sock == INVALID_SOCKET)
return XMPP_EINT;
/* setup handler */
@@ -1888,7 +1870,6 @@ static int _conn_connect(xmpp_conn_t *conn,
conn->state = XMPP_STATE_CONNECTING;
conn->timeout_stamp = time_stamp();
strophe_debug(conn->ctx, "xmpp", "Attempting to connect to %s", host);
return 0;
}
@@ -1972,14 +1953,16 @@ void send_stanza(xmpp_conn_t *conn,
size_t len;
if (conn->state != XMPP_STATE_CONNECTED)
return;
goto out;
if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
return;
goto out;
}
_send_raw(conn, buf, len, owner, NULL);
out:
xmpp_stanza_release(stanza);
}
void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)

View File

@@ -60,6 +60,18 @@
/** Max buffer size for receiving messages. */
#define STROPE_MESSAGE_BUFFER_SIZE 4096
static int _connect_next(xmpp_conn_t *conn)
{
sock_close(conn->sock);
conn->sock = sock_connect(conn->xsock);
if (conn->sock == INVALID_SOCKET)
return -1;
conn->timeout_stamp = time_stamp();
return 0;
}
/** Run the event loop once.
* This function will run send any data that has been queued by
* xmpp_send and related functions and run through the Strophe even
@@ -209,9 +221,14 @@ next_item:
conn->connect_timeout)
FD_SET(conn->sock, &wfds);
else {
conn->error = ETIMEDOUT;
strophe_info(ctx, "xmpp", "Connection attempt timed out.");
conn_disconnect(conn);
ret = _connect_next(conn);
if (ret != 0) {
conn->error = ETIMEDOUT;
conn_disconnect(conn);
} else {
FD_SET(conn->sock, &wfds);
}
}
break;
case XMPP_STATE_CONNECTED:
@@ -272,7 +289,11 @@ next_item:
/* connection failed */
strophe_debug(ctx, "xmpp", "connection failed, error %d",
ret);
conn_disconnect(conn);
ret = _connect_next(conn);
if (ret != 0) {
conn->error = ret;
conn_disconnect(conn);
}
break;
}

View File

@@ -37,8 +37,10 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
size_t len, nlen, dlen, rlen;
/* jid must at least have a domain */
if (domain == NULL)
if (domain == NULL) {
strophe_error(ctx, "jid", "domainpart missing.");
return NULL;
}
/* accumulate lengths */
dlen = strlen(domain);
@@ -46,6 +48,26 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
rlen = (resource) ? strlen(resource) + 1 : 0;
len = nlen + dlen + rlen;
if (dlen > 1023) {
strophe_error(ctx, "jid", "domainpart too long.");
return NULL;
}
if (nlen > 1024) {
strophe_error(ctx, "jid", "localpart too long.");
return NULL;
}
if (rlen > 1024) {
strophe_error(ctx, "jid", "resourcepart too long.");
return NULL;
}
if (node) {
if (strcspn(node, "\"&'/:<>@") != nlen - 1) {
strophe_error(ctx, "jid", "localpart contained invalid character.");
return NULL;
}
}
/* concat components */
result = strophe_alloc(ctx, len + 1);
if (result != NULL) {
@@ -96,17 +118,29 @@ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
*/
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
{
char *dup_jid = strophe_strdup(ctx, jid);
char *result = NULL;
const char *c;
c = strchr(jid, '@');
/* Apply the same parsing rules from rfc7622 Section 3.2
* 1. Strip resource
* 2. take part before the '@'
*/
char *resource = strchr(dup_jid, '/');
if (resource != NULL) {
*resource = '\0';
}
c = strchr(dup_jid, '@');
if (c != NULL) {
result = strophe_alloc(ctx, (c - jid) + 1);
result = strophe_alloc(ctx, (c - dup_jid) + 1);
if (result != NULL) {
memcpy(result, jid, (c - jid));
result[c - jid] = '\0';
memcpy(result, dup_jid, (c - dup_jid));
result[c - dup_jid] = '\0';
}
}
strophe_free(ctx, dup_jid);
return result;
}
@@ -120,24 +154,29 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
*/
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
{
char *result = NULL;
const char *c;
size_t dlen;
char *dup_jid = strophe_strdup(ctx, jid);
c = strchr(jid, '@');
if (c == NULL) {
/* no node, assume domain */
c = jid;
/* rfc7622 Section 3.2
* 1. Remove any portion from the first '/' character to the end of the
* string (if there is a '/' character present).
*/
char *resource = strchr(dup_jid, '/');
if (resource != NULL) {
*resource = '\0';
}
/* 2. Remove any portion from the beginning of the string to the first
* '@' character (if there is an '@' character present).
*/
char *at_sign = strchr(dup_jid, '@');
char *result = NULL;
if (at_sign != NULL) {
result = strophe_strdup(ctx, (at_sign + 1));
} else {
/* advance past the separator */
c++;
}
dlen = strcspn(c, "/"); /* do not include resource */
result = strophe_alloc(ctx, dlen + 1);
if (result != NULL) {
memcpy(result, c, dlen);
result[dlen] = '\0';
result = strophe_strdup(ctx, dup_jid);
}
strophe_free(ctx, dup_jid);
return result;
}

View File

@@ -152,8 +152,7 @@ static void complete_inner_text(parser_t *parser)
/* FIXME: disconnect on allocation error */
if (stanza) {
xmpp_stanza_set_text(stanza, parser->inner_text);
xmpp_stanza_add_child(parser->stanza, stanza);
xmpp_stanza_release(stanza);
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
}
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
@@ -195,8 +194,7 @@ _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
if (parser->stanza != NULL) {
complete_inner_text(parser);
xmpp_stanza_add_child(parser->stanza, child);
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
}
parser->stanza = child;
}

View File

@@ -164,10 +164,7 @@ static void _start_element(void *userdata,
xmpp_stanza_set_ns(child, (char *)uri);
/* add child to parent */
xmpp_stanza_add_child(parser->stanza, child);
/* the child is owned by the toplevel stanza now */
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
/* make child the current stanza */
parser->stanza = child;
@@ -223,8 +220,7 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
}
xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
xmpp_stanza_add_child(parser->stanza, stanza);
xmpp_stanza_release(stanza);
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
}
/* create a new parser */

View File

@@ -91,6 +91,25 @@ void resolver_shutdown(void)
#endif
}
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
const char *host,
unsigned short port,
unsigned short prio,
unsigned short weight)
{
resolver_srv_rr_t *rr = strophe_alloc(ctx, sizeof(*rr));
if (rr) {
memset(rr, 0, sizeof(*rr));
rr->port = port;
rr->priority = prio;
rr->weight = weight;
if (host) {
snprintf(rr->target, sizeof(rr->target), "%s", host);
}
}
return rr;
}
static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
{
resolver_srv_rr_t *rr_head;
@@ -158,6 +177,10 @@ int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
#else
set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list);
if (set != XMPP_DOMAIN_FOUND && *srv_rr_list != NULL) {
resolver_srv_free(ctx, *srv_rr_list);
*srv_rr_list = NULL;
}
#endif
resolver_srv_list_sort(srv_rr_list);
@@ -434,17 +457,19 @@ static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
j += 10;
if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
rr = strophe_alloc(ctx, sizeof(*rr));
rr->next = *srv_rr_list;
rr->priority = xmpp_ntohs_ptr(&buf[j]);
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
name_len = message_name_get(buf, len, j + 6, rr->target,
sizeof(rr->target));
if (name_len > 0)
*srv_rr_list = rr;
else
strophe_free(ctx, rr); /* skip broken record */
rr = resolver_srv_rr_new(ctx, NULL, 0, 0, 0);
if (rr) {
rr->next = *srv_rr_list;
rr->priority = xmpp_ntohs_ptr(&buf[j]);
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
name_len = message_name_get(buf, len, j + 6, rr->target,
sizeof(rr->target));
if (name_len > 0)
*srv_rr_list = rr;
else
strophe_free(ctx, rr); /* skip broken record */
}
}
j += rdlength;
}

View File

@@ -36,6 +36,12 @@ typedef struct resolver_srv_rr_struc {
void resolver_initialize(void);
void resolver_shutdown(void);
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
const char *host,
unsigned short port,
unsigned short prio,
unsigned short weight);
/** Perform lookup for RFC1035 message format.
* This function allocates all elements.
*

View File

@@ -24,6 +24,7 @@
#include <iphlpapi.h>
#include <mstcpip.h> /* tcp_keepalive */
#else
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
@@ -34,6 +35,18 @@
#endif
#include "common.h"
#include "resolver.h"
struct _xmpp_sock_t {
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
struct addrinfo *ainfo_list;
struct addrinfo *ainfo_cur;
resolver_srv_rr_t *srv_rr_list;
resolver_srv_rr_t *srv_rr_cur;
const char *host;
unsigned short port;
};
void sock_initialize(void)
{
@@ -68,50 +81,164 @@ static int _in_progress(int error)
#endif
}
sock_t sock_connect(xmpp_conn_t *conn, const char *host, unsigned short port)
static void sock_getaddrinfo(xmpp_sock_t *xsock)
{
sock_t sock;
char service[6];
struct addrinfo *res, *ainfo, hints;
int err;
struct addrinfo hints;
int rc;
strophe_snprintf(service, 6, "%u", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(host, service, &hints, &res);
if (err != 0)
return -1;
for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock < 0)
continue;
if (conn->sockopt_cb != NULL)
err = (conn->sockopt_cb)(conn, &sock);
if (err != 0) {
sock_close(sock);
continue;
}
err = sock_set_nonblocking(sock);
if (err == 0) {
err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
if (err == 0 || _in_progress(sock_error()))
break;
}
sock_close(sock);
if (xsock->ainfo_list) {
freeaddrinfo(xsock->ainfo_list);
xsock->ainfo_list = NULL;
}
freeaddrinfo(res);
sock = ainfo == NULL ? -1 : sock;
if (xsock->srv_rr_cur) {
/* Cache host and port for debug logs. */
xsock->host = xsock->srv_rr_cur->target;
xsock->port = xsock->srv_rr_cur->port;
strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
&xsock->ainfo_list);
if (rc != 0) {
strophe_debug(xsock->ctx, "sock", "getaddrinfo() failed with %d",
rc);
xsock->ainfo_list = NULL;
}
}
xsock->ainfo_cur = xsock->ainfo_list;
}
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port)
{
xmpp_ctx_t *ctx = conn->ctx;
xmpp_sock_t *xsock;
int found = XMPP_DOMAIN_NOT_FOUND;
xsock = strophe_alloc(ctx, sizeof(*xsock));
if (!xsock) {
return NULL;
}
xsock->ctx = ctx;
xsock->conn = conn;
xsock->host = NULL;
xsock->port = 0;
if (!host) {
found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
&xsock->srv_rr_list);
if (XMPP_DOMAIN_NOT_FOUND == found)
strophe_debug(ctx, "sock",
"SRV lookup failed, connecting via domain.");
}
if (XMPP_DOMAIN_NOT_FOUND == found) {
/* Resolution failed or the host is provided explicitly. */
xsock->srv_rr_list =
resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
}
xsock->srv_rr_cur = xsock->srv_rr_list;
xsock->ainfo_list = NULL;
sock_getaddrinfo(xsock);
if (xsock->srv_rr_cur)
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
return xsock;
}
void sock_free(xmpp_sock_t *xsock)
{
if (!xsock)
return;
if (xsock->ainfo_list)
freeaddrinfo(xsock->ainfo_list);
if (xsock->srv_rr_list)
resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
strophe_free(xsock->ctx, xsock);
}
static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
{
buf[0] = '\0';
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
buflen);
break;
default:
strophe_snprintf(buf, buflen, "<Unknown>");
}
return buf;
}
sock_t sock_connect(xmpp_sock_t *xsock)
{
struct addrinfo *ainfo;
sock_t sock;
int rc = 0;
char buf[64];
do {
if (!xsock->ainfo_cur) {
sock_getaddrinfo(xsock);
if (xsock->srv_rr_cur)
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
}
if (!xsock->ainfo_cur) {
/* We tried all available addresses. */
return INVALID_SOCKET;
}
ainfo = xsock->ainfo_cur;
strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
xsock->host, xsock->port,
_sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock != INVALID_SOCKET) {
if (xsock->conn->sockopt_cb) {
/* Don't allow user to overwrite sockfd value. */
sock_t sock_copy = sock;
rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
if (rc != 0) {
strophe_debug(xsock->ctx, "sock",
"User's setsockopt callback"
"failed with %d (errno=%d)",
rc, errno);
}
}
if (rc == 0)
rc = sock_set_nonblocking(sock);
if (rc == 0)
rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
/* Assume only connect() can cause "in progress" error. */
if (rc != 0 && !_in_progress(sock_error())) {
sock_close(sock);
sock = INVALID_SOCKET;
}
}
strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
} while (sock == INVALID_SOCKET);
return sock;
}

View File

@@ -20,6 +20,7 @@
#ifndef _WIN32
typedef int sock_t;
#define INVALID_SOCKET (-1)
#else
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -28,12 +29,19 @@ typedef int sock_t;
typedef SOCKET sock_t;
#endif
typedef struct _xmpp_sock_t xmpp_sock_t;
void sock_initialize(void);
void sock_shutdown(void);
int sock_error(void);
sock_t sock_connect(xmpp_conn_t *conn, const char *host, unsigned short port);
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port);
void sock_free(xmpp_sock_t *xsock);
sock_t sock_connect(xmpp_sock_t *xsock);
int sock_close(sock_t sock);
int sock_set_blocking(sock_t sock);

View File

@@ -182,6 +182,7 @@ int xmpp_stanza_release(xmpp_stanza_t *stanza)
while (child) {
tchild = child;
child = child->next;
tchild->next = NULL;
xmpp_stanza_release(tchild);
}
@@ -1604,8 +1605,7 @@ xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
}
xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
xmpp_stanza_add_child(error, error_type);
xmpp_stanza_release(error_type);
xmpp_stanza_add_child_ex(error, error_type, 0);
if (text) {
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
@@ -1615,11 +1615,9 @@ xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
xmpp_stanza_set_text(content, text);
xmpp_stanza_add_child(error_text, content);
xmpp_stanza_release(content);
xmpp_stanza_add_child_ex(error_text, content, 0);
xmpp_stanza_add_child(error, error_text);
xmpp_stanza_release(error_text);
xmpp_stanza_add_child_ex(error, error_text, 0);
}
return error;

View File

@@ -34,17 +34,19 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
#define COMPARE(v1, v2) \
do { \
const char *__v1 = v1; \
const char *__v2 = v2; \
if (!__v1 || !__v2 || strcmp(__v1, __v2) != 0) { \
printf("Error: %s\n" \
"Expected: %s\n" \
"Got: %s\n", \
#v1 " != " #v2, __v1, __v2); \
exit(1); \
} \
#define COMPARE(v1, v2) \
do { \
const char *__v1 = v1; \
const char *__v2 = v2; \
if ((__v1 == NULL) && (__v2 == NULL)) { \
/* noop */ \
} else if (!__v1 || !__v2 || strcmp(__v1, __v2) != 0) { \
printf("Error: %s\n" \
"Expected: %s\n" \
"Got: %s\n", \
#v1 " != " #v2, __v1, __v2); \
exit(1); \
} \
} while (0)
#define COMPARE_BUF(v1, len1, v2, len2) \

View File

@@ -15,114 +15,80 @@
#include "strophe.h"
#include "common.h"
static const char jid1[] = "foo@bar.com";
static const char jid2[] = "anyone@example.com/hullo";
static const char jid3[] = "manic.porter@xyz.net/frob";
static const char jid4[] = "domain.tld";
#include "test.h"
static const char *_s(const char *s)
{
return s == NULL ? "<NULL>" : s;
}
int test_jid(xmpp_ctx_t *ctx)
static int test_jid(xmpp_ctx_t *ctx)
{
char *bare;
char *node;
char *domain;
char *resource;
size_t n;
struct {
const char *jid;
const char *bare;
const char *node;
const char *domain;
const char *resource;
} testcases[] = {
{"foo@bar.com", "foo@bar.com", "foo", "bar.com", NULL},
{
"anyone@example.com/hullo",
"anyone@example.com",
"anyone",
"example.com",
"hullo",
},
{
"a.example.com/b@example.net",
"a.example.com",
NULL,
"a.example.com",
"b@example.net",
},
{
"manic.porter@xyz.net/frob",
"manic.porter@xyz.net",
"manic.porter",
"xyz.net",
"frob",
},
{
"domain.tld",
"domain.tld",
NULL,
"domain.tld",
NULL,
},
};
bare = xmpp_jid_bare(ctx, jid1);
node = xmpp_jid_node(ctx, jid1);
domain = xmpp_jid_domain(ctx, jid1);
resource = xmpp_jid_resource(ctx, jid1);
printf("jid '%s' parsed to %s, %s, %s\n", jid1, _s(node), _s(domain),
_s(resource));
if (bare == NULL || strcmp(bare, "foo@bar.com"))
return 1;
if (node == NULL || strcmp(node, "foo"))
return 1;
if (domain == NULL || strcmp(domain, "bar.com"))
return 1;
if (resource != NULL)
return 1;
if (bare)
strophe_free(ctx, bare);
if (node)
strophe_free(ctx, node);
if (domain)
strophe_free(ctx, domain);
if (resource)
strophe_free(ctx, resource);
for (n = 0; n < sizeof(testcases) / sizeof(testcases[0]); ++n) {
bare = xmpp_jid_bare(ctx, testcases[n].jid);
node = xmpp_jid_node(ctx, testcases[n].jid);
domain = xmpp_jid_domain(ctx, testcases[n].jid);
resource = xmpp_jid_resource(ctx, testcases[n].jid);
printf("jid '%s' parsed to %s, %s, %s\n", testcases[n].jid, _s(node),
_s(domain), _s(resource));
COMPARE(testcases[n].bare, bare);
COMPARE(testcases[n].node, node);
COMPARE(testcases[n].domain, domain);
COMPARE(testcases[n].resource, resource);
if (bare)
strophe_free(ctx, bare);
if (node)
strophe_free(ctx, node);
if (domain)
strophe_free(ctx, domain);
if (resource)
strophe_free(ctx, resource);
}
bare = xmpp_jid_bare(ctx, jid2);
node = xmpp_jid_node(ctx, jid2);
domain = xmpp_jid_domain(ctx, jid2);
resource = xmpp_jid_resource(ctx, jid2);
printf("jid '%s' parsed to %s, %s, %s\n", jid2, _s(node), _s(domain),
_s(resource));
if (bare == NULL || strcmp(bare, "anyone@example.com"))
return 1;
if (node == NULL || strcmp(node, "anyone"))
return 1;
if (domain == NULL || strcmp(domain, "example.com"))
return 1;
if (resource == NULL || strcmp(resource, "hullo"))
return 1;
if (bare)
strophe_free(ctx, bare);
if (node)
strophe_free(ctx, node);
if (domain)
strophe_free(ctx, domain);
if (resource)
strophe_free(ctx, resource);
bare = xmpp_jid_bare(ctx, jid3);
node = xmpp_jid_node(ctx, jid3);
domain = xmpp_jid_domain(ctx, jid3);
resource = xmpp_jid_resource(ctx, jid3);
printf("jid '%s' parsed to %s, %s, %s\n", jid3, _s(node), _s(domain),
_s(resource));
if (bare == NULL || strcmp(bare, "manic.porter@xyz.net"))
return 1;
if (node == NULL || strcmp(node, "manic.porter"))
return 1;
if (domain == NULL || strcmp(domain, "xyz.net"))
return 1;
if (resource == NULL || strcmp(resource, "frob"))
return 1;
if (bare)
strophe_free(ctx, bare);
if (node)
strophe_free(ctx, node);
if (domain)
strophe_free(ctx, domain);
if (resource)
strophe_free(ctx, resource);
bare = xmpp_jid_bare(ctx, jid4);
node = xmpp_jid_node(ctx, jid4);
domain = xmpp_jid_domain(ctx, jid4);
resource = xmpp_jid_resource(ctx, jid4);
printf("jid '%s' parsed to %s, %s, %s\n", jid4, _s(node), _s(domain),
_s(resource));
if (bare == NULL || strcmp(bare, "domain.tld"))
return 1;
if (node != NULL)
return 1;
if (domain == NULL || strcmp(domain, "domain.tld"))
return 1;
if (resource != NULL)
return 1;
if (bare)
strophe_free(ctx, bare);
if (node)
strophe_free(ctx, node);
if (domain)
strophe_free(ctx, domain);
if (resource)
strophe_free(ctx, resource);
printf("test_jid() finished successfully\n");
return 0;
}
@@ -143,6 +109,19 @@ int test_jid_new(xmpp_ctx_t *ctx)
return 1;
strophe_free(ctx, jid);
const char *invalid_chars = "\"&'/:<>@";
char localpart[] = "localpart";
do {
localpart[1] = *invalid_chars;
jid = xmpp_jid_new(ctx, localpart, "bar.com", NULL);
if (jid != NULL) {
printf("Shouldn't have created JID with localpart=\"%s\"\n",
localpart);
return 1;
}
invalid_chars++;
} while (*invalid_chars != '\0');
return 0;
}