22 Commits

Author SHA1 Message Date
Steffen Jaeckel
541c22cb13 Allow configuring compression level
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-11-18 10:13:43 +01:00
Steffen Jaeckel
06f2ba19b4 Introduce generic xmpp_conn_set_*() API
Instead of having a multitude of setter API functions for configuration
options, provide generic ones for the different types we have.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-11-18 10:13:43 +01:00
ArtSin
5690c4ed89 Add long tests and _MIN/_MAX to test_snprintf 2024-10-23 20:15:57 +04:00
ArtSin
62953e9ecf Change int_nums to int in test_snprintf
All format specifiers in `int_fmt` are for `int`, not `long`, and all numbers
in `int_nums` fit into `int`.
2024-10-23 13:55:44 +04:00
Steffen Jaeckel
5142eea2eb SSL_get_peer_certificate() is deprecated since OpenSSL 3.0
Unfortunately OpenSSL didn't implement a warning for deprecated macros, so
this wasn't discovered until now.

Discovered in [0].

OpenSSL patch to improve the situation proposed in [1].

[0] https://github.com/strophe/libstrophe/discussions/244
[1] https://github.com/openssl/openssl/pull/24296

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-05-28 10:18:44 +02:00
Steffen Jaeckel
5dd9e4b0e0 Improve error messages
* be a bit more verbose on `getaddrinfo()` error
* fix missing space

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-05-23 16:07:32 +02:00
Steffen Jaeckel
4ec4f09b35 Fix xssl tests
Make sure to use the custom built TLS library.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-05-23 16:05:51 +02:00
Steffen Jaeckel
e24819a405 Run the xSSL Valgrind test in the same runner
Building the SSL library is too much overhead, let's do this only once
and re-build and run libstrophe with Valgrind enabled in the same runner.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-05-23 16:05:51 +02:00
Steffen Jaeckel
d0d1fa9835 Update xSSL CI jobs
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-05-23 16:05:51 +02:00
Steffen Jaeckel
da1229814c Add support for SCRAM-SHA-512-PLUS
Patch written by @matthias-hmb

This fixes #241

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-04-16 16:11:09 +02:00
Steffen Jaeckel
d82df1273c Add support for SSLKEYLOGFILE with OpenSSL
GnuTLS has support already built-in.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-04-10 17:22:14 +02:00
Steffen Jaeckel
9fef4b7d02 Restore old interface if TLS connection failed
This was reported as creating a segfault in [0]

[0] https://github.com/profanity-im/profanity/issues/1963

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-04-10 17:22:14 +02:00
Steffen Jaeckel
4359536a17 Allow to drop the first element of the send queue
In case we're in disconnected state, allow the user to drop the first
element of the send queue, even if it was already in progress
to be written.

Before this patch, the first element could not be retrieved at all, even
after a disconnect.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-04-10 11:59:36 +02:00
Steffen Jaeckel
03d43132bc Fix SM in case the user drops the first stanza
We have to reset the `r_sent` flag in case we drop the `<r>` stanza,
otherwise the SM `h` request mechanism stops working.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-04-09 16:47:00 +02:00
Steffen Jaeckel
4d59a9fe45 Release libstrophe-0.13.1
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-21 13:47:07 +01:00
Steffen Jaeckel
40f2452fb0 Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-21 13:19:47 +01:00
Steffen Jaeckel
1cf09b1870 Only signal "stream negotiation success" once.
The code-path which checks whether the server requires an "xmpp-session"
was untested and therefore the connection-callback handler was triggered
twice with the state `XMPP_CONN_CONNECT`. This happened if the server
supports stream-management and requires a session.

Reported via https://github.com/profanity-im/profanity/issues/1954

Fixup of c7d410f38b

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-14 11:40:59 +01:00
Steffen Jaeckel
5edc480932 Fix sock_connect()
`rc` must be reset in order to make the `do-while` loop work in case
there's no `sockopt_cb` set.

Fixup of f3460460e9

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-14 11:40:38 +01:00
Steffen Jaeckel
641211ecae Minor improvements
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-12 17:47:24 +01:00
Steffen Jaeckel
d8fd6e6d14 Improve documentation
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-12 17:46:52 +01:00
Omar Polo
9410530507 Replace usage of EBADFD (not in POSIX) 2024-02-05 11:09:25 +01:00
Steffen Jaeckel
d6d71e97f8 Release libstrophe-0.13.0
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-01 09:34:32 +01:00
26 changed files with 787 additions and 360 deletions

View File

@@ -50,28 +50,31 @@ jobs:
strategy: strategy:
matrix: matrix:
xssl_versions: xssl_versions:
- { version: "master", continue: true, libressl: true } - { version: "master", continue: true, libressl: true }
- { version: "OPENBSD_7_4", continue: true, libressl: true } - { version: "OPENBSD_7_5", continue: true, libressl: true }
- { version: "v3.8.1", continue: true, libressl: true } - { version: "v3.9.2", continue: true, libressl: true }
- { version: "OPENBSD_7_3", continue: true, libressl: true } - { version: "OPENBSD_7_4", continue: true, libressl: true }
- { version: "OPENBSD_7_2", continue: true, libressl: true } - { version: "v3.8.4", continue: true, libressl: true }
- { version: "OPENBSD_7_1", continue: true, libressl: true } - { version: "OPENBSD_7_3", continue: true, libressl: true }
- { version: "OPENBSD_7_2", continue: true, libressl: true }
- { version: "OPENBSD_7_1", continue: true, libressl: true }
# https://github.com/libressl-portable/portable/issues/760 # https://github.com/libressl-portable/portable/issues/760
# - { version: "v3.5.2", continue: true, libressl: true } # - { version: "v3.5.2", continue: true, libressl: true }
- { version: "OPENBSD_7_0", continue: true, libressl: true } - { version: "OPENBSD_7_0", continue: true, libressl: true }
# OPENBSD_7_0 is basically the "fixed v3.4.3" # OPENBSD_7_0 is basically the "fixed v3.4.3"
# - { version: "v3.4.3", continue: true, libressl: true } # - { version: "v3.4.3", continue: true, libressl: true }
- { version: "v3.4.2", continue: true, libressl: true } - { version: "v3.4.2", continue: true, libressl: true }
- { version: "OPENBSD_6_9", continue: true, libressl: true } - { version: "OPENBSD_6_9", continue: true, libressl: true }
- { version: "v3.1.5", continue: true, libressl: true } - { version: "v3.1.5", continue: true, libressl: true }
- { version: "v2.1.10", continue: true, libressl: true } - { version: "v2.1.10", continue: true, libressl: true }
- { version: "openssl-3.0", continue: true, libressl: false } - { version: "openssl-3.0", continue: true, libressl: false }
- { version: "openssl-3.0.10", continue: false, libressl: false } - { version: "openssl-3.0.13", continue: false, libressl: false }
- { version: "openssl-3.1", continue: true, libressl: false } - { version: "openssl-3.1", continue: true, libressl: false }
- { version: "openssl-3.1.2", continue: false, libressl: false } - { version: "openssl-3.1.5", continue: false, libressl: false }
valgrind: - { version: "openssl-3.2", continue: true, libressl: false }
- { configure: '' , make: 'check' } - { version: "openssl-3.2.1", continue: false, libressl: false }
- { configure: '--enable-valgrind' , make: 'check-valgrind' } - { version: "openssl-3.3", continue: true, libressl: false }
- { version: "openssl-3.3.0", continue: false, libressl: false }
name: xSSL tests name: xSSL tests
continue-on-error: ${{ matrix.xssl_versions.continue }} continue-on-error: ${{ matrix.xssl_versions.continue }}
steps: steps:
@@ -89,11 +92,19 @@ jobs:
- name: Build the library - name: Build the library
run: | run: |
./bootstrap.sh ./bootstrap.sh
./configure ${{ matrix.valgrind.configure }} PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" CFLAGS="-Werror -g3" --prefix="${HOME}/xssl" PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
make -j$(nproc) make -j$(nproc)
- name: Run tests - name: Run tests
run: | run: |
make -j$(nproc) ${{ matrix.valgrind.make }} LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check
- name: Build the library with Valgrind enabled
run: |
./bootstrap.sh
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure --enable-valgrind CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
make -j$(nproc)
- name: Run tests with Valgrind enabled
run: |
LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check-valgrind
- name: Error logs - name: Error logs
if: ${{ failure() }} if: ${{ failure() }}
run: | run: |

View File

@@ -1,3 +1,19 @@
0.13.1
- Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3 (40f2452)
- Only signal "stream negotiation success" once (1cf09b1)
- Fix `sock_connect()` not looping over all DNS records returned if no `sockopt_cb` is set (5edc480)
- Replace usage of EBADFD, it's not in POSIX (#235)
0.13.0
- Fix connected/connecting signaling to user (#227)
- Fix wording of licensing terms (#225)
- Prepare for future changes in OpenSSL (#226)
- Improve Stream Management (#227) (#230)
- Add SCRAM-PLUS Variants (#228)
- Introduce XEP-0138 stream compression (#231)
- Deprecated the following API (#227):
- xmpp_conn_disable_tls() - replaced by a flag set by xmpp_conn_set_flags()
0.12.3 0.12.3
- Improve TCP-connection establishment (#221) - Improve TCP-connection establishment (#221)
- Handle case where the server doesn't provide the `bind` feature (#224) - Handle case where the server doesn't provide the `bind` feature (#224)
@@ -21,7 +37,7 @@
- Fix some build steps when builddir != srcdir (#208) - Fix some build steps when builddir != srcdir (#208)
- Allow the user to disable build of examples (#209) - Allow the user to disable build of examples (#209)
- CI builds against OpenSSL 3 (#206) - CI builds against OpenSSL 3 (#206)
- Change the call signature of the following API: - Change the call signature of the following API (#208):
- xmpp_conn_set_client_cert() - the PKCS#12 file has now to be passed via the `cert` - xmpp_conn_set_client_cert() - the PKCS#12 file has now to be passed via the `cert`
parameter. Originally it was via `key`. Currently both styles are supported, parameter. Originally it was via `key`. Currently both styles are supported,
but in a future release only passing via `cert` will be accepted. but in a future release only passing via `cert` will be accepted.

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = Strophe
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.12 PROJECT_NUMBER = 0.13
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

View File

@@ -88,7 +88,7 @@ or if you have everything configured properly:
Then open `docs/html/index.html`. Then open `docs/html/index.html`.
An online version of the documentation of the latest release is available on http://strophe.im/libstrophe/ An online version of the documentation of the latest release is available on https://strophe.im/libstrophe/
Releases Releases
-------- --------

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], [13])
m4_define([v_patch], [3]) m4_define([v_patch], [1])
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))

View File

@@ -115,8 +115,8 @@ int main(int argc, char **argv)
*/ */
/* setup authentication information */ /* setup authentication information */
xmpp_conn_set_jid(conn, argv[1]); xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
xmpp_conn_set_pass(conn, argv[2]); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
/* initiate connection */ /* initiate connection */
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx); xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);

View File

@@ -116,9 +116,9 @@ int main(int argc, char **argv)
/* setup authentication information */ /* setup authentication information */
if (jid) if (jid)
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
if (password) if (password)
xmpp_conn_set_pass(conn, password); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
/* initiate connection */ /* initiate connection */
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) { if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {

View File

@@ -293,20 +293,24 @@ create_connection:
xmpp_conn_set_flags(conn, flags); xmpp_conn_set_flags(conn, flags);
/* ask for a password if key is protected */ /* ask for a password if key is protected */
xmpp_conn_set_password_callback(conn, password_callback, NULL); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
/* try at max 3 times in case the user enters the password wrong */ /* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_password_retries(conn, 3); xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
/* setup authentication information */ /* setup authentication information */
if (key) if (key) {
xmpp_conn_set_client_cert(conn, cert, key); xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
}
if (jid) if (jid)
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
if (password) if (password)
xmpp_conn_set_pass(conn, password); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
/* enable TCP keepalive, using canned callback function */ /* enable TCP keepalive, using canned callback function */
if (tcp_keepalive) if (tcp_keepalive)
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
xmpp_sockopt_cb_keepalive);
/* set Stream-Mangement state if available */ /* set Stream-Mangement state if available */
if (sm_state) { if (sm_state) {

View File

@@ -331,27 +331,31 @@ int main(int argc, char **argv)
xmpp_conn_set_flags(conn, flags); xmpp_conn_set_flags(conn, flags);
/* configure TCP keepalive (optional) */ /* configure TCP keepalive (optional) */
if (tcp_keepalive) if (tcp_keepalive)
xmpp_conn_set_sockopt_callback(conn, sockopt_cb); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
sockopt_cb);
/* ask for a password if key is protected */ /* ask for a password if key is protected */
xmpp_conn_set_password_callback(conn, password_callback, NULL); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
/* try at max 3 times in case the user enters the password wrong */ /* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_password_retries(conn, 3); xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
/* setup authentication information */ /* setup authentication information */
if (key) { if (key) {
xmpp_conn_set_client_cert(conn, cert, key); xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
} }
if (jid) if (jid)
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
if (password) if (password)
xmpp_conn_set_pass(conn, password); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
if (certfail) if (certfail)
xmpp_conn_set_certfail_handler(conn, certfail_handler); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER,
certfail_handler);
if (capath) if (capath)
xmpp_conn_set_capath(conn, capath); xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, capath);
if (cafile) if (cafile)
xmpp_conn_set_cafile(conn, cafile); xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, cafile);
/* initiate connection */ /* initiate connection */
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) { if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {

View File

@@ -80,8 +80,8 @@ int main(int argc, char **argv)
conn = xmpp_conn_new(ctx); conn = xmpp_conn_new(ctx);
/* setup authentication information */ /* setup authentication information */
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_pass(conn, pass); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
/* initiate connection */ /* initiate connection */
xmpp_connect_component(conn, host, port, conn_handler, ctx); xmpp_connect_component(conn, host, port, conn_handler, ctx);

View File

@@ -342,7 +342,7 @@ int main(int argc, char **argv)
/* jid can be a jid or domain for "raw" connection */ /* jid can be a jid or domain for "raw" connection */
domain = xmpp_jid_domain(ctx, jid); domain = xmpp_jid_domain(ctx, jid);
xmpp_conn_set_jid(conn, domain); xmpp_conn_set_string(conn, XMPP_SETTING_JID, domain);
xmpp_free(ctx, domain); xmpp_free(ctx, domain);
/* private data */ /* private data */

View File

@@ -118,8 +118,8 @@ int main(int argc, char **argv)
*/ */
/* setup authentication information */ /* setup authentication information */
xmpp_conn_set_jid(conn, argv[1]); xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
xmpp_conn_set_pass(conn, argv[2]); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
/* initiate connection */ /* initiate connection */
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx); xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);

View File

@@ -269,8 +269,8 @@ int main(int argc, char **argv)
log = xmpp_get_default_logger(XMPP_LEVEL_INFO); log = xmpp_get_default_logger(XMPP_LEVEL_INFO);
ctx = xmpp_ctx_new(NULL, log); ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx); conn = xmpp_conn_new(ctx);
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
xmpp_conn_set_pass(conn, pass); xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
vcard.ctx = ctx; vcard.ctx = ctx;
xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard); xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard);
xmpp_run(ctx); xmpp_run(ctx);

View File

@@ -5,7 +5,7 @@ includedir=@includedir@
Name: libstrophe Name: libstrophe
Description: A simple, lightweight C library for writing XMPP clients Description: A simple, lightweight C library for writing XMPP clients
URL: http://strophe.im/libstrophe/ URL: https://strophe.im/libstrophe/
Version: @VERSION@ Version: @VERSION@
Requires: Requires:
Requires.private: @PC_REQUIRES@ Requires.private: @PC_REQUIRES@

View File

@@ -1095,21 +1095,19 @@ static int _handle_features_compress(xmpp_conn_t *conn,
static int static int
_handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
xmpp_stanza_t *bind, *session, *opt; xmpp_stanza_t *bind, *session;
xmpp_stanza_t *resume; xmpp_stanza_t *resume;
const char *ns;
char h[11]; char h[11];
UNUSED(userdata); UNUSED(userdata);
/* remove missing features handler */ /* Remove missing features handler */
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl); xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
/* check whether resource binding is required */ /* Check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name(stanza, "bind"); bind = xmpp_stanza_get_child_by_name_and_ns(stanza, "bind", XMPP_NS_BIND);
if (bind) { if (bind) {
ns = xmpp_stanza_get_ns(bind); conn->bind_required = 1;
conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
bind = xmpp_stanza_copy(bind); bind = xmpp_stanza_copy(bind);
if (!bind) { if (!bind) {
disconnect_mem_error(conn); disconnect_mem_error(conn);
@@ -1119,25 +1117,31 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
conn->bind_required = 0; conn->bind_required = 0;
} }
/* check whether session establishment is required */ /* Check whether session establishment is required.
session = xmpp_stanza_get_child_by_name(stanza, "session"); *
* The mechanism is deprecated, but we still support it.
*
* RFC3921 contains Ch. 3 "Session Establishment".
*
* RFC6121 removes this and explains in Ch. 1.4:
* "Interoperability Note: [...] Implementation and deployment experience
* has shown that this additional step is unnecessary. [...]" */
session = xmpp_stanza_get_child_by_name_and_ns(stanza, "session",
XMPP_NS_SESSION);
if (session) { if (session) {
ns = xmpp_stanza_get_ns(session); conn->session_required =
opt = xmpp_stanza_get_child_by_name(session, "optional"); xmpp_stanza_get_child_by_name(session, "optional") == NULL;
if (!opt)
conn->session_required =
ns != NULL && strcmp(ns, XMPP_NS_SESSION) == 0;
} }
/* Check stream-management support */
if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) { if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) {
/* stream management supported */
conn->sm_state->sm_support = 1; conn->sm_state->sm_support = 1;
} }
/* we are expecting either <bind/> and <session/> since this is a /* We are expecting either <bind/> and optionally <session/> since this is a
XMPP style connection or we <resume/> the previous session */ XMPP style connection or we <resume/> the previous session */
/* check whether we can <resume/> the previous session */ /* Check whether we can <resume/> the previous session */
if (!conn->sm_disable && conn->sm_state->can_resume && if (!conn->sm_disable && conn->sm_state->can_resume &&
conn->sm_state->previd && conn->sm_state->bound_jid) { conn->sm_state->previd && conn->sm_state->bound_jid) {
resume = xmpp_stanza_new(conn->ctx); resume = xmpp_stanza_new(conn->ctx);
@@ -1184,11 +1188,57 @@ static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata)
return 0; return 0;
} }
static void _session_start(xmpp_conn_t *conn)
{
xmpp_stanza_t *session;
xmpp_stanza_t *iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
if (!iq) {
disconnect_mem_error(conn);
return;
}
session = xmpp_stanza_new(conn->ctx);
if (!session) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
/* setup response handlers */
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT, NULL);
xmpp_stanza_set_name(session, "session");
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
xmpp_stanza_add_child_ex(iq, session, 0);
/* send session establishment request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
}
static void _sm_enable(xmpp_conn_t *conn)
{
xmpp_stanza_t *enable = xmpp_stanza_new(conn->ctx);
if (!enable) {
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(enable, "enable");
xmpp_stanza_set_ns(enable, XMPP_NS_SM);
if (!conn->sm_state->dont_request_resume)
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);
conn->sm_state->sm_sent_nr = 0;
conn->sm_state->sm_enabled = 1;
}
static int static int
_handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
const char *type; const char *type;
xmpp_stanza_t *iq, *session, *binding, *jid_stanza, *enable = NULL; xmpp_stanza_t *binding, *jid_stanza;
UNUSED(userdata); UNUSED(userdata);
@@ -1211,58 +1261,19 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
} }
} }
/* send enable directly after the bind request */
if (conn->sm_state->sm_support && !conn->sm_disable) {
enable = xmpp_stanza_new(conn->ctx);
if (!enable) {
disconnect_mem_error(conn);
return 0;
}
xmpp_stanza_set_name(enable, "enable");
xmpp_stanza_set_ns(enable, XMPP_NS_SM);
if (!conn->sm_state->dont_request_resume)
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);
conn->sm_state->sm_sent_nr = 0;
conn->sm_state->sm_enabled = 1;
}
/* establish a session if required */ /* establish a session if required */
if (conn->session_required) { if (conn->session_required) {
/* setup response handlers */ _session_start(conn);
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL); }
handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT, /* send enable directly after the bind request */
NULL); else if (conn->sm_state->sm_support && !conn->sm_disable) {
_sm_enable(conn);
/* send session request */
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
if (!iq) {
disconnect_mem_error(conn);
return 0;
}
session = xmpp_stanza_new(conn->ctx);
if (!session) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return 0;
}
xmpp_stanza_set_name(session, "session");
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
xmpp_stanza_add_child_ex(iq, session, 0);
/* send session establishment request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
} }
/* if there's no xmpp session required and we didn't try to enable /* if there's no xmpp session required and we didn't try to enable
* stream-management, we're done here and the stream-negotiation was * stream-management, we're done here and the stream-negotiation was
* successful * successful
*/ */
if (!conn->session_required && !enable) { else {
_stream_negotiation_success(conn); _stream_negotiation_success(conn);
} }
} else { } else {
@@ -1299,8 +1310,11 @@ _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_disconnect(conn); xmpp_disconnect(conn);
} else if (type && strcmp(type, "result") == 0) { } else if (type && strcmp(type, "result") == 0) {
strophe_debug(conn->ctx, "xmpp", "Session establishment successful."); strophe_debug(conn->ctx, "xmpp", "Session establishment successful.");
if (conn->sm_state->sm_support && !conn->sm_disable) {
_stream_negotiation_success(conn); _sm_enable(conn);
} else {
_stream_negotiation_success(conn);
}
} else { } else {
strophe_error(conn->ctx, "xmpp", strophe_error(conn->ctx, "xmpp",
"Server sent malformed session reply."); "Server sent malformed session reply.");

View File

@@ -175,9 +175,11 @@ struct _xmpp_send_queue_t {
#define SASL_MASK_EXTERNAL (1 << 6) #define SASL_MASK_EXTERNAL (1 << 6)
#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7) #define SASL_MASK_SCRAMSHA1_PLUS (1 << 7)
#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8) #define SASL_MASK_SCRAMSHA256_PLUS (1 << 8)
#define SASL_MASK_SCRAMSHA512_PLUS (1 << 9)
#define SASL_MASK_SCRAM_PLUS \ #define SASL_MASK_SCRAM_PLUS \
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS) (SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS | \
SASL_MASK_SCRAMSHA512_PLUS)
#define SASL_MASK_SCRAM_WEAK \ #define SASL_MASK_SCRAM_WEAK \
(SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512) (SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512)
#define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK) #define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK)
@@ -278,7 +280,8 @@ struct _xmpp_conn_t {
struct { struct {
struct xmpp_compression *state; struct xmpp_compression *state;
int allowed, supported, dont_reset; int allowed, dont_reset, level;
int supported;
} compression; } compression;
char *lang; char *lang;

View File

@@ -63,7 +63,7 @@ static int _conn_decompress(struct xmpp_compression *comp,
break; break;
default: default:
strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret); strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret);
comp->conn->error = EBADFD; comp->conn->error = ret;
conn_disconnect(comp->conn); conn_disconnect(comp->conn);
break; break;
} }
@@ -129,7 +129,7 @@ _compression_write(xmpp_conn_t *conn, const void *buff, size_t len, int flush)
} }
if (ret != Z_OK) { if (ret != Z_OK) {
strophe_error(conn->ctx, "zlib", "deflate error %d", ret); strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
conn->error = EBADFD; conn->error = ret;
conn_disconnect(conn); conn_disconnect(conn);
return ret; return ret;
} }
@@ -227,22 +227,22 @@ int compression_init(xmpp_conn_t *conn)
comp->compression.stream.next_out = comp->compression.buffer; comp->compression.stream.next_out = comp->compression.buffer;
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE; comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
int err = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION); int ret = deflateInit(&comp->compression.stream, conn->compression.level);
if (err != Z_OK) { if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->compression.buffer); strophe_free_and_null(conn->ctx, comp->compression.buffer);
conn->error = EBADFD; conn->error = ret;
conn_disconnect(conn); conn_disconnect(conn);
return err; return ret;
} }
_init_zlib_compression(conn->ctx, &comp->decompression); _init_zlib_compression(conn->ctx, &comp->decompression);
err = inflateInit(&comp->decompression.stream); ret = inflateInit(&comp->decompression.stream);
if (err != Z_OK) { if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->decompression.buffer); strophe_free_and_null(conn->ctx, comp->decompression.buffer);
conn->error = EBADFD; conn->error = ret;
conn_disconnect(conn); conn_disconnect(conn);
return err; return ret;
} }
return 0; return 0;
} }

View File

@@ -167,6 +167,8 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
tls_clear_password_cache(conn); tls_clear_password_cache(conn);
conn->password_retries = 1; conn->password_retries = 1;
conn->compression.level = -1;
conn->parser = conn->parser =
parser_new(conn->ctx, _handle_stream_start, _handle_stream_end, parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
_handle_stream_stanza, conn); _handle_stream_stanza, conn);
@@ -217,32 +219,6 @@ xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
return conn; return conn;
} }
/** Register sockopt callback
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started.
*
* If the connection is already connected, this callback will be called
* immediately.
*
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn The Strophe connection object this callback is being registered
* for
* @param callback a xmpp_sockopt_callback callback function that will receive
* notifications of connection status
*
* @ingroup Connections
*/
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback)
{
conn->sockopt_cb = callback;
if (conn->state != XMPP_STATE_DISCONNECTED)
callback(conn, &conn->sock);
}
/** Release a Strophe connection object. /** Release a Strophe connection object.
* Decrement the reference count by one for a connection, freeing the * Decrement the reference count by one for a connection, freeing the
* connection object if the count reaches 0. * connection object if the count reaches 0.
@@ -366,6 +342,204 @@ int xmpp_conn_release(xmpp_conn_t *conn)
return released; return released;
} }
/** Configure a connection-related int setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_RETRIES
* Set the number of retry attempts to decrypt a private key file. \n
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* - \ref XMPP_SETTING_COMPRESSION_LEVEL
* Set the compression level. \n
* For zlib the valid range is `-1` to `9`.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_int(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
int value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_RETRIES:
if (value <= 0)
conn->password_retries = 1;
else
conn->password_retries = value;
break;
case XMPP_SETTING_COMPRESSION_LEVEL:
conn->compression.level = value;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Int setting %d", setting);
return;
}
}
/** Configure a connection-related string setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_JID
* Set the JID of the user that will be bound to the connection. \n
* This should not be used after a connection is created. \n
* If the supplied JID is missing the node, SASL ANONYMOUS authentication
* will be used.
*
* - \ref XMPP_SETTING_PASS
* Set the password used to authenticate the connection.
*
* - \ref XMPP_SETTING_CAFILE
* Set CAfile.
*
* - \ref XMPP_SETTING_CAPATH
* Set CApath.
*
* - \ref XMPP_SETTING_CLIENT_CERT
* Set the Client Certificate or PKCS#12 encoded file that will be bound to
* the connection. \n
* This should not be used after a connection is created. \n
* In case the PKCS#12 file is encrypted, a callback must be set via
* \ref xmpp_conn_set_functionpointer with the option
* \ref XMPP_SETTING_PASSWORD_CALLBACK so the TLS stack can retrieve the
* password.
*
* - \ref XMPP_SETTING_CLIENT_KEY
* Set the Private Key that will be bound to the connection. \n
* This should not be used after a connection is created. \n
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_functionpointer with the option
* \ref XMPP_SETTING_PASSWORD_CALLBACK so the TLS stack can retrieve the
* password.
*
*
*
* If any value was previously set, it will be discarded.
* The function will make a copy of the value given.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_string(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
const char *value)
{
char **target;
switch (setting) {
case XMPP_SETTING_JID:
target = &conn->jid;
break;
case XMPP_SETTING_PASS:
target = &conn->pass;
break;
case XMPP_SETTING_CAFILE:
target = &conn->tls_cafile;
break;
case XMPP_SETTING_CAPATH:
target = &conn->tls_capath;
break;
case XMPP_SETTING_CLIENT_CERT:
target = &conn->tls_client_cert;
break;
case XMPP_SETTING_CLIENT_KEY:
target = &conn->tls_client_key;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid String setting %d", setting);
return;
}
if (*target)
strophe_free(conn->ctx, *target);
*target = NULL;
if (value) {
*target = strophe_strdup(conn->ctx, value);
}
}
/** Configure a connection-related pointer setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_CALLBACK_USERDATA
* Set the userdata pointer that is pass when the password-retrieval
* callback is called. \ref XMPP_SETTING_PASSWORD_CALLBACK
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_pointer(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
void *value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_CALLBACK_USERDATA:
conn->password_callback_userdata = value;
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Pointer setting %d", setting);
return;
}
}
/** Configure a connection-related functionpointer setting
*
* `setting` can be one of:
* - \ref XMPP_SETTING_PASSWORD_CALLBACK
* Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file. The userdata pointer can be set
* via \ref xmpp_conn_set_pointer with the setting
* \ref XMPP_SETTING_PASSWORD_CALLBACK_USERDATA
*
* - \ref XMPP_SETTING_CERTFAIL_HANDLER
* Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* - \ref XMPP_SETTING_SOCKOPT_CALLBACK
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started. \n
* If the connection is already connected, this callback will be called
* immediately. \n
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn a Strophe connection object
* @param setting The setting that shall be configured
* @param value The value, the settings should get
*
* @ingroup Connections
*/
void xmpp_conn_set_functionpointer_impl(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
xmpp_conn_pfn_t value)
{
switch (setting) {
case XMPP_SETTING_PASSWORD_CALLBACK:
conn->password_callback = (xmpp_password_callback)value;
break;
case XMPP_SETTING_CERTFAIL_HANDLER:
conn->certfail_handler = (xmpp_certfail_handler)value;
break;
case XMPP_SETTING_SOCKOPT_CALLBACK:
conn->sockopt_cb = (xmpp_sockopt_callback)value;
if (conn->state != XMPP_STATE_DISCONNECTED)
conn->sockopt_cb(conn, &conn->sock);
break;
default:
strophe_warn(conn->ctx, "xmpp", "Invalid Functionpointer setting %d",
setting);
return;
}
}
/** Get the JID which is or will be bound to the connection. /** Get the JID which is or will be bound to the connection.
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
@@ -397,66 +571,6 @@ const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
return conn->bound_jid; return conn->bound_jid;
} }
/** Set the JID of the user that will be bound to the connection.
* If any JID was previously set, it will be discarded. This should not be
* be used after a connection is created. The function will make a copy of
* the JID string. If the supplied JID is missing the node, SASL
* ANONYMOUS authentication will be used.
*
* @param conn a Strophe connection object
* @param jid a full or bare JID
*
* @ingroup Connections
*/
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
{
if (conn->jid)
strophe_free(conn->ctx, conn->jid);
conn->jid = strophe_strdup(conn->ctx, jid);
}
/** Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* @param conn a Strophe connection object
* @param hndl certfail Handler function
*
* @ingroup TLS
*/
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl)
{
conn->certfail_handler = hndl;
}
/** Set the CAfile
*
* @param conn a Strophe connection object
* @param path path to a certificate file
*
* @ingroup TLS
*/
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);
}
/** Set the CApath
*
* @param conn a Strophe connection object
* @param path path to a folder containing certificates
*
* @ingroup TLS
*/
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);
}
/** Retrieve the peer certificate /** Retrieve the peer certificate
* *
* The returned Certificate object must be free'd by calling * The returned Certificate object must be free'd by calling
@@ -473,41 +587,6 @@ xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
return tls_peer_cert(conn); return tls_peer_cert(conn);
} }
/** Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file.
*
* @param conn a Strophe connection object
* @param cb The callback function that shall be called
* @param userdata An opaque data pointer that will be passed to the callback
*
* @ingroup TLS
*/
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata)
{
conn->password_callback = cb;
conn->password_callback_userdata = userdata;
}
/** Set the number of retry attempts to decrypt a private key file.
*
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* @param conn a Strophe connection object
* @param retries The number of retries that should be tried
*
* @ingroup TLS
*/
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
{
if (retries == 0)
conn->password_retries = 1;
else
conn->password_retries = retries;
}
/** Retrieve the path of the key file that shall be unlocked. /** Retrieve the path of the key file that shall be unlocked.
* *
* This makes usually sense to be called from the * This makes usually sense to be called from the
@@ -524,49 +603,6 @@ const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
return conn->tls_client_key; return conn->tls_client_key;
} }
/** Set the Client Certificate and Private Key or PKCS#12 encoded file that
* will be bound to the connection. If any of them was previously set, it
* will be discarded. This should not be used after a connection is created.
* The function will make a copy of the strings passed in.
*
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
* password.
*
* In case one wants to use a PKCS#12 encoded file, it should be passed via
* the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
* `key` is deprecated.
*
* @param conn a Strophe connection object
* @param cert path to a certificate file or a P12 file
* @param key path to a private key file or a P12 file
*
* @ingroup TLS
*/
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
const char *const cert,
const char *const key)
{
strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
if (conn->tls_client_cert)
strophe_free(conn->ctx, conn->tls_client_cert);
conn->tls_client_cert = NULL;
if (conn->tls_client_key)
strophe_free(conn->ctx, conn->tls_client_key);
conn->tls_client_key = NULL;
if (cert && key) {
conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
conn->tls_client_key = strophe_strdup(conn->ctx, key);
} else if (cert && !key) {
conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
} else if (!cert && key) {
strophe_warn(conn->ctx, "xmpp",
"xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
"parameter is deprecated. Use 'cert' instead");
conn->tls_client_cert = strophe_strdup(conn->ctx, key);
}
}
/** Get the number of xmppAddr entries in the client certificate. /** Get the number of xmppAddr entries in the client certificate.
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
@@ -607,22 +643,6 @@ const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
return conn->pass; return conn->pass;
} }
/** Set the password used to authenticate the connection.
* If any password was previously set, it will be discarded. The function
* will make a copy of the password string.
*
* @param conn a Strophe connection object
* @param pass the password
*
* @ingroup Connections
*/
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
{
if (conn->pass)
strophe_free(conn->ctx, conn->pass);
conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
}
/** Get the strophe context that the connection is associated with. /** Get the strophe context that the connection is associated with.
* @param conn a Strophe connection object * @param conn a Strophe connection object
* *
@@ -1092,6 +1112,7 @@ int conn_tls_start(xmpp_conn_t *conn)
} }
if (conn->tls != NULL) { if (conn->tls != NULL) {
struct conn_interface old_intf = conn->intf;
conn->intf = tls_intf; conn->intf = tls_intf;
conn->intf.conn = conn; conn->intf.conn = conn;
if (tls_start(conn->tls)) { if (tls_start(conn->tls)) {
@@ -1102,6 +1123,7 @@ int conn_tls_start(xmpp_conn_t *conn)
tls_free(conn->tls); tls_free(conn->tls);
conn->tls = NULL; conn->tls = NULL;
conn->tls_failed = 1; conn->tls_failed = 1;
conn->intf = old_intf;
} }
} }
if (rc != 0) { if (rc != 0) {
@@ -1147,14 +1169,14 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
* *
* Supported flags are: * Supported flags are:
* *
* - XMPP_CONN_FLAG_DISABLE_TLS * - \ref XMPP_CONN_FLAG_DISABLE_TLS
* - XMPP_CONN_FLAG_MANDATORY_TLS * - \ref XMPP_CONN_FLAG_MANDATORY_TLS
* - XMPP_CONN_FLAG_LEGACY_SSL * - \ref XMPP_CONN_FLAG_LEGACY_SSL
* - XMPP_CONN_FLAG_TRUST_TLS * - \ref XMPP_CONN_FLAG_TRUST_TLS
* - XMPP_CONN_FLAG_LEGACY_AUTH * - \ref XMPP_CONN_FLAG_LEGACY_AUTH
* - XMPP_CONN_FLAG_DISABLE_SM * - \ref XMPP_CONN_FLAG_DISABLE_SM
* - XMPP_CONN_FLAG_ENABLE_COMPRESSION * - \ref XMPP_CONN_FLAG_ENABLE_COMPRESSION
* - XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * - \ref XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
* @param flags ORed connection flags * @param flags ORed connection flags
@@ -1435,6 +1457,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
xmpp_queue_element_t which) xmpp_queue_element_t which)
{ {
xmpp_send_queue_t *t; xmpp_send_queue_t *t;
int disconnected = conn->state == XMPP_STATE_DISCONNECTED;
/* Fast return paths */ /* Fast return paths */
/* empty queue */ /* empty queue */
@@ -1443,7 +1466,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
/* one element in queue */ /* one element in queue */
if (conn->send_queue_head == conn->send_queue_tail) { if (conn->send_queue_head == conn->send_queue_tail) {
/* head is already sent out partially */ /* head is already sent out partially */
if (conn->send_queue_head->wip) if (conn->send_queue_head->wip && !disconnected)
return NULL; return NULL;
/* the element is no USER element */ /* the element is no USER element */
if (conn->send_queue_head->owner != XMPP_QUEUE_USER) if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
@@ -1467,7 +1490,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
return NULL; return NULL;
/* head is already sent out partially */ /* head is already sent out partially */
if (t == conn->send_queue_head && t->wip) if (t == conn->send_queue_head && t->wip && !disconnected)
t = t->next; t = t->next;
/* search forward to find the first USER element */ /* search forward to find the first USER element */
@@ -1481,8 +1504,11 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
/* In case there exists a SM stanza that is linked to the /* In case there exists a SM stanza that is linked to the
* one we're currently dropping, also delete that one. * one we're currently dropping, also delete that one.
*/ */
if (t->next && t->next->userdata == t) if (t->next && t->next->userdata == t) {
strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next)); strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next));
/* reset the flag, so we restart to send `<r>` stanzas */
conn->sm_state->r_sent = 0;
}
/* Finally drop the element */ /* Finally drop the element */
return _drop_send_queue_element(conn, t); return _drop_send_queue_element(conn, t);
} }

View File

@@ -19,6 +19,7 @@
*/ */
#include "common.h" #include "common.h"
#include <limits.h>
/** Allocate memory in a Strophe context. /** Allocate memory in a Strophe context.
* All Strophe functions will use this to allocate memory. * All Strophe functions will use this to allocate memory.
@@ -86,6 +87,10 @@ char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
return strophe_strndup(ctx, s, len); return strophe_strndup(ctx, s, len);
} }
/** Write to the log.
*
* @ingroup Deprecated
*/
void xmpp_log(const xmpp_ctx_t *ctx, void xmpp_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level, xmpp_log_level_t level,
const char *area, const char *area,
@@ -217,6 +222,10 @@ char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
return strophe_strtok_r(s, delim, saveptr); return strophe_strtok_r(s, delim, saveptr);
} }
/** snprintf(3) implementation.
*
* @ingroup Deprecated
*/
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...) int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
{ {
va_list ap; va_list ap;
@@ -228,11 +237,184 @@ int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
return ret; return ret;
} }
/** vsnprintf(3) implementation.
*
* @ingroup Deprecated
*/
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg) int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg)
{ {
return strophe_vsnprintf(str, count, fmt, arg); return strophe_vsnprintf(str, count, fmt, arg);
} }
/** Set the JID of the user that will be bound to the connection.
* If any JID was previously set, it will be discarded. This should not be
* be used after a connection is created. The function will make a copy of
* the JID string. If the supplied JID is missing the node, SASL
* ANONYMOUS authentication will be used.
*
* @param conn a Strophe connection object
* @param jid a full or bare JID
*
* @ingroup Deprecated
*/
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
{
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
}
/** Set the password used to authenticate the connection.
* If any password was previously set, it will be discarded. The function
* will make a copy of the password string.
*
* @param conn a Strophe connection object
* @param pass the password
*
* @ingroup Deprecated
*/
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
{
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
}
/** Set the CAfile
*
* @param conn a Strophe connection object
* @param path path to a certificate file
*
* @ingroup Deprecated
*/
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
{
xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, path);
}
/** Set the CApath
*
* @param conn a Strophe connection object
* @param path path to a folder containing certificates
*
* @ingroup Deprecated
*/
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
{
xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, path);
}
/** Set the Client Certificate and Private Key or PKCS#12 encoded file that
* will be bound to the connection. If any of them was previously set, it
* will be discarded. This should not be used after a connection is created.
* The function will make a copy of the strings passed in.
*
* In case the Private Key is encrypted, a callback must be set via
* \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
* password.
*
* In case one wants to use a PKCS#12 encoded file, it should be passed via
* the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
* `key` is deprecated.
*
* @param conn a Strophe connection object
* @param cert path to a certificate file or a P12 file
* @param key path to a private key file or a P12 file
*
* @ingroup Deprecated
*/
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
const char *const cert,
const char *const key)
{
strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
if (!cert && key) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, key);
strophe_warn(conn->ctx, "xmpp",
"xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
"parameter is deprecated. Use 'cert' instead");
} else {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
}
}
/** Set the number of retry attempts to decrypt a private key file.
*
* In case the user enters the password manually it can be useful to
* directly retry if the decryption of the key file failed.
*
* @param conn a Strophe connection object
* @param retries The number of retries that should be tried
*
* @ingroup Deprecated
*/
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
{
int val;
if (retries > INT_MAX) {
val = INT_MAX;
strophe_warn(conn->ctx, "xmpp", "retries capped from %u to %d", retries,
val);
} else {
val = (int)retries;
}
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, val);
}
/** Set the Callback function which will be called when the TLS stack can't
* decrypt a password protected key file.
*
* @param conn a Strophe connection object
* @param cb The callback function that shall be called
* @param userdata An opaque data pointer that will be passed to the callback
*
* @ingroup Deprecated
*/
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, cb);
xmpp_conn_set_pointer(conn, XMPP_SETTING_PASSWORD_CALLBACK_USERDATA,
userdata);
}
/** Set the Handler function which will be called when the TLS stack can't
* verify the CA of the server we're trying to connect to.
*
* @param conn a Strophe connection object
* @param hndl certfail Handler function
*
* @ingroup Deprecated
*/
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER, hndl);
}
/** Register sockopt callback
* Set function to be called when a new socket is created to allow setting
* socket options before connection is started.
*
* If the connection is already connected, this callback will be called
* immediately.
*
* To set options that can only be applied to disconnected sockets, the
* callback must be registered before connecting.
*
* @param conn The Strophe connection object this callback is being registered
* for
* @param callback a xmpp_sockopt_callback callback function that will receive
* notifications of connection status
*
* @ingroup Deprecated
*/
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback)
{
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
callback);
}
/** Set TCP keepalive parameters /** Set TCP keepalive parameters
* Turn on TCP keepalive and set timeout and interval. Zero timeout * Turn on TCP keepalive and set timeout and interval. Zero timeout
* disables TCP keepalives. The parameters are applied immediately for * disables TCP keepalives. The parameters are applied immediately for
@@ -253,7 +435,8 @@ void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
conn->ka_timeout = timeout; conn->ka_timeout = timeout;
conn->ka_interval = interval; conn->ka_interval = interval;
conn->ka_count = 0; conn->ka_count = 0;
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
xmpp_sockopt_cb_keepalive);
} }
/** Disable TLS for this connection, called by users of the library. /** Disable TLS for this connection, called by users of the library.

View File

@@ -78,12 +78,23 @@ const struct hash_alg scram_sha512 = {
(void (*)(void *, const uint8_t *, size_t))sha512_process, (void (*)(void *, const uint8_t *, size_t))sha512_process,
(void (*)(void *, uint8_t *))sha512_done}; (void (*)(void *, uint8_t *))sha512_done};
const struct hash_alg scram_sha512_plus = {
"SCRAM-SHA-512-PLUS",
SASL_MASK_SCRAMSHA512_PLUS,
SHA512_DIGEST_SIZE,
(void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
(void (*)(void *))sha512_init,
(void (*)(void *, const uint8_t *, size_t))sha512_process,
(void (*)(void *, uint8_t *))sha512_done};
/* The order of this list defines the order in which the SCRAM algorithms are /* The order of this list defines the order in which the SCRAM algorithms are
* tried if the server supports them. * tried if the server supports them.
* Their order is derived from * Their order is derived from
* https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage * https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
*/ */
const struct hash_alg *scram_algs[] = { const struct hash_alg *scram_algs[] = {
/* *1 */
&scram_sha512_plus,
/* *1 */ /* *1 */
&scram_sha256_plus, &scram_sha256_plus,
/* *1 */ /* *1 */

View File

@@ -123,7 +123,8 @@ static void sock_getaddrinfo(xmpp_sock_t *xsock)
rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints, rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
&xsock->ainfo_list); &xsock->ainfo_list);
if (rc != 0) { if (rc != 0) {
strophe_debug(xsock->ctx, "sock", "getaddrinfo() failed with %d", strophe_debug(xsock->ctx, "sock",
"getaddrinfo() failed with %s (%d)", gai_strerror(rc),
rc); rc);
xsock->ainfo_list = NULL; xsock->ainfo_list = NULL;
} }
@@ -207,7 +208,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
{ {
struct addrinfo *ainfo; struct addrinfo *ainfo;
sock_t sock; sock_t sock;
int rc = 0; int rc;
char buf[64]; char buf[64];
do { do {
@@ -228,6 +229,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock != INVALID_SOCKET) { if (sock != INVALID_SOCKET) {
rc = 0;
if (xsock->conn->sockopt_cb) { if (xsock->conn->sockopt_cb) {
/* Don't allow user to overwrite sockfd value. */ /* Don't allow user to overwrite sockfd value. */
sock_t sock_copy = sock; sock_t sock_copy = sock;
@@ -235,7 +237,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
if (rc != 0) { if (rc != 0) {
strophe_debug(xsock->ctx, "sock", strophe_debug(xsock->ctx, "sock",
"User's setsockopt callback" "User's setsockopt callback"
"failed with %d (errno=%d)", " failed with %d (errno=%d)",
rc, errno); rc, errno);
} }
} }

View File

@@ -51,8 +51,10 @@
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if OPENSSL_VERSION_NUMBER < 0x30000000L
#define STROPHE_ERR_func_error_string(e) ERR_func_error_string(e) #define STROPHE_ERR_func_error_string(e) ERR_func_error_string(e)
#define STROPHE_SSL_get1_peer_certificate(s) SSL_get_peer_certificate(s)
#else #else
#define STROPHE_ERR_func_error_string(e) "" #define STROPHE_ERR_func_error_string(e) ""
#define STROPHE_SSL_get1_peer_certificate(s) SSL_get1_peer_certificate(s)
#endif #endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L #if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -109,6 +111,7 @@ struct _tls {
X509 *client_cert; X509 *client_cert;
void *channel_binding_data; void *channel_binding_data;
size_t channel_binding_size; size_t channel_binding_size;
FILE *keylogfile;
int lasterror; int lasterror;
}; };
@@ -556,6 +559,36 @@ static int _tls_password_callback(char *buf, int size, int rwflag, void *u)
return tls_caching_password_callback(buf, size, u); return tls_caching_password_callback(buf, size, u);
} }
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
static void _keylog_cb(const SSL *ssl, const char *line)
{
xmpp_conn_t *conn = SSL_get_app_data(ssl);
fwrite(line, strlen(line), 1, conn->tls->keylogfile);
fputc('\n', conn->tls->keylogfile);
fflush(conn->tls->keylogfile);
}
#endif
static void _try_open_keylogfile(tls_t *tls)
{
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
const char *first_line = "# SSL Key logfile generated by libstrophe\n";
const char *SSLKEYLOGFILE = getenv("SSLKEYLOGFILE");
if (!SSLKEYLOGFILE || *SSLKEYLOGFILE == '\0')
return;
tls->keylogfile = fopen(SSLKEYLOGFILE, "abe");
if (!tls->keylogfile) {
strophe_warn(tls->ctx, "tls", "Could not open SSL keylog file %s",
SSLKEYLOGFILE);
return;
}
fwrite(first_line, strlen(first_line), 1, tls->keylogfile);
SSL_CTX_set_keylog_callback(tls->ssl_ctx, _keylog_cb);
#else
UNUSED(tls);
#endif
}
tls_t *tls_new(xmpp_conn_t *conn) tls_t *tls_new(xmpp_conn_t *conn)
{ {
tls_t *tls = strophe_alloc(conn->ctx, sizeof(*tls)); tls_t *tls = strophe_alloc(conn->ctx, sizeof(*tls));
@@ -699,6 +732,8 @@ tls_t *tls_new(xmpp_conn_t *conn)
ret = SSL_set_fd(tls->ssl, conn->sock); ret = SSL_set_fd(tls->ssl, conn->sock);
if (ret <= 0) if (ret <= 0)
goto err_free_ssl; goto err_free_ssl;
_try_open_keylogfile(tls);
} }
return tls; return tls;
@@ -717,6 +752,8 @@ err:
void tls_free(tls_t *tls) void tls_free(tls_t *tls)
{ {
if (tls->keylogfile)
fclose(tls->keylogfile);
strophe_free(tls->ctx, tls->channel_binding_data); strophe_free(tls->ctx, tls->channel_binding_data);
SSL_free(tls->ssl); SSL_free(tls->ssl);
X509_free(tls->client_cert); X509_free(tls->client_cert);
@@ -727,7 +764,7 @@ void tls_free(tls_t *tls)
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn) xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
{ {
if (conn && conn->tls && conn->tls->ssl) { if (conn && conn->tls && conn->tls->ssl) {
X509 *cert = SSL_get_peer_certificate(conn->tls->ssl); X509 *cert = STROPHE_SSL_get1_peer_certificate(conn->tls->ssl);
if (cert) { if (cert) {
xmpp_tlscert_t *tlscert = _x509_to_tlscert(conn->ctx, cert); xmpp_tlscert_t *tlscert = _x509_to_tlscert(conn->ctx, cert);
X509_free(cert); X509_free(cert);
@@ -750,8 +787,9 @@ int tls_init_channel_binding(tls_t *tls,
{ {
const char *label = NULL; const char *label = NULL;
size_t labellen = 0; size_t labellen = 0;
int ssl_version = SSL_version(tls->ssl);
switch (SSL_version(tls->ssl)) { switch (ssl_version) {
case SSL3_VERSION: case SSL3_VERSION:
*binding_prefix = "tls-unique"; *binding_prefix = "tls-unique";
*binding_prefix_len = strlen("tls-unique"); *binding_prefix_len = strlen("tls-unique");
@@ -774,7 +812,7 @@ int tls_init_channel_binding(tls_t *tls,
break; break;
#endif #endif
default: default:
strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s", strophe_error(tls->ctx, "tls", "Unsupported TLS/SSL Version: %s",
SSL_get_version(tls->ssl)); SSL_get_version(tls->ssl));
return -1; return -1;
} }
@@ -785,11 +823,28 @@ int tls_init_channel_binding(tls_t *tls,
if (!tls->channel_binding_data) if (!tls->channel_binding_data)
return -1; return -1;
if (SSL_export_keying_material(tls->ssl, tls->channel_binding_data, if (ssl_version <= TLS1_2_VERSION) {
tls->channel_binding_size, label, labellen, size_t len;
NULL, 0, 0) != 1) { if (SSL_session_reused(tls->ssl)) {
strophe_error(tls->ctx, "tls", "Could not get channel binding data"); len = SSL_get_peer_finished(tls->ssl, tls->channel_binding_data,
return -1; tls->channel_binding_size);
} else {
len = SSL_get_finished(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size);
}
if (len != tls->channel_binding_size) {
strophe_error(tls->ctx, "tls",
"Got channel binding data of wrong size %zu", len);
return -1;
}
} else {
if (SSL_export_keying_material(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size, label,
labellen, NULL, 0, 0) != 1) {
strophe_error(tls->ctx, "tls",
"Could not get channel binding data");
return -1;
}
} }
return 0; return 0;
} }
@@ -981,7 +1036,7 @@ static void _tls_dump_cert_info(tls_t *tls)
X509 *cert; X509 *cert;
char *name; char *name;
cert = SSL_get_peer_certificate(tls->ssl); cert = STROPHE_SSL_get1_peer_certificate(tls->ssl);
if (cert == NULL) if (cert == NULL)
strophe_debug(tls->ctx, "tls", "Certificate was not presented by peer"); strophe_debug(tls->ctx, "tls", "Certificate was not presented by peer");
else { else {

102
strophe.h
View File

@@ -371,37 +371,70 @@ typedef int (*xmpp_sockopt_callback)(xmpp_conn_t *conn, void *sock);
/* an example callback that sets basic keepalive parameters */ /* an example callback that sets basic keepalive parameters */
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock); int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx); xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn); xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn);
int xmpp_conn_release(xmpp_conn_t *conn); int xmpp_conn_release(xmpp_conn_t *conn);
/** Connection settings
*/
typedef enum xmpp_conn_setting_t {
/* String values */
XMPP_SETTING_JID = 0x0, /**< JID */
XMPP_SETTING_PASS, /**< Password */
XMPP_SETTING_CAFILE, /**< CAfile */
XMPP_SETTING_CAPATH, /**< CApath */
XMPP_SETTING_CLIENT_CERT, /**< Client Certificate */
XMPP_SETTING_CLIENT_KEY, /**< Key of Client Certificate */
/* Int values */
XMPP_SETTING_PASSWORD_RETRIES = 0x40, /**< Number of retry attempts to
decrypt a private key file. */
XMPP_SETTING_COMPRESSION_LEVEL, /**< Compression level. */
/* Pointer values */
XMPP_SETTING_PASSWORD_CALLBACK_USERDATA = 0x80, /**< Userdata for password
callback function */
/* Functionpointer values */
XMPP_SETTING_PASSWORD_CALLBACK = 0xC0, /**< Callback function to retrieve
password for key file. */
XMPP_SETTING_CERTFAIL_HANDLER, /**< Handler function when certificate chain
can't be verified */
XMPP_SETTING_SOCKOPT_CALLBACK, /**< Callback function when a new socket is
created. */
} xmpp_conn_setting_t;
void xmpp_conn_set_int(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
int value);
void xmpp_conn_set_string(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
const char *value);
void xmpp_conn_set_pointer(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
void *value);
/* Provide a wrapper macro, so users don't have to cast their function pointer
* type manually. */
typedef int (*xmpp_conn_pfn_t)();
#define xmpp_conn_set_functionpointer(c, s, p) \
do { \
xmpp_conn_set_functionpointer_impl(c, s, (xmpp_conn_pfn_t)p); \
} while (0)
void xmpp_conn_set_functionpointer_impl(xmpp_conn_t *conn,
xmpp_conn_setting_t setting,
xmpp_conn_pfn_t pfn);
long xmpp_conn_get_flags(const xmpp_conn_t *conn); long xmpp_conn_get_flags(const xmpp_conn_t *conn);
int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags); int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags);
const char *xmpp_conn_get_jid(const xmpp_conn_t *conn); const char *xmpp_conn_get_jid(const xmpp_conn_t *conn);
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn); const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn);
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
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_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl);
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn); xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn);
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata);
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn); const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn);
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
const char *cert,
const char *key);
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn); unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n); char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
const char *xmpp_conn_get_pass(const xmpp_conn_t *conn); const char *xmpp_conn_get_pass(const xmpp_conn_t *conn);
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn); xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn);
int xmpp_conn_is_secured(xmpp_conn_t *conn); int xmpp_conn_is_secured(xmpp_conn_t *conn);
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback);
int xmpp_conn_is_connecting(xmpp_conn_t *conn); int xmpp_conn_is_connecting(xmpp_conn_t *conn);
int xmpp_conn_is_connected(xmpp_conn_t *conn); int xmpp_conn_is_connected(xmpp_conn_t *conn);
int xmpp_conn_is_disconnected(xmpp_conn_t *conn); int xmpp_conn_is_disconnected(xmpp_conn_t *conn);
@@ -445,6 +478,7 @@ int xmpp_conn_tls_start(xmpp_conn_t *conn);
void xmpp_disconnect(xmpp_conn_t *conn); void xmpp_disconnect(xmpp_conn_t *conn);
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza); void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...); void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len); void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len);
@@ -707,11 +741,19 @@ void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
*/ */
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len); void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
/** /*
* Formerly "private but exported" functions made public for now to announce * Formerly "private but exported" functions made public for now to announce
* deprecation */ * deprecation */
#include <stdarg.h> #include <stdarg.h>
#if !defined(XMPP_DEPRECATED)
/**
* XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API
* functions
*
* @param x The function that can be used as a replacement or 'internal' if
* there is no replacement
*/
#if defined(__GNUC__) #if defined(__GNUC__)
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 405) #if (__GNUC__ * 100 + __GNUC_MINOR__ >= 405)
#define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x))) #define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x)))
@@ -723,6 +765,7 @@ void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
#else #else
#define XMPP_DEPRECATED(x) #define XMPP_DEPRECATED(x)
#endif #endif
#endif
XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size); XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size);
XMPP_DEPRECATED(internal) XMPP_DEPRECATED(internal)
@@ -762,6 +805,31 @@ void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval);
XMPP_DEPRECATED(xmpp_conn_set_flags) XMPP_DEPRECATED(xmpp_conn_set_flags)
void xmpp_conn_disable_tls(xmpp_conn_t *conn); void xmpp_conn_disable_tls(xmpp_conn_t *conn);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
XMPP_DEPRECATED(xmpp_conn_set_string)
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
const char *cert,
const char *key);
XMPP_DEPRECATED(xmpp_conn_set_int)
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl);
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -7,6 +7,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <limits.h>
#undef HAVE_VSNPRINTF #undef HAVE_VSNPRINTF
#undef HAVE_SNPRINTF #undef HAVE_SNPRINTF
@@ -28,7 +29,14 @@ int main(void)
char *int_fmt[] = {"%-1.5d", "%1.5d", "%123.9d", "%5.5d", char *int_fmt[] = {"%-1.5d", "%1.5d", "%123.9d", "%5.5d",
"%10.5d", "% 10.5d", "%+22.33d", "%01.3d", "%10.5d", "% 10.5d", "%+22.33d", "%01.3d",
"%4d", "0x%x", "0x%04x", NULL}; "%4d", "0x%x", "0x%04x", NULL};
long int_nums[] = {-1, 134, 91340, 341, 0203, 0x76543210, 0}; int int_nums[] = {-1, 134, 91340, 341, 0203,
0x76543210, INT_MIN, INT_MAX, 0};
char *long_fmt[] = {"%-1.5ld", "%1.5ld", "%123.9ld", "%5.5ld",
"%10.5ld", "% 10.5ld", "%+22.33ld", "%01.3ld",
"%4ld", "0x%lx", "0x%04lx", NULL};
long long_nums[] = {-1L, 134L, 91340L,
341L, 0203L, 0xFEDCBA9876543210L,
LONG_MIN, LONG_MAX, 0L};
int x, y; int x, y;
int fail = 0; int fail = 0;
int num = 0; int num = 0;
@@ -60,6 +68,19 @@ int main(void)
} }
num++; num++;
} }
for (x = 0; long_fmt[x] != NULL; x++)
for (y = 0; long_nums[y] != 0; y++) {
strophe_snprintf(buf1, sizeof(buf1), long_fmt[x], long_nums[y]);
sprintf(buf2, long_fmt[x], long_nums[y]);
if (strcmp(buf1, buf2)) {
printf("xmpp_snprintf doesn't match Format: "
"%s\n\txmpp_snprintf = %s\n\tsprintf = %s\n",
long_fmt[x], buf1, buf2);
fail++;
}
num++;
}
printf("%d tests failed out of %d.\n", fail, num); printf("%d tests failed out of %d.\n", fail, num);
return fail != 0 ? 1 : 0; return fail != 0 ? 1 : 0;
} }

View File

@@ -175,7 +175,7 @@ static void test_stanza_error(xmpp_ctx_t *ctx)
xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL); xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL);
assert(error != NULL); assert(error != NULL);
mood = xmpp_stanza_new_from_string(ctx, str_mood); mood = xmpp_stanza_new_from_string(ctx, str_mood);
assert(stanza != NULL); assert(mood != NULL);
assert(xmpp_stanza_get_to(error) != NULL); assert(xmpp_stanza_get_to(error) != NULL);
COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error)); COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error));

View File

@@ -16,6 +16,7 @@
#include <string.h> #include <string.h>
#include <sys/param.h> #include <sys/param.h>
#define XMPP_DEPRECATED(x)
#include "strophe.h" #include "strophe.h"
#include "test.h" #include "test.h"
@@ -41,12 +42,14 @@ int main()
} client_cert[] = { } client_cert[] = {
{0, "tests/cert.pem", "tests/key.pem"}, {0, "tests/cert.pem", "tests/key.pem"},
{1, "tests/cert.pem", "tests/key_encrypted.pem"}, {1, "tests/cert.pem", "tests/key_encrypted.pem"},
{0, NULL, "tests/cert.emptypass.pfx"},
{0, NULL, "tests/cert.nopass.pfx"},
{1, NULL, "tests/cert.pfx"},
{0, "tests/cert.emptypass.pfx", NULL}, {0, "tests/cert.emptypass.pfx", NULL},
{0, "tests/cert.nopass.pfx", NULL}, {0, "tests/cert.nopass.pfx", NULL},
{1, "tests/cert.pfx", NULL}, {1, "tests/cert.pfx", NULL},
/* Backward compatibility checks for change introduced in #208
* To be removed, once xmpp_conn_set_client_cert() is gone */
{0, NULL, "tests/cert.emptypass.pfx"},
{0, NULL, "tests/cert.nopass.pfx"},
{1, NULL, "tests/cert.pfx"},
}; };
const char *srcdir; const char *srcdir;
@@ -78,9 +81,15 @@ int main()
keyfile = NULL; keyfile = NULL;
if (client_cert[m].needs_callback) if (client_cert[m].needs_callback)
xmpp_conn_set_password_callback(conn, password_callback, NULL); xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
password_callback);
xmpp_conn_set_client_cert(conn, certfile, keyfile); if (certfile) {
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, certfile);
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, keyfile);
} else {
xmpp_conn_set_client_cert(conn, certfile, keyfile);
}
xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn); xmppaddr_num[0] = '0' + xmpp_conn_cert_xmppaddr_num(conn);