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: on:
push: push:
branches: [master] branches:
- master
- next
pull_request: pull_request:
branches: [master] branches: [master]
@@ -95,13 +97,18 @@ jobs:
name: Check if release would work name: Check if release would work
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install dependencies - name: install dependencies & bootstrap
run: | run: |
sudo apt update sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev sudo apt install -y libtool pkg-config libexpat1-dev dash
- name: Setup the library
run: |
./bootstrap.sh ./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 ./configure
- name: Try release & tests - name: Try release & tests
run: | 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> 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 0.12.2
- Fix reconnect issues when Stream Management is enabled (#211) - Fix reconnect issues when Stream Management is enabled (#211)
- Fix resolver ... this time for real hopefully (fixup of #200) (#214) - 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_maj], [0])
m4_define([v_min], [12]) 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([project_version], [v_maj.v_min.v_patch])
m4_define([lt_cur], m4_eval(v_maj + v_min)) 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])]) AC_DEFINE([VA_LIST_IS_ARRAY], [1],[Define if va_list is an array type])])
fi 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], PKG_CHECK_MODULES([gnutls], [gnutls],
[PC_REQUIRES="gnutls ${PC_REQUIRES}"], [PC_REQUIRES="gnutls ${PC_REQUIRES}"],
[AC_CHECK_HEADER([gnutls/gnutls.h], [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])] [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 if test "x$PLATFORM" != xwin32; then
AC_MSG_ERROR([schannel is only supported on Windows]) AC_MSG_ERROR([schannel is only supported on Windows])
fi fi

View File

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

View File

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

View File

@@ -29,7 +29,6 @@
#include "common.h" #include "common.h"
#include "util.h" #include "util.h"
#include "parser.h" #include "parser.h"
#include "resolver.h"
#ifndef DEFAULT_SEND_QUEUE_MAX #ifndef DEFAULT_SEND_QUEUE_MAX
/** @def 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 void _conn_reset(xmpp_conn_t *conn);
static int _conn_connect(xmpp_conn_t *conn, static int _conn_connect(xmpp_conn_t *conn,
const char *domain, const char *domain,
const char *host,
unsigned short port,
xmpp_conn_type_t type, xmpp_conn_type_t type,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *userdata); 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); xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
send_stanza(conn, error, XMPP_QUEUE_STROPHE); send_stanza(conn, error, XMPP_QUEUE_STROPHE);
xmpp_stanza_release(error);
} }
/** Create a new Strophe connection object. /** 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->type = XMPP_UNKNOWN;
conn->state = XMPP_STATE_DISCONNECTED; conn->state = XMPP_STATE_DISCONNECTED;
conn->sock = -1; conn->xsock = NULL;
conn->sock = INVALID_SOCKET;
conn->ka_timeout = KEEPALIVE_TIMEOUT; conn->ka_timeout = KEEPALIVE_TIMEOUT;
conn->ka_interval = KEEPALIVE_INTERVAL; conn->ka_interval = KEEPALIVE_INTERVAL;
conn->ka_count = KEEPALIVE_COUNT; conn->ka_count = KEEPALIVE_COUNT;
@@ -404,6 +400,7 @@ int xmpp_conn_release(xmpp_conn_t *conn)
if (conn->sm_state) if (conn->sm_state)
xmpp_free_sm_state(conn->sm_state); xmpp_free_sm_state(conn->sm_state);
tls_clear_password_cache(conn); tls_clear_password_cache(conn);
sock_free(conn->xsock);
strophe_free(ctx, conn); strophe_free(ctx, conn);
released = 1; 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) 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); 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) 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); conn->tls_capath = strophe_strdup(conn->ctx, path);
} }
@@ -703,12 +704,7 @@ int xmpp_connect_client(xmpp_conn_t *conn,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *userdata) void *userdata)
{ {
resolver_srv_rr_t *srv_rr_list = NULL;
resolver_srv_rr_t *rr;
char *domain; char *domain;
const char *host = NULL;
unsigned short port = 0;
int found = XMPP_DOMAIN_NOT_FOUND;
int rc; int rc;
if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) { 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) { if (!conn->sm_state) {
conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state)); conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
if (!conn->sm_state) if (!conn->sm_state)
return XMPP_EMEM; goto err_mem;
memset(conn->sm_state, 0, sizeof(*conn->sm_state)); memset(conn->sm_state, 0, sizeof(*conn->sm_state));
conn->sm_state->ctx = conn->ctx; conn->sm_state->ctx = conn->ctx;
} }
if (altdomain != NULL) { if (altdomain != NULL)
strophe_debug(conn->ctx, "xmpp", "Connecting via altdomain."); strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
host = altdomain;
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
found = XMPP_DOMAIN_ALTDOMAIN;
if (conn->tls_legacy_ssl && !altdomain) {
/* SSL tunneled connection on 5223 port is legacy and doesn't /* SSL tunneled connection on 5223 port is legacy and doesn't
* have an SRV record. */ * have an SRV record. */
} else if (!conn->tls_legacy_ssl) { altdomain = domain;
found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain,
&srv_rr_list);
} }
altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
if (XMPP_DOMAIN_NOT_FOUND == found) { if (conn->xsock)
strophe_debug(conn->ctx, "xmpp", sock_free(conn->xsock);
"SRV lookup failed, " conn->xsock = sock_new(conn, domain, altdomain, altport);
"connecting via domain."); if (!conn->xsock)
host = domain; goto err_mem;
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);
rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
strophe_free(conn->ctx, domain); strophe_free(conn->ctx, domain);
resolver_srv_free(conn->ctx, srv_rr_list);
return rc; return rc;
err_mem:
strophe_free(conn->ctx, domain);
return XMPP_EMEM;
} }
/** Initiate a component connection to server. /** 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); 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 /* JID serves as an identifier here and will be used as "to" attribute
of the stream */ of the stream */
return _conn_connect(conn, conn->jid, server, port, XMPP_COMPONENT, return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
callback, userdata);
} }
/** Initiate a raw connection to the XMPP server. /** Initiate a raw connection to the XMPP server.
@@ -975,7 +963,8 @@ void conn_disconnect(xmpp_conn_t *conn)
tls_free(conn->tls); tls_free(conn->tls);
conn->tls = NULL; conn->tls = NULL;
} }
sock_close(conn->sock); if (conn->sock != INVALID_SOCKET)
sock_close(conn->sock);
_reset_sm_state_for_reconnect(conn); _reset_sm_state_for_reconnect(conn);
/* fire off connection handler */ /* 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) 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. /** 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); strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
xmpp_stanza_set_attribute(a, "h", h); xmpp_stanza_set_attribute(a, "h", h);
send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE); send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
xmpp_stanza_release(a);
} else if (strcmp(name, "a") == 0) { } else if (strcmp(name, "a") == 0) {
attr_h = xmpp_stanza_get_attribute(stanza, "h"); attr_h = xmpp_stanza_get_attribute(stanza, "h");
if (!attr_h) { if (!attr_h) {
@@ -1844,8 +1832,6 @@ static void _conn_reset(xmpp_conn_t *conn)
static int _conn_connect(xmpp_conn_t *conn, static int _conn_connect(xmpp_conn_t *conn,
const char *domain, const char *domain,
const char *host,
unsigned short port,
xmpp_conn_type_t type, xmpp_conn_type_t type,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *userdata) void *userdata)
@@ -1856,8 +1842,6 @@ static int _conn_connect(xmpp_conn_t *conn,
return XMPP_EINVOP; return XMPP_EINVOP;
if (type != XMPP_CLIENT && type != XMPP_COMPONENT) if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
return XMPP_EINVOP; return XMPP_EINVOP;
if (host == NULL || port == 0)
return XMPP_EINT;
_conn_reset(conn); _conn_reset(conn);
@@ -1866,10 +1850,8 @@ static int _conn_connect(xmpp_conn_t *conn,
if (!conn->domain) if (!conn->domain)
return XMPP_EMEM; return XMPP_EMEM;
conn->sock = sock_connect(conn, host, port); conn->sock = sock_connect(conn->xsock);
strophe_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d", if (conn->sock == INVALID_SOCKET)
host, port, conn->sock);
if (conn->sock == -1)
return XMPP_EINT; return XMPP_EINT;
/* setup handler */ /* setup handler */
@@ -1888,7 +1870,6 @@ static int _conn_connect(xmpp_conn_t *conn,
conn->state = XMPP_STATE_CONNECTING; conn->state = XMPP_STATE_CONNECTING;
conn->timeout_stamp = time_stamp(); conn->timeout_stamp = time_stamp();
strophe_debug(conn->ctx, "xmpp", "Attempting to connect to %s", host);
return 0; return 0;
} }
@@ -1972,14 +1953,16 @@ void send_stanza(xmpp_conn_t *conn,
size_t len; size_t len;
if (conn->state != XMPP_STATE_CONNECTED) if (conn->state != XMPP_STATE_CONNECTED)
return; goto out;
if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) { if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
strophe_error(conn->ctx, "conn", "Failed to stanza_to_text"); strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
return; goto out;
} }
_send_raw(conn, buf, len, owner, NULL); _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) 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. */ /** Max buffer size for receiving messages. */
#define STROPE_MESSAGE_BUFFER_SIZE 4096 #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. /** Run the event loop once.
* This function will run send any data that has been queued by * This function will run send any data that has been queued by
* xmpp_send and related functions and run through the Strophe even * xmpp_send and related functions and run through the Strophe even
@@ -209,9 +221,14 @@ next_item:
conn->connect_timeout) conn->connect_timeout)
FD_SET(conn->sock, &wfds); FD_SET(conn->sock, &wfds);
else { else {
conn->error = ETIMEDOUT;
strophe_info(ctx, "xmpp", "Connection attempt timed out."); 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; break;
case XMPP_STATE_CONNECTED: case XMPP_STATE_CONNECTED:
@@ -272,7 +289,11 @@ next_item:
/* connection failed */ /* connection failed */
strophe_debug(ctx, "xmpp", "connection failed, error %d", strophe_debug(ctx, "xmpp", "connection failed, error %d",
ret); ret);
conn_disconnect(conn); ret = _connect_next(conn);
if (ret != 0) {
conn->error = ret;
conn_disconnect(conn);
}
break; break;
} }

View File

@@ -37,8 +37,10 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
size_t len, nlen, dlen, rlen; size_t len, nlen, dlen, rlen;
/* jid must at least have a domain */ /* jid must at least have a domain */
if (domain == NULL) if (domain == NULL) {
strophe_error(ctx, "jid", "domainpart missing.");
return NULL; return NULL;
}
/* accumulate lengths */ /* accumulate lengths */
dlen = strlen(domain); dlen = strlen(domain);
@@ -46,6 +48,26 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx,
rlen = (resource) ? strlen(resource) + 1 : 0; rlen = (resource) ? strlen(resource) + 1 : 0;
len = nlen + dlen + rlen; 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 */ /* concat components */
result = strophe_alloc(ctx, len + 1); result = strophe_alloc(ctx, len + 1);
if (result != NULL) { 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 *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
{ {
char *dup_jid = strophe_strdup(ctx, jid);
char *result = NULL; char *result = NULL;
const char *c; 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) { if (c != NULL) {
result = strophe_alloc(ctx, (c - jid) + 1); result = strophe_alloc(ctx, (c - dup_jid) + 1);
if (result != NULL) { if (result != NULL) {
memcpy(result, jid, (c - jid)); memcpy(result, dup_jid, (c - dup_jid));
result[c - jid] = '\0'; result[c - dup_jid] = '\0';
} }
} }
strophe_free(ctx, dup_jid);
return result; 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 *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
{ {
char *result = NULL; char *dup_jid = strophe_strdup(ctx, jid);
const char *c;
size_t dlen;
c = strchr(jid, '@'); /* rfc7622 Section 3.2
if (c == NULL) { * 1. Remove any portion from the first '/' character to the end of the
/* no node, assume domain */ * string (if there is a '/' character present).
c = jid; */
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 { } else {
/* advance past the separator */ result = strophe_strdup(ctx, dup_jid);
c++;
}
dlen = strcspn(c, "/"); /* do not include resource */
result = strophe_alloc(ctx, dlen + 1);
if (result != NULL) {
memcpy(result, c, dlen);
result[dlen] = '\0';
} }
strophe_free(ctx, dup_jid);
return result; return result;
} }

View File

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

View File

@@ -164,10 +164,7 @@ static void _start_element(void *userdata,
xmpp_stanza_set_ns(child, (char *)uri); xmpp_stanza_set_ns(child, (char *)uri);
/* add child to parent */ /* add child to parent */
xmpp_stanza_add_child(parser->stanza, child); xmpp_stanza_add_child_ex(parser->stanza, child, 0);
/* the child is owned by the toplevel stanza now */
xmpp_stanza_release(child);
/* make child the current stanza */ /* make child the current stanza */
parser->stanza = child; 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_set_text_with_size(stanza, (char *)chr, len);
xmpp_stanza_add_child(parser->stanza, stanza); xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
xmpp_stanza_release(stanza);
} }
/* create a new parser */ /* create a new parser */

View File

@@ -91,6 +91,25 @@ void resolver_shutdown(void)
#endif #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) static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
{ {
resolver_srv_rr_t *rr_head; 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); set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
#else #else
set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list); 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 #endif
resolver_srv_list_sort(srv_rr_list); 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]); rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
j += 10; j += 10;
if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) { if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
rr = strophe_alloc(ctx, sizeof(*rr)); rr = resolver_srv_rr_new(ctx, NULL, 0, 0, 0);
rr->next = *srv_rr_list; if (rr) {
rr->priority = xmpp_ntohs_ptr(&buf[j]); rr->next = *srv_rr_list;
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]); rr->priority = xmpp_ntohs_ptr(&buf[j]);
rr->port = xmpp_ntohs_ptr(&buf[j + 4]); rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
name_len = message_name_get(buf, len, j + 6, rr->target, rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
sizeof(rr->target)); name_len = message_name_get(buf, len, j + 6, rr->target,
if (name_len > 0) sizeof(rr->target));
*srv_rr_list = rr; if (name_len > 0)
else *srv_rr_list = rr;
strophe_free(ctx, rr); /* skip broken record */ else
strophe_free(ctx, rr); /* skip broken record */
}
} }
j += rdlength; j += rdlength;
} }

View File

@@ -36,6 +36,12 @@ typedef struct resolver_srv_rr_struc {
void resolver_initialize(void); void resolver_initialize(void);
void resolver_shutdown(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. /** Perform lookup for RFC1035 message format.
* This function allocates all elements. * This function allocates all elements.
* *

View File

@@ -24,6 +24,7 @@
#include <iphlpapi.h> #include <iphlpapi.h>
#include <mstcpip.h> /* tcp_keepalive */ #include <mstcpip.h> /* tcp_keepalive */
#else #else
#include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
@@ -34,6 +35,18 @@
#endif #endif
#include "common.h" #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) void sock_initialize(void)
{ {
@@ -68,50 +81,164 @@ static int _in_progress(int error)
#endif #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]; char service[6];
struct addrinfo *res, *ainfo, hints; struct addrinfo hints;
int err; int rc;
strophe_snprintf(service, 6, "%u", port); if (xsock->ainfo_list) {
freeaddrinfo(xsock->ainfo_list);
memset(&hints, 0, sizeof(struct addrinfo)); xsock->ainfo_list = NULL;
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);
} }
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; return sock;
} }

View File

@@ -20,6 +20,7 @@
#ifndef _WIN32 #ifndef _WIN32
typedef int sock_t; typedef int sock_t;
#define INVALID_SOCKET (-1)
#else #else
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -28,12 +29,19 @@ typedef int sock_t;
typedef SOCKET sock_t; typedef SOCKET sock_t;
#endif #endif
typedef struct _xmpp_sock_t xmpp_sock_t;
void sock_initialize(void); void sock_initialize(void);
void sock_shutdown(void); void sock_shutdown(void);
int sock_error(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_close(sock_t sock);
int sock_set_blocking(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) { while (child) {
tchild = child; tchild = child;
child = child->next; child = child->next;
tchild->next = NULL;
xmpp_stanza_release(tchild); 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_set_ns(error_type, XMPP_NS_STREAMS_IETF);
xmpp_stanza_add_child(error, error_type); xmpp_stanza_add_child_ex(error, error_type, 0);
xmpp_stanza_release(error_type);
if (text) { if (text) {
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx); 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_ns(error_text, XMPP_NS_STREAMS_IETF);
xmpp_stanza_set_text(content, text); xmpp_stanza_set_text(content, text);
xmpp_stanza_add_child(error_text, content); xmpp_stanza_add_child_ex(error_text, content, 0);
xmpp_stanza_release(content);
xmpp_stanza_add_child(error, error_text); xmpp_stanza_add_child_ex(error, error_text, 0);
xmpp_stanza_release(error_text);
} }
return error; return error;

View File

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

View File

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