46 Commits

Author SHA1 Message Date
Steffen Jaeckel
dfb3e86824 Use void* to store function pointer when compiled as C23.
[0] decided that `rettype (*foo)();` must now be interpreted as
`rettype (*foo)(void);`. Luckily it also allows now to store function
pointers in a `void*` (c.f. Ch. J.5.7).

[0]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 17:24:12 +01:00
Steffen Jaeckel
a81f0a8a5f Auto-format according to new clang-format.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 15:18:52 +01:00
Steffen Jaeckel
5509ec3b35 Update CI
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-26 14:17:54 +01:00
Steffen Jaeckel
67b1a2f3f4 Add support for DragonFlyBSD in configure.ac
Hinted to by gnemmi in profanity@rooms.dismail.de

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-03-13 19:49:14 +01:00
Steffen Jaeckel
7c2e78937d Release libstrophe-0.14.0
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2025-03-13 14:44:37 +01:00
Steffen Jaeckel
b7bae1502f Update docs of SM-serialization.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2025-03-13 14:44:08 +01:00
Steffen Jaeckel
e3d734c5ff Fix SM-serialization testcase
`xmpp_run_once()` uses `FD_SET()` to determine which sockets to `select()`
on. The socket gets initialized to `INVALID_SOCKET = -1` inside
`xmpp_conn_new()` which leads to a buffer overflow once `FD_SET()` is
called.

This is only exposed when enabling a higher optimization level, which
was only done in the `release-test` CI job.

* Fix this testcase by initializing the socket to a possible value.
* Build the Valgrind CI jobs with `-O2`.
* Make the output of the `release-test` more verbose.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2024-11-19 16:26:50 +01:00
Steffen Jaeckel
e7b08faaa7 Clean up SM state serialization
* Refactor `xmpp_sm_state_restore()`

Add new internal functions to load u32 and string values. This also brings
length checks in order to verify we don't read more sm_state than there
potentially is and type checks to ensure we have the correct CBOR types.

Each element is added to the queue right after creation, so it won't leak
in case creating its content fails.

* Refactor `sm_state_serialize()`

* Store & restore ints as/from big endian, CBOR requires that

* Bring API names in line with the usual naming

* Auto-format sources

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2024-11-19 16:26:50 +01:00
Stephen Paul Weber
f3fa9f557b Allow serializing SM state
It is useful in some applications to get a snapshot of the stream
management state for storage outside the process, in order to recover
from crashes and other things.  The get_sm_state already present is not
suitable for this because it returns a live object tied in to the
current context and such, and containing much unneeded internal-api
data.

Introduce xmpp_sm_state_set_callback which is called every time the
state changes with a serialized state, and a dual xmpp_sm_state_restore
which takes in this serialized state and sets up a new sm_state based on
that.

The serialization is considered opaque from the PoV of the API, but is
based on CBOR to facilitate easy debugging.
2024-11-19 16:26:49 +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
Steffen Jaeckel
3f03de031c Re-factor into separate compression module
Introduce a `conn_interface` to simplify the decision logic which API
we must call.

This also fixes some bugs of the previous commit.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-01-31 20:37:30 +01:00
Steffen Jaeckel
c7d410f38b First version of XEP-0138 support
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-01-30 14:23:26 +01:00
Steffen Jaeckel
fa960429b8 Improve Stream Management
* Delay the notification of the library user that the connection was
  successful, until SM is reported by the server as enabled.
* Clear the SM queue in case resumption failed.
* Improve some debug statements & comments.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-01-30 14:23:26 +01:00
Steffen Jaeckel
bfd0872aea Add support for SCRAM-*-PLUS SASL mechanisms
This fixes #133

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-27 23:50:16 +01:00
Steffen Jaeckel
c5b6026fc6 Use memset() in constructors
Instead of setting each element individually to 0 or NULL, use memset.

This also fixes the missing initialization of `conn->sm_disable`.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-08 15:23:49 +01:00
Steffen Jaeckel
7fbf507f58 Fix connected/connecting semantics
In case a connection is established, but the stream negotiation has not
completed yet, a user may think that they can start sending data,
but this is not the case.

All of the following quotes originate in RFC6120.

As of Ch. 8:

> After a client and a server (or two servers) have completed stream
> negotiation, either party can send XML stanzas.

As of Ch. 7.3.1:

> The parties to a stream MUST consider resource binding as
> mandatory-to-negotiate.

As of Ch. 6.3.1:

> The parties to a stream MUST consider SASL as mandatory-to-negotiate.

As of Ch. 4.3.5:

> The initiating entity MUST NOT attempt to send XML stanzas to entities
> other than itself (i.e., the client's connected resource or any other
> authenticated resource of the client's account) or the server to which
> it is connected until stream negotiation has been completed.
> Even if the initiating entity does attempt to do so, the receiving
> entity MUST NOT accept such stanzas and MUST close the stream with a
> <not-authorized/> stream error [...]

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-08 15:23:49 +01:00
Steffen Jaeckel
69f42360cf Officially deprecate xmpp_conn_disable_tls()
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-08 15:23:49 +01:00
Steffen Jaeckel
f40fcbe5d1 Introduce strophe_free_and_null()
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-01 01:40:45 +01:00
Steffen Jaeckel
13d0354176 Send less ACK requests
Only send an `r` stanza after we've received the reply to the previous one.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-01 01:40:45 +01:00
Steffen Jaeckel
53d8eb17b6 Improve macros used in tests
* Be more verbose in which argument is `should` and which is `is`.
* Fix the order in the `ENSURE_EQ()` macro.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-11-01 01:40:45 +01:00
Steffen Jaeckel
9735af515a Improve debug message
Before this change we couldn't differentiate between this instance
and the one further down in the code.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-10-26 22:23:28 +02:00
Steffen Jaeckel
c30bd0d9ab Fix typo
... also make it possible to pre-define `STROPHE_MESSAGE_BUFFER_SIZE` in
a custom build if someone doesn't want such a huge stack buffer.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-10-26 22:23:03 +02:00
Steffen Jaeckel
fdd717a246 Run CI against newer (libre|open)ssl
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-10-23 23:55:56 +02:00
Theo Buehler
7894d15929 Stop reaching into X509_ALGOR
While the struct is still public in all versions of OpenSSL and its
forks, this may well change in the future. Prepare for that case and
use the X509_ALGOR_get0() accessor which has been available with this
signature since OpenSSL 1.1.
2023-10-21 07:59:44 +02:00
Steffen Jaeckel
f49ccb3262 Fix wording of licensing terms
It has been pointed out that the wording of the license of this library is
not entirely clear.

The term "dual licensing" usually refers to a licence choice of two
licenses "LICENSE1 _or_ LICENSE2.
Instead the license of this library claimed "LICENSE1 _and_ LICENSE2".

After an internal discussion with @metajack and @pasis it was made clear
that the initial idea was to dual license the library in the usual way.

This was also made clear by jack on the ML in the past [0].
As of jack, these licensing terms originated from jquery, which also used
the 'and' version in the past and has since been corrected [1].

This patch changes the license terms to 'MIT or GPLv3' and also adds SPDX
headers [2].

[0] https://groups.google.com/g/libstrophe/c/JkFgr601JQc
[1] https://stackoverflow.com/q/2758409
[2] https://spdx.org

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-25 18:08:42 +02:00
Steffen Jaeckel
069787db51 Update certificate used in tests
Fedora patches OpenSSL and disables usage of the ECC curve we used
in the old certificate.
This patch changes the ECC curve to a supported one.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-09 15:51:50 +02:00
Steffen Jaeckel
9eba8e8738 Allow running tests when static library is disabled
We then only run a subset of the tests, but still better than failing to
build.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-09 13:34:18 +02:00
86 changed files with 2382 additions and 683 deletions

View File

@@ -10,12 +10,12 @@ on:
jobs:
linux-tests:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
strategy:
matrix:
valgrind:
- { configure: '' , make: 'check' }
- { configure: '--enable-valgrind' , make: 'check-valgrind' }
- { configure: '' , cflags: '', make: 'check' }
- { configure: '--enable-valgrind' , cflags: '-O2', make: 'check-valgrind' }
options:
- { configure: '' }
- { configure: '--without-libxml2' }
@@ -24,6 +24,7 @@ jobs:
- { configure: '--disable-tls' }
- { configure: '--enable-cares' }
- { configure: '--disable-getrandom' }
- { configure: '--disable-static' }
name: Regular Tests
steps:
- uses: actions/checkout@v2
@@ -34,7 +35,7 @@ jobs:
- name: Build the library
run: |
./bootstrap.sh
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3"
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3 ${{ matrix.valgrind.cflags }}"
make -j$(nproc)
- name: Run tests
run: |
@@ -45,26 +46,35 @@ jobs:
cat test-suite*.log || true
xssl-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
xssl_versions:
- { version: "master", continue: true, libressl: true }
- { version: "OPENBSD_7_1", continue: true, libressl: true }
- { version: "master", continue: true, libressl: true }
- { version: "OPENBSD_7_5", continue: true, libressl: true }
- { version: "v3.9.2", continue: true, libressl: true }
- { version: "OPENBSD_7_4", continue: true, libressl: true }
- { version: "v3.8.4", 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
# - { 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"
# - { version: "v3.4.3", continue: true, libressl: true }
- { version: "v3.4.2", continue: true, libressl: true }
- { version: "OPENBSD_6_9", continue: true, libressl: true }
- { version: "v3.1.5", continue: true, libressl: true }
- { version: "v2.1.10", continue: true, libressl: true }
- { version: "openssl-3.0", continue: true, libressl: false }
- { version: "openssl-3.0.4", continue: false, libressl: false }
valgrind:
- { configure: '' , make: 'check' }
- { configure: '--enable-valgrind' , make: 'check-valgrind' }
# - { version: "v3.4.3", continue: true, libressl: true }
- { version: "v3.4.2", continue: true, libressl: true }
- { version: "OPENBSD_6_9", continue: true, libressl: true }
- { version: "v3.1.5", continue: true, libressl: true }
- { version: "v2.1.10", continue: true, libressl: true }
- { version: "openssl-3.0", continue: true, libressl: false }
- { version: "openssl-3.0.13", continue: false, libressl: false }
- { version: "openssl-3.1", continue: true, libressl: false }
- { version: "openssl-3.1.5", continue: false, libressl: false }
- { version: "openssl-3.2", continue: true, libressl: false }
- { version: "openssl-3.2.1", continue: false, libressl: false }
- { version: "openssl-3.3", continue: true, libressl: false }
- { version: "openssl-3.3.0", continue: false, libressl: false }
name: xSSL tests
continue-on-error: ${{ matrix.xssl_versions.continue }}
steps:
@@ -82,11 +92,19 @@ jobs:
- name: Build the library
run: |
./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)
- name: Run tests
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
if: ${{ failure() }}
run: |
@@ -117,9 +135,14 @@ jobs:
if: ${{ !failure() }}
run: |
cat testbuild.log
- name: Error logs
if: ${{ failure() }}
run: |
cat testbuild.log || true
cat testerr.log || true
code-style:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
name: Check coding style
continue-on-error: true
steps:

2
.gitignore vendored
View File

@@ -46,6 +46,7 @@ examples/roster
examples/uuid
examples/vcard
testbuild*.log
testerr*.log
test-release/
test_stamp
test-suite*.log
@@ -65,6 +66,7 @@ tests/test_resolver
tests/test_sasl
tests/test_scram
tests/test_send_queue
tests/test_serialize_sm
tests/test_sha1
tests/test_sha256
tests/test_sha512

View File

@@ -1,3 +1,29 @@
0.14.0
- Add SM-state serialization mechanism (#239)
- Add support for `SCRAM-SHA-512-PLUS` (da122981)
- Add support for `SSLKEYLOGFILE` with OpenSSL (d82df127)
- Fix some issues related to Stream Management (03d43132, 4359536a)
- Fix a potential segfault (9fef4b7d)
- New API:
- xmpp_conn_set_sm_callback()
- xmpp_conn_restore_sm_state()
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
- Improve TCP-connection establishment (#221)
- Handle case where the server doesn't provide the `bind` feature (#224)
@@ -21,7 +47,7 @@
- Fix some build steps when builddir != srcdir (#208)
- Allow the user to disable build of examples (#209)
- 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`
parameter. Originally it was via `key`. Currently both styles are supported,
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
# control system is used.
PROJECT_NUMBER = 0.12
PROJECT_NUMBER = 0.14
# 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

View File

@@ -1,5 +1,5 @@
libstrophe XMPP client library
Copyright (C) 2005-2009 Collecta, Inc.
This program is dual licensed under the MIT and GPLv3 licenses.
This program is dual licensed under the MIT or GPLv3 licenses.
Please the files MIT-LICENSE.txt and GPL-LICENSE.txt for details.

View File

@@ -10,6 +10,9 @@ COVERAGE_POST=@COVERAGE_POST@
PARSER_CFLAGS=@PARSER_CFLAGS@
PARSER_LIBS=@PARSER_LIBS@
ZLIB_CFLAGS=@ZLIB_CFLAGS@
ZLIB_LIBS=@ZLIB_LIBS@
if TLS_WITH_GNUTLS
SSL_CFLAGS = @gnutls_CFLAGS@
SSL_LIBS = @gnutls_LIBS@
@@ -32,8 +35,8 @@ STROPHE_LIBS = $(COVERAGE_PRE) libstrophe.la $(COVERAGE_POST) $(COVERAGE_LDFLAGS
## Main build targets
lib_LTLIBRARIES = libstrophe.la
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS)
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) -no-undefined
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS) $(ZLIB_CFLAGS)
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) $(ZLIB_LIBS) -no-undefined
# Export only public API
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
@@ -82,6 +85,12 @@ if NEED_SNPRINTF
libstrophe_la_SOURCES += src/snprintf.c
endif
if DISABLE_COMPRESSION
libstrophe_la_SOURCES += src/compression_dummy.c
else
libstrophe_la_SOURCES += src/compression.c
endif
if DISABLE_TLS
libstrophe_la_SOURCES += src/tls_dummy.c
else
@@ -175,23 +184,30 @@ endif
## Tests
TESTS = \
STATIC_TESTS = \
tests/check_parser \
tests/test_hash \
tests/test_jid \
tests/test_ctx \
tests/test_send_queue \
tests/test_serialize_sm \
tests/test_string \
tests/test_resolver
TESTS = \
tests/test_sha1 \
tests/test_sha256 \
tests/test_sha512 \
tests/test_md5 \
tests/test_rand \
tests/test_scram \
tests/test_ctx \
tests/test_base64 \
tests/test_hash \
tests/test_jid \
tests/test_send_queue \
tests/test_snprintf \
tests/test_string \
tests/test_stanza \
tests/test_resolver
tests/test_stanza
if !DISABLE_STATIC
TESTS += $(STATIC_TESTS)
endif
if !DISABLE_TLS
TESTS += tests/test_xmppaddr
@@ -270,6 +286,11 @@ tests_test_send_queue_CFLAGS = -I$(top_srcdir)/src
tests_test_send_queue_LDADD = $(STROPHE_LIBS)
tests_test_send_queue_LDFLAGS = -static
tests_test_serialize_sm_SOURCES = tests/test_serialize_sm.c tests/test.c tests/test.h
tests_test_serialize_sm_CFLAGS = -I$(top_srcdir)/src
tests_test_serialize_sm_LDADD = $(STROPHE_LIBS)
tests_test_serialize_sm_LDFLAGS = -static
tests_test_snprintf_SOURCES = tests/test_snprintf.c
tests_test_snprintf_CFLAGS = -I$(top_srcdir)/src
@@ -312,6 +333,7 @@ dist-archives:
test-release: dist
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
@touch testerr-$(PACKAGE_VERSION).log && ln -sf testerr-$(PACKAGE_VERSION).log testerr.log
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
echo "Success" && popd

View File

@@ -5,7 +5,7 @@ libstrophe is a lightweight XMPP client library written in C. It has
minimal dependencies and is configurable for various environments. It
runs well on Linux, Unix and Windows based platforms.
libstrophe is dual licensed under MIT and GPLv3.
libstrophe is dual licensed under MIT or GPLv3.
Build Instructions
------------------
@@ -88,7 +88,7 @@ or if you have everything configured properly:
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
--------

View File

@@ -1,6 +1,6 @@
m4_define([v_maj], [0])
m4_define([v_min], [12])
m4_define([v_patch], [3])
m4_define([v_min], [14])
m4_define([v_patch], [0])
m4_define([project_version], [v_maj.v_min.v_patch])
m4_define([lt_cur], m4_eval(v_maj + v_min))
@@ -18,9 +18,10 @@ PKG_PROG_PKG_CONFIG
AC_CANONICAL_HOST
AS_CASE([$host_os],
[dragonfly*], [PLATFORM="bsd"],
[freebsd*], [PLATFORM="bsd"],
[openbsd*], [PLATFORM="bsd"],
[netbsd*], [PLATFORM="bsd"],
[openbsd*], [PLATFORM="bsd"],
[*nto*|*qnx*], [PLATFORM="qnx"],
[*solaris*], [PLATFORM="solaris"],
[*android*], [PLATFORM="android"],
@@ -51,6 +52,8 @@ AC_ARG_ENABLE([cares],
[AS_HELP_STRING([--enable-cares], [use c-ares for DNS resolution])])
AC_ARG_ENABLE([getrandom],
[AS_HELP_STRING([--disable-getrandom], [disable usage of the getrandom() system call])])
AC_ARG_ENABLE([zlib],
[AS_HELP_STRING([--disable-zlib], [disable compression support])])
AC_ARG_ENABLE([fuzzing],
[AS_HELP_STRING([--enable-fuzzing], [turn on fuzzing test])],
@@ -247,6 +250,18 @@ fi
fi
if test "x$enable_zlib" != xno; then
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
[
PC_REQUIRES="zlib ${PC_REQUIRES}"
ZLIB_CFLAGS=$zlib_CFLAGS
ZLIB_LIBS=$zlib_LIBS
AC_DEFINE([HAVE_ZLIB])
],
[AC_MSG_ERROR([zlib not found])])
# TODO: if pkg-config doesn't find, check the library manually
fi
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
[AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir],
@@ -255,7 +270,9 @@ m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
AM_CONDITIONAL([DISABLE_COMPRESSION], [test x$enable_zlib = xno])
AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno])
AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno])
AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno])
AM_CONDITIONAL([TLS_WITH_GNUTLS], [test x$with_gnutls = xyes])
AM_CONDITIONAL([TLS_WITH_SCHANNEL], [test x$with_schannel = xyes])
@@ -289,5 +306,7 @@ AC_SUBST(PARSER_LIBS)
AC_SUBST(RESOLV_CFLAGS)
AC_SUBST(RESOLV_LIBS)
AC_SUBST(WARNING_FLAGS)
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
AC_CONFIG_FILES([Makefile libstrophe.pc])
AC_OUTPUT

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* active.c
** libstrophe XMPP client library -- basic usage example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* This example demonstrates basic handler functions by printing out

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* basic.c
** libstrophe XMPP client library -- basic usage example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* bot.c
** libstrophe XMPP client library -- basic usage example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* simple bot example
@@ -76,13 +77,19 @@ int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
return 1;
}
static int _quit_handler(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
xmpp_disconnect(conn);
return 0;
}
int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *body, *reply;
const char *type;
char *intext, *replytext;
int quit = 0;
body = xmpp_stanza_get_child_by_name(stanza, "body");
if (body == NULL)
@@ -102,11 +109,11 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
if (strcmp(intext, "quit") == 0) {
replytext = strdup("bye!");
quit = 1;
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
} else if (strcmp(intext, "reconnect") == 0) {
replytext = strdup("alright, let's see what happens!");
reconnect = 1;
quit = 1;
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
} else {
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
strcpy(replytext, intext);
@@ -119,9 +126,6 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_stanza_release(reply);
free(replytext);
if (quit)
xmpp_disconnect(conn);
return 1;
}
@@ -213,7 +217,10 @@ static void usage(int exit_code)
" --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
"Note: --disable-tls conflicts with --mandatory-tls or "
"--legacy-ssl\n");
"--legacy-ssl\n"
" --zlib Enable compression via zlib.\n"
" --dont-reset When using zlib, don't do a full-flush "
"after compression.\n");
exit(exit_code);
}
@@ -243,6 +250,10 @@ int main(int argc, char **argv)
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
else if (strcmp(argv[i], "--legacy-auth") == 0)
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
else if (strcmp(argv[i], "--zlib") == 0)
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
else if (strcmp(argv[i], "--dont-reset") == 0)
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
jid = argv[i];
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* complex.c
** libstrophe XMPP client library -- more complex usage example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>
@@ -238,6 +239,9 @@ static void usage(int exit_code)
" --enable-certfail Enable certfail handler.\n"
" --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
" --zlib Enable compression via zlib.\n"
" --dont-reset When using zlib, don't do a full-flush "
"after compression.\n"
" --verbose Increase the verbosity level.\n"
" --tcp-keepalive Configure TCP keepalive.\n\n"
"Note: --disable-tls conflicts with --mandatory-tls or "
@@ -272,6 +276,10 @@ int main(int argc, char **argv)
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
else if (strcmp(argv[i], "--legacy-auth") == 0)
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
else if (strcmp(argv[i], "--zlib") == 0)
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
else if (strcmp(argv[i], "--dont-reset") == 0)
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
else if (strcmp(argv[i], "--verbose") == 0)
verbosity++;
else if (strcmp(argv[i], "--tcp-keepalive") == 0)

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* component.c
** libstrophe XMPP client library -- external component (XEP-0114) example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* This example demonstrates simple connection to a server

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* perf.c
* strophe XMPP client library -- performance measure
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* register.c
* strophe XMPP client library -- In-band registration (XEP-0077)
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/*

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* roster.c
** libstrophe XMPP client library -- handler example
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* This example demonstrates basic handler functions by printing out

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* vcard.c
* strophe XMPP client library -- vCard example
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>

View File

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

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* auth.c
** strophe XMPP client library -- auth functions and handlers
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -61,6 +62,7 @@
static void _auth(xmpp_conn_t *conn);
static void _auth_legacy(xmpp_conn_t *conn);
static void _handle_open_compress(xmpp_conn_t *conn);
static void _handle_open_sasl(xmpp_conn_t *conn);
static void _handle_open_tls(xmpp_conn_t *conn);
@@ -71,6 +73,9 @@ static int _handle_component_hs_response(xmpp_conn_t *conn,
static int
_handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
static int _handle_features_compress(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata);
static int
_handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
@@ -82,7 +87,8 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
static int _handle_scram_challenge(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata);
static char *_make_scram_init_msg(xmpp_conn_t *conn);
struct scram_user_data;
static int _make_scram_init_msg(struct scram_user_data *scram);
static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata);
static int _handle_missing_bind(xmpp_conn_t *conn, void *userdata);
@@ -205,12 +211,54 @@ static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
return 0;
}
typedef void (*text_handler)(xmpp_conn_t *conn, const char *text);
static void _foreach_child(xmpp_conn_t *conn,
xmpp_stanza_t *parent,
const char *name,
text_handler hndl)
{
xmpp_stanza_t *children;
for (children = xmpp_stanza_get_children(parent); children;
children = xmpp_stanza_get_next(children)) {
const char *child_name = xmpp_stanza_get_name(children);
if (child_name && strcmp(child_name, name) == 0) {
char *text = xmpp_stanza_get_text(children);
if (text == NULL)
continue;
hndl(conn, text);
strophe_free(conn->ctx, text);
}
}
}
static void _handle_sasl_children(xmpp_conn_t *conn, const char *text)
{
if (strcasecmp(text, "PLAIN") == 0) {
conn->sasl_support |= SASL_MASK_PLAIN;
} else if (strcasecmp(text, "EXTERNAL") == 0 &&
(conn->tls_client_cert || conn->tls_client_key)) {
conn->sasl_support |= SASL_MASK_EXTERNAL;
} else if (strcasecmp(text, "DIGEST-MD5") == 0) {
conn->sasl_support |= SASL_MASK_DIGESTMD5;
} else if (strcasecmp(text, "ANONYMOUS") == 0) {
conn->sasl_support |= SASL_MASK_ANONYMOUS;
} else {
size_t n;
for (n = 0; n < scram_algs_num; ++n) {
if (strcasecmp(text, scram_algs[n]->scram_name) == 0) {
conn->sasl_support |= scram_algs[n]->mask;
break;
}
}
}
}
static int
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *child, *mech;
const char *ns;
char *text;
xmpp_stanza_t *child;
UNUSED(userdata);
@@ -220,10 +268,9 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
/* check for TLS */
if (!conn->secured) {
if (!conn->tls_disabled) {
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
if (child) {
ns = xmpp_stanza_get_ns(child);
conn->tls_support = ns != NULL && strcmp(ns, XMPP_NS_TLS) == 0;
if (xmpp_stanza_get_child_by_name_and_ns(stanza, "starttls",
XMPP_NS_TLS)) {
conn->tls_support = 1;
}
} else {
conn->tls_support = 0;
@@ -231,36 +278,10 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
}
/* check for SASL */
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
ns = child ? xmpp_stanza_get_ns(child) : NULL;
if (child && ns && strcmp(ns, XMPP_NS_SASL) == 0) {
for (mech = xmpp_stanza_get_children(child); mech;
mech = xmpp_stanza_get_next(mech)) {
if (xmpp_stanza_get_name(mech) &&
strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
text = xmpp_stanza_get_text(mech);
if (text == NULL)
continue;
if (strcasecmp(text, "PLAIN") == 0)
conn->sasl_support |= SASL_MASK_PLAIN;
else if (strcasecmp(text, "EXTERNAL") == 0 &&
(conn->tls_client_cert || conn->tls_client_key))
conn->sasl_support |= SASL_MASK_EXTERNAL;
else if (strcasecmp(text, "DIGEST-MD5") == 0)
conn->sasl_support |= SASL_MASK_DIGESTMD5;
else if (strcasecmp(text, "SCRAM-SHA-1") == 0)
conn->sasl_support |= SASL_MASK_SCRAMSHA1;
else if (strcasecmp(text, "SCRAM-SHA-256") == 0)
conn->sasl_support |= SASL_MASK_SCRAMSHA256;
else if (strcasecmp(text, "SCRAM-SHA-512") == 0)
conn->sasl_support |= SASL_MASK_SCRAMSHA512;
else if (strcasecmp(text, "ANONYMOUS") == 0)
conn->sasl_support |= SASL_MASK_ANONYMOUS;
strophe_free(conn->ctx, text);
}
}
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "mechanisms",
XMPP_NS_SASL);
if (child) {
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
}
/* Disable PLAIN when other secure mechanisms are supported */
@@ -334,7 +355,9 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
(char *)userdata);
/* reset parser */
conn_prepare_reset(conn, _handle_open_sasl);
conn_prepare_reset(conn, conn->compression.allowed
? _handle_open_compress
: _handle_open_sasl);
/* send stream tag */
conn_open_stream(conn);
@@ -438,7 +461,11 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
}
struct scram_user_data {
xmpp_conn_t *conn;
int sasl_plus;
char *scram_init;
char *channel_binding;
const char *first_bare;
const struct hash_alg *alg;
};
@@ -470,8 +497,9 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
if (!challenge)
goto err;
response = sasl_scram(conn->ctx, scram_ctx->alg, challenge,
scram_ctx->scram_init, conn->jid, conn->pass);
response =
sasl_scram(conn->ctx, scram_ctx->alg, scram_ctx->channel_binding,
challenge, scram_ctx->first_bare, conn->jid, conn->pass);
strophe_free(conn->ctx, challenge);
if (!response)
goto err;
@@ -505,7 +533,8 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
*/
rc = _handle_sasl_result(conn, stanza,
(void *)scram_ctx->alg->scram_name);
strophe_free(conn->ctx, scram_ctx->scram_init);
strophe_free_and_null(conn->ctx, scram_ctx->channel_binding);
strophe_free_and_null(conn->ctx, scram_ctx->scram_init);
strophe_free(conn->ctx, scram_ctx);
}
@@ -516,33 +545,103 @@ err_release_auth:
err_free_response:
strophe_free(conn->ctx, response);
err:
strophe_free(conn->ctx, scram_ctx->scram_init);
strophe_free_and_null(conn->ctx, scram_ctx->channel_binding);
strophe_free_and_null(conn->ctx, scram_ctx->scram_init);
strophe_free(conn->ctx, scram_ctx);
disconnect_mem_error(conn);
return 0;
}
static char *_make_scram_init_msg(xmpp_conn_t *conn)
static int _make_scram_init_msg(struct scram_user_data *scram)
{
xmpp_conn_t *conn = scram->conn;
xmpp_ctx_t *ctx = conn->ctx;
size_t message_len;
char *node;
char *message;
char nonce[32];
const void *binding_data;
const char *binding_type;
char *node, *message;
size_t message_len, binding_type_len = 0, binding_data_len;
int l, is_secured = xmpp_conn_is_secured(conn);
/* This buffer must be able to hold:
* "p=<10 bytes binding type>,,<36 bytes binding data>"
* + alignment */
char buf[56];
if (scram->sasl_plus) {
if (!is_secured) {
strophe_error(
ctx, "xmpp",
"SASL: Server requested a -PLUS variant to authenticate, "
"but the connection is not secured. This is an error on "
"the server side we can't do anything about.");
return -1;
}
if (tls_init_channel_binding(conn->tls, &binding_type,
&binding_type_len)) {
return -1;
}
/* directly account for the '=' char in 'p=<binding-type>' */
binding_type_len += 1;
}
node = xmpp_jid_node(ctx, conn->jid);
if (!node) {
return NULL;
return -1;
}
xmpp_rand_nonce(ctx->rand, nonce, sizeof(nonce));
message_len = strlen(node) + strlen(nonce) + 8 + 1;
/* 32 bytes nonce is enough */
xmpp_rand_nonce(ctx->rand, buf, 33);
message_len = strlen(node) + strlen(buf) + 8 + binding_type_len + 1;
message = strophe_alloc(ctx, message_len);
if (message) {
strophe_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce);
if (!message) {
goto err_node;
}
/* increase length to account for 'y,,', 'n,,' or 'p,,'.
* In the 'p' case the '=' sign has already been accounted for above.
*/
binding_type_len += 3;
if (scram->sasl_plus) {
l = strophe_snprintf(message, message_len, "p=%s,,n=%s,r=%s",
binding_type, node, buf);
} else {
l = strophe_snprintf(message, message_len, "%c,,n=%s,r=%s",
is_secured ? 'y' : 'n', node, buf);
}
if (l < 0 || (size_t)l >= message_len) {
goto err_msg;
}
if (binding_type_len > sizeof(buf)) {
goto err_msg;
}
/* Make `first_bare` point to the 'n' of 'n=<node>' of the
* client-first-message */
scram->first_bare = message + binding_type_len;
memcpy(buf, message, binding_type_len);
if (scram->sasl_plus) {
binding_data =
tls_get_channel_binding_data(conn->tls, &binding_data_len);
if (!binding_data) {
goto err_msg;
}
if (binding_data_len > sizeof(buf) - binding_type_len) {
strophe_error(ctx, "xmpp", "Channel binding data is too long (%zu)",
binding_data_len);
goto err_msg;
}
memcpy(&buf[binding_type_len], binding_data, binding_data_len);
binding_type_len += binding_data_len;
}
scram->channel_binding =
xmpp_base64_encode(ctx, (void *)buf, binding_type_len);
memset(buf, 0, binding_type_len);
strophe_free(ctx, node);
scram->scram_init = message;
return message;
return 0;
err_msg:
strophe_free(ctx, message);
err_node:
strophe_free(ctx, node);
return -1;
}
static xmpp_stanza_t *_make_starttls(xmpp_conn_t *conn)
@@ -635,7 +734,7 @@ static void _auth(xmpp_conn_t *conn)
return;
}
if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
if (anonjid && (conn->sasl_support & SASL_MASK_ANONYMOUS)) {
/* some crap here */
auth = _make_sasl_auth(conn, "ANONYMOUS");
if (!auth) {
@@ -701,22 +800,26 @@ static void _auth(xmpp_conn_t *conn)
"Password hasn't been set, and SASL ANONYMOUS unsupported.");
xmpp_disconnect(conn);
} else if (conn->sasl_support & SASL_MASK_SCRAM) {
size_t n;
scram_ctx = strophe_alloc(conn->ctx, sizeof(*scram_ctx));
if (conn->sasl_support & SASL_MASK_SCRAMSHA512)
scram_ctx->alg = &scram_sha512;
else if (conn->sasl_support & SASL_MASK_SCRAMSHA256)
scram_ctx->alg = &scram_sha256;
else if (conn->sasl_support & SASL_MASK_SCRAMSHA1)
scram_ctx->alg = &scram_sha1;
memset(scram_ctx, 0, sizeof(*scram_ctx));
for (n = 0; n < scram_algs_num; ++n) {
if (conn->sasl_support & scram_algs[n]->mask) {
scram_ctx->alg = scram_algs[n];
break;
}
}
auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name);
if (!auth) {
disconnect_mem_error(conn);
return;
}
/* don't free scram_init on success */
scram_ctx->scram_init = _make_scram_init_msg(conn);
if (!scram_ctx->scram_init) {
scram_ctx->conn = conn;
scram_ctx->sasl_plus =
scram_ctx->alg->mask & SASL_MASK_SCRAM_PLUS ? 1 : 0;
if (_make_scram_init_msg(scram_ctx)) {
strophe_free(conn->ctx, scram_ctx);
xmpp_stanza_release(auth);
disconnect_mem_error(conn);
@@ -752,7 +855,7 @@ static void _auth(xmpp_conn_t *conn)
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
/* SASL SCRAM-SHA-1 was tried, unset flag */
/* SASL algorithm was tried, unset flag */
conn->sasl_support &= ~scram_ctx->alg->mask;
} else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
auth = _make_sasl_auth(conn, "DIGEST-MD5");
@@ -812,10 +915,10 @@ static void _auth(xmpp_conn_t *conn)
}
}
static void _auth_success(xmpp_conn_t *conn)
static void _stream_negotiation_success(xmpp_conn_t *conn)
{
tls_clear_password_cache(conn);
conn->authenticated = 1;
conn->stream_negotiation_completed = 1;
/* call connection handler */
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
}
@@ -865,6 +968,17 @@ static void _handle_open_sasl(xmpp_conn_t *conn)
NULL);
}
/* called when stream:stream tag received after compression has been enabled */
static void _handle_open_compress(xmpp_conn_t *conn)
{
strophe_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
/* setup stream:features handlers */
handler_add(conn, _handle_features_compress, XMPP_NS_STREAMS, "features",
NULL, NULL);
handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
}
static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
{
xmpp_stanza_t *iq, *res, *text;
@@ -877,6 +991,7 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
/* send bind request */
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_bind1");
if (!iq) {
xmpp_stanza_release(bind);
disconnect_mem_error(conn);
return 0;
}
@@ -921,24 +1036,78 @@ static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
return 0;
}
static int _handle_compress_result(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
{
const char *name = xmpp_stanza_get_name(stanza);
UNUSED(userdata);
if (!name)
return 0;
if (strcmp(name, "compressed") == 0) {
/* Stream compression enabled, we need to restart the stream */
strophe_debug(conn->ctx, "xmpp", "Stream compression enabled");
/* reset parser */
conn_prepare_reset(conn, _handle_open_sasl);
/* make compression effective */
compression_init(conn);
/* send stream tag */
conn_open_stream(conn);
}
return 0;
}
static int _handle_features_compress(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata)
{
const char *compress = "<compress xmlns='" XMPP_NS_COMPRESSION
"'><method>zlib</method></compress>";
xmpp_stanza_t *child;
/* remove missing features handler */
xmpp_timed_handler_delete(conn, _handle_missing_features);
/* check for compression */
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "compression",
XMPP_NS_FEATURE_COMPRESSION);
if (conn->compression.allowed && child) {
_foreach_child(conn, child, "method",
compression_handle_feature_children);
}
if (conn->compression.supported) {
send_raw(conn, compress, strlen(compress), XMPP_QUEUE_STROPHE, NULL);
handler_add(conn, _handle_compress_result, XMPP_NS_COMPRESSION, NULL,
NULL, NULL);
} else {
return _handle_features_sasl(conn, stanza, userdata);
}
return 0;
}
static int
_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;
const char *ns;
char h[11];
UNUSED(userdata);
/* remove missing features handler */
/* Remove missing features handler */
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
/* check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name(stanza, "bind");
/* Check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name_and_ns(stanza, "bind", XMPP_NS_BIND);
if (bind) {
ns = xmpp_stanza_get_ns(bind);
conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
conn->bind_required = 1;
bind = xmpp_stanza_copy(bind);
if (!bind) {
disconnect_mem_error(conn);
@@ -948,25 +1117,31 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
conn->bind_required = 0;
}
/* check whether session establishment is required */
session = xmpp_stanza_get_child_by_name(stanza, "session");
/* Check whether session establishment is required.
*
* 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) {
ns = xmpp_stanza_get_ns(session);
opt = xmpp_stanza_get_child_by_name(session, "optional");
if (!opt)
conn->session_required =
ns != NULL && strcmp(ns, XMPP_NS_SESSION) == 0;
conn->session_required =
xmpp_stanza_get_child_by_name(session, "optional") == NULL;
}
/* Check stream-management support */
if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) {
/* stream management supported */
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 */
/* 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 &&
conn->sm_state->previd && conn->sm_state->bound_jid) {
resume = xmpp_stanza_new(conn->ctx);
@@ -1013,11 +1188,58 @@ static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata)
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;
trigger_sm_callback(conn);
}
static int
_handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
const char *type;
xmpp_stanza_t *iq, *enable, *session, *binding, *jid_stanza;
xmpp_stanza_t *binding, *jid_stanza;
UNUSED(userdata);
@@ -1042,50 +1264,18 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
/* establish a session if required */
if (conn->session_required) {
/* setup response handlers */
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT,
NULL);
/* 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);
_session_start(conn);
}
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);
/* send enable directly after the bind request */
else if (conn->sm_state->sm_support && !conn->sm_disable) {
_sm_enable(conn);
}
if (!conn->session_required) {
_auth_success(conn);
/* 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
* successful
*/
else {
_stream_negotiation_success(conn);
}
} else {
strophe_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
@@ -1121,8 +1311,11 @@ _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_disconnect(conn);
} else if (type && strcmp(type, "result") == 0) {
strophe_debug(conn->ctx, "xmpp", "Session establishment successful.");
_auth_success(conn);
if (conn->sm_state->sm_support && !conn->sm_disable) {
_sm_enable(conn);
} else {
_stream_negotiation_success(conn);
}
} else {
strophe_error(conn->ctx, "xmpp",
"Server sent malformed session reply.");
@@ -1153,23 +1346,59 @@ static int _handle_missing_legacy(xmpp_conn_t *conn, void *userdata)
return 0;
}
static int _get_h_attribute(xmpp_stanza_t *stanza, unsigned long *ul_h)
{
const char *h = xmpp_stanza_get_attribute(stanza, "h");
if (!h || string_to_ul(h, ul_h)) {
strophe_error(
stanza->ctx, "xmpp",
"SM error: failed parsing 'h', \"%s\" got converted to %llu.",
STR_MAYBE_NULL(h), *ul_h);
return -1;
}
return 0;
}
static void _sm_queue_cleanup(xmpp_conn_t *conn, unsigned long ul_h)
{
xmpp_send_queue_t *e;
while ((e = peek_queue_front(&conn->sm_state->sm_queue))) {
if (e->sm_h >= ul_h)
break;
e = pop_queue_front(&conn->sm_state->sm_queue);
strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
}
}
static void _sm_queue_resend(xmpp_conn_t *conn)
{
xmpp_send_queue_t *e;
while ((e = pop_queue_front(&conn->sm_state->sm_queue))) {
/* Re-send what was already sent out and is still in the
* SM queue (i.e. it hasn't been ACK'ed by the server)
*/
strophe_debug_verbose(2, conn->ctx, "conn", "SM_Q_RESEND: %p, h=%lu", e,
e->sm_h);
send_raw(conn, e->data, e->len, e->owner, NULL);
strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
}
}
static int _handle_sm(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
{
xmpp_stanza_t *failed_cause;
const char *name, *id, *previd, *resume, *h, *cause;
xmpp_send_queue_t *e;
xmpp_stanza_t *failed_cause, *bind = NULL;
const char *name, *id, *previd, *resume, *cause;
unsigned long ul_h = 0;
UNUSED(userdata);
name = xmpp_stanza_get_name(stanza);
if (!name)
goto LBL_ERR;
goto err_sm;
if (strcmp(name, "enabled") == 0) {
conn->sm_state->sm_enabled = 1;
conn->sm_state->sm_handled_nr = 0;
resume = xmpp_stanza_get_attribute(stanza, "resume");
if (resume && (strcasecmp(resume, "true") || strcmp(resume, "1"))) {
@@ -1179,11 +1408,17 @@ static int _handle_sm(xmpp_conn_t *const conn,
"SM error: server said it can resume, but "
"didn't provide an ID.");
name = NULL;
goto LBL_ERR;
goto err_sm;
}
conn->sm_state->can_resume = 1;
conn->sm_state->id = strophe_strdup(conn->ctx, id);
}
/* We maybe have stuff in the SM queue if we tried to resume, but the
* server doesn't remember all details of our session, but the `h` was
* still available.
*/
_sm_queue_resend(conn);
_stream_negotiation_success(conn);
} else if (strcmp(name, "resumed") == 0) {
previd = xmpp_stanza_get_attribute(stanza, "previd");
if (!previd || strcmp(previd, conn->sm_state->previd)) {
@@ -1191,16 +1426,11 @@ static int _handle_sm(xmpp_conn_t *const conn,
"SM error: previd didn't match, ours is \"%s\".",
conn->sm_state->previd);
name = NULL;
goto LBL_ERR;
goto err_sm;
}
h = xmpp_stanza_get_attribute(stanza, "h");
if (!h || string_to_ul(h, &ul_h)) {
strophe_error(conn->ctx, "xmpp",
"SM error: failed parsing 'h', it got converted "
"to %llu.",
ul_h);
if (_get_h_attribute(stanza, &ul_h)) {
name = NULL;
goto LBL_ERR;
goto err_sm;
}
conn->sm_state->sm_enabled = 1;
conn->sm_state->id = conn->sm_state->previd;
@@ -1211,54 +1441,55 @@ static int _handle_sm(xmpp_conn_t *const conn,
conn->sm_state->sm_sent_nr = conn->sm_state->sm_queue.head->sm_h;
else
conn->sm_state->sm_sent_nr = ul_h;
while ((e = pop_queue_front(&conn->sm_state->sm_queue))) {
if (e->sm_h >= ul_h) {
/* Re-send what was already sent out and is still in the
* SM queue (i.e. it hasn't been ACK'ed by the server)
*/
send_raw(conn, e->data, e->len, e->owner, NULL);
}
strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
}
_sm_queue_cleanup(conn, ul_h);
_sm_queue_resend(conn);
strophe_debug(conn->ctx, "xmpp", "Session resumed successfully.");
_auth_success(conn);
_stream_negotiation_success(conn);
} else if (strcmp(name, "failed") == 0) {
name = NULL;
conn->sm_state->sm_enabled = 0;
failed_cause =
xmpp_stanza_get_child_by_ns(stanza, XMPP_NS_STANZAS_IETF);
if (!failed_cause)
goto LBL_ERR;
goto err_sm;
cause = xmpp_stanza_get_name(failed_cause);
if (!cause)
goto LBL_ERR;
goto err_sm;
if (!strcmp(cause, "item-not-found") ||
!strcmp(cause, "feature-not-implemented")) {
if (!strcmp(cause, "item-not-found")) {
if (conn->sm_state->resume) {
conn->sm_state->resume = 0;
conn->sm_state->can_resume = 0;
/* remember that the server reports having support
* for resumption, but actually it doesn't ...
/* It's no error if there's no `h` attribute included
* but if there is, it gives a hint at what the server
* already received.
*/
conn->sm_state->dont_request_resume =
!strcmp(cause, "feature-not-implemented");
strophe_free(conn->ctx, conn->sm_state->previd);
conn->sm_state->previd = NULL;
strophe_free(conn->ctx, conn->sm_state->bound_jid);
conn->sm_state->bound_jid = NULL;
_do_bind(conn, conn->sm_state->bind);
conn->sm_state->bind = NULL;
if (!_get_h_attribute(stanza, &ul_h)) {
/* In cases there's no `h` included, drop all elements. */
ul_h = (unsigned long)-1;
}
_sm_queue_cleanup(conn, ul_h);
}
} else if (!strcmp(cause, "feature-not-implemented")) {
conn->sm_state->resume = 0;
conn->sm_state->can_resume = 0;
/* remember that the server reports having support
* for resumption, but actually it doesn't ...
*/
conn->sm_state->dont_request_resume = 1;
}
conn->sm_state->sm_handled_nr = 0;
bind = conn->sm_state->bind;
conn->sm_state->bind = NULL;
reset_sm_state(conn->sm_state);
_do_bind(conn, bind);
} else {
/* unknown stanza received */
name = NULL;
}
LBL_ERR:
trigger_sm_callback(conn);
err_sm:
if (!name) {
char *err = "Couldn't convert stanza to text!";
char *buf;
@@ -1277,7 +1508,8 @@ LBL_ERR:
buf);
if (buf != err)
strophe_free(conn->ctx, buf);
conn->sm_state->sm_enabled = 0;
/* Don't disable for <failure> cases, they're no hard errors */
conn->sm_state->sm_enabled = bind != NULL;
}
return 0;
}
@@ -1310,7 +1542,7 @@ _handle_legacy(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
/* auth succeeded */
strophe_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
_auth_success(conn);
_stream_negotiation_success(conn);
} else {
strophe_error(conn->ctx, "xmpp",
"Server sent us a legacy authentication "
@@ -1500,7 +1732,7 @@ int _handle_component_hs_response(xmpp_conn_t *conn,
xmpp_disconnect(conn);
return XMPP_EINT;
} else {
_auth_success(conn);
_stream_negotiation_success(conn);
}
/* We don't need this handler anymore, return 0 so it can be deleted
@@ -1522,8 +1754,8 @@ int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata)
void auth_handle_open_raw(xmpp_conn_t *conn)
{
handler_reset_timed(conn, 0);
/* user handlers are not called before authentication is completed. */
_auth_success(conn);
/* user handlers are not called before stream negotiation has completed. */
_stream_negotiation_success(conn);
}
void auth_handle_open_stub(xmpp_conn_t *conn)

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* common.h
** strophe XMPP client library -- internal common structures
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -29,12 +30,17 @@
#include "snprintf.h"
/** handlers **/
#if (__STDC_VERSION__ >= 202000L)
typedef void* xmpp_void_handler;
#else
typedef int (*xmpp_void_handler)();
#endif
typedef struct _xmpp_handlist_t xmpp_handlist_t;
struct _xmpp_handlist_t {
/* common members */
int user_handler;
int (*handler)();
xmpp_void_handler handler;
void *userdata;
int enabled; /* handlers are added disabled and enabled after the
* handler chain is processed to prevent stanzas from
@@ -92,6 +98,13 @@ void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s);
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
void strophe_free(const xmpp_ctx_t *ctx, void *p);
#define strophe_free_and_null(ctx, p) \
do { \
if (p) { \
strophe_free(ctx, (p)); \
(p) = NULL; \
} \
} while (0)
/* wrappers for xmpp_log at specific levels */
void strophe_error(const xmpp_ctx_t *ctx,
@@ -119,6 +132,12 @@ void strophe_log_internal(const xmpp_ctx_t *ctx,
const char *fmt,
va_list ap);
#if defined(__OpenBSD__)
#define STR_MAYBE_NULL(p) (p) ? (p) : "(null)"
#else
#define STR_MAYBE_NULL(p) (p)
#endif
/** connection **/
/* opaque connection object */
@@ -159,9 +178,16 @@ struct _xmpp_send_queue_t {
#define SASL_MASK_SCRAMSHA256 (1 << 4)
#define SASL_MASK_SCRAMSHA512 (1 << 5)
#define SASL_MASK_EXTERNAL (1 << 6)
#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7)
#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8)
#define SASL_MASK_SCRAMSHA512_PLUS (1 << 9)
#define SASL_MASK_SCRAM \
#define SASL_MASK_SCRAM_PLUS \
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS | \
SASL_MASK_SCRAMSHA512_PLUS)
#define SASL_MASK_SCRAM_WEAK \
(SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512)
#define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK)
enum {
XMPP_PORT_CLIENT = 5222,
@@ -180,14 +206,36 @@ struct _xmpp_sm_t {
int sm_support;
int sm_enabled;
int can_resume, resume, dont_request_resume;
xmpp_queue_t sm_queue;
int r_sent;
uint32_t sm_handled_nr;
uint32_t sm_sent_nr;
xmpp_queue_t sm_queue;
char *id, *previd, *bound_jid;
xmpp_stanza_t *bind;
};
struct conn_interface {
int (*read)(struct conn_interface *intf, void *buff, size_t len);
int (*write)(struct conn_interface *intf, const void *buff, size_t len);
int (*flush)(struct conn_interface *intf);
int (*pending)(struct conn_interface *intf);
int (*get_error)(struct conn_interface *intf);
int (*error_is_recoverable)(struct conn_interface *intf, int err);
xmpp_conn_t *conn;
};
int conn_interface_write(struct conn_interface *intf,
const void *buff,
size_t len);
int conn_int_nop(struct conn_interface *intf);
int compression_init(xmpp_conn_t *conn);
void compression_free(xmpp_conn_t *conn);
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
struct _xmpp_conn_t {
struct conn_interface intf;
unsigned int ref;
xmpp_ctx_t *ctx;
xmpp_conn_type_t type;
@@ -235,6 +283,11 @@ struct _xmpp_conn_t {
int sm_disable;
xmpp_sm_state_t *sm_state;
struct {
struct xmpp_compression *state;
int allowed, supported, dont_reset;
} compression;
char *lang;
char *domain;
char *jid;
@@ -262,8 +315,9 @@ struct _xmpp_conn_t {
/* stream open handler */
xmpp_open_handler open_handler;
/* user handlers only get called after authentication */
int authenticated;
/* user handlers only get called after the stream negotiation has completed
*/
int stream_negotiation_completed;
/* connection events handler */
xmpp_conn_handler conn_handler;
@@ -274,6 +328,8 @@ struct _xmpp_conn_t {
hash_t *id_handlers;
xmpp_handlist_t *handlers;
xmpp_sockopt_callback sockopt_cb;
xmpp_sm_callback sm_callback;
void *sm_callback_ctx;
};
void conn_disconnect(xmpp_conn_t *conn);
@@ -327,6 +383,8 @@ void handler_add(xmpp_conn_t *conn,
void handler_system_delete_all(xmpp_conn_t *conn);
/* utility functions */
void trigger_sm_callback(xmpp_conn_t *conn);
void reset_sm_state(xmpp_sm_state_t *sm_state);
void disconnect_mem_error(xmpp_conn_t *conn);
/* auth functions */
@@ -337,6 +395,7 @@ void auth_handle_open_stub(xmpp_conn_t *conn);
/* queue functions */
void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item);
xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue);
xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue);
char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e);

270
src/compression.c Normal file
View File

@@ -0,0 +1,270 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* compression.c
** strophe XMPP client library -- XEP-0138 Stream Compression
**
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
* XEP-0138 Stream Compression.
*/
#include <zlib.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#ifndef STROPHE_COMPRESSION_BUFFER_SIZE
/** Max buffer size for compressed data (send & receive). */
#define STROPHE_COMPRESSION_BUFFER_SIZE 4096
#endif
struct zlib_compression {
void *buffer, *buffer_end;
z_stream stream;
};
struct xmpp_compression {
xmpp_conn_t *conn;
struct zlib_compression compression, decompression;
struct conn_interface next;
};
static int _conn_decompress(struct xmpp_compression *comp,
size_t c_len,
void *buff,
size_t len)
{
if (comp->decompression.stream.next_in == NULL) {
comp->decompression.stream.next_in = comp->decompression.buffer;
comp->decompression.buffer_end =
comp->decompression.stream.next_in + c_len;
comp->decompression.stream.avail_in = c_len;
} else if (c_len) {
strophe_error(comp->conn->ctx, "zlib",
"_conn_decompress() called with c_len=%zu", c_len);
}
comp->decompression.stream.next_out = buff;
comp->decompression.stream.avail_out = len;
int ret = inflate(&comp->decompression.stream, Z_SYNC_FLUSH);
switch (ret) {
case Z_STREAM_END:
case Z_OK:
if (comp->decompression.buffer_end ==
comp->decompression.stream.next_in)
comp->decompression.stream.next_in = NULL;
return comp->decompression.stream.next_out - (Bytef *)buff;
case Z_BUF_ERROR:
break;
default:
strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret);
comp->conn->error = ret;
conn_disconnect(comp->conn);
break;
}
return 0;
}
static int compression_read(struct conn_interface *intf, void *buff, size_t len)
{
xmpp_conn_t *conn = intf->conn;
struct xmpp_compression *comp = conn->compression.state;
void *dbuff = buff;
size_t dlen = len;
if (comp->decompression.stream.next_in != NULL) {
return _conn_decompress(comp, 0, buff, len);
}
dbuff = comp->decompression.buffer;
dlen = STROPHE_COMPRESSION_BUFFER_SIZE;
int ret = comp->next.read(intf, dbuff, dlen);
if (ret > 0) {
return _conn_decompress(comp, ret, buff, len);
}
return ret;
}
static int _try_compressed_write_to_network(xmpp_conn_t *conn, int force)
{
struct xmpp_compression *comp = conn->compression.state;
int ret = 0;
ptrdiff_t len =
comp->compression.stream.next_out - (Bytef *)comp->compression.buffer;
int buffer_full =
comp->compression.stream.next_out == comp->compression.buffer_end;
if ((buffer_full || force) && len > 0) {
ret = conn_interface_write(&comp->next, comp->compression.buffer, len);
if (ret < 0)
return ret;
comp->compression.stream.next_out = comp->compression.buffer;
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
}
return ret;
}
static int
_compression_write(xmpp_conn_t *conn, const void *buff, size_t len, int flush)
{
int ret;
const void *buff_end = buff + len;
struct xmpp_compression *comp = conn->compression.state;
comp->compression.stream.next_in = (Bytef *)buff;
comp->compression.stream.avail_in = len;
do {
ret = _try_compressed_write_to_network(conn, 0);
if (ret < 0) {
return ret;
}
ret = deflate(&comp->compression.stream, flush);
if (ret == Z_STREAM_END) {
break;
}
if (flush && ret == Z_BUF_ERROR) {
break;
}
if (ret != Z_OK) {
strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
conn->error = ret;
conn_disconnect(conn);
return ret;
}
ret = comp->compression.stream.next_in - (Bytef *)buff;
} while (comp->compression.stream.next_in < (Bytef *)buff_end);
if (flush) {
ret = _try_compressed_write_to_network(conn, 1);
if (ret < 0) {
return ret;
}
}
return ret;
}
static int
compression_write(struct conn_interface *intf, const void *buff, size_t len)
{
return _compression_write(intf->conn, buff, len, Z_NO_FLUSH);
}
static int compression_flush(struct conn_interface *intf)
{
xmpp_conn_t *conn = intf->conn;
struct xmpp_compression *comp = conn->compression.state;
return _compression_write(conn, comp->compression.buffer, 0,
conn->compression.dont_reset ? Z_SYNC_FLUSH
: Z_FULL_FLUSH);
}
static int compression_pending(struct conn_interface *intf)
{
xmpp_conn_t *conn = intf->conn;
struct xmpp_compression *comp = conn->compression.state;
return comp->decompression.stream.next_in != NULL ||
comp->next.pending(intf);
}
static int compression_get_error(struct conn_interface *intf)
{
struct conn_interface *next = &intf->conn->compression.state->next;
return next->get_error(next);
}
static int compression_is_recoverable(struct conn_interface *intf, int err)
{
struct conn_interface *next = &intf->conn->compression.state->next;
return next->error_is_recoverable(next, err);
}
static const struct conn_interface compression_intf = {
compression_read,
compression_write,
compression_flush,
compression_pending,
compression_get_error,
compression_is_recoverable,
NULL,
};
static void *_zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
size_t sz = items * size;
/* Poor man's multiplication overflow check */
if (sz < items || sz < size)
return NULL;
return strophe_alloc(opaque, sz);
}
static void _init_zlib_compression(xmpp_ctx_t *ctx, struct zlib_compression *s)
{
s->buffer = strophe_alloc(ctx, STROPHE_COMPRESSION_BUFFER_SIZE);
s->buffer_end = s->buffer + STROPHE_COMPRESSION_BUFFER_SIZE;
s->stream.opaque = ctx;
s->stream.zalloc = _zlib_alloc;
s->stream.zfree = (free_func)strophe_free;
}
int compression_init(xmpp_conn_t *conn)
{
if (!conn->compression.allowed || !conn->compression.supported)
return -1;
conn->compression.state =
strophe_alloc(conn->ctx, sizeof(*conn->compression.state));
struct xmpp_compression *comp = conn->compression.state;
memset(comp, 0, sizeof(*comp));
comp->conn = conn;
comp->next = conn->intf;
conn->intf = compression_intf;
conn->intf.conn = conn;
_init_zlib_compression(conn->ctx, &comp->compression);
comp->compression.stream.next_out = comp->compression.buffer;
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
int ret = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->compression.buffer);
conn->error = ret;
conn_disconnect(conn);
return ret;
}
_init_zlib_compression(conn->ctx, &comp->decompression);
ret = inflateInit(&comp->decompression.stream);
if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
conn->error = ret;
conn_disconnect(conn);
return ret;
}
return 0;
}
void compression_free(xmpp_conn_t *conn)
{
struct xmpp_compression *comp = conn->compression.state;
if (!comp)
return;
if (comp->compression.buffer) {
deflateEnd(&comp->compression.stream);
strophe_free_and_null(conn->ctx, comp->compression.buffer);
}
if (comp->decompression.buffer) {
inflateEnd(&comp->decompression.stream);
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
}
}
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
{
if (strcasecmp(text, "zlib") == 0) {
conn->compression.supported = 1;
}
}

36
src/compression_dummy.c Normal file
View File

@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* compression_dummy.c
** strophe XMPP client library -- Dummy Compression
**
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
* Dummy Compression.
*/
#include <strings.h>
#include <errno.h>
#include "common.h"
int compression_init(xmpp_conn_t *conn)
{
conn->compression.supported = 0;
return -1;
}
void compression_free(xmpp_conn_t *conn)
{
UNUSED(conn);
}
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
{
UNUSED(text);
conn->compression.supported = 0;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* conn.c
** strophe XMPP client library -- connection object functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -20,6 +21,7 @@
*/
#include <errno.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
@@ -77,6 +79,7 @@
#define KEEPALIVE_COUNT 3
#endif
static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner);
static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata);
static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn);
static char *_conn_build_stream_tag(xmpp_conn_t *conn,
@@ -140,28 +143,19 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
conn = strophe_alloc(ctx, sizeof(xmpp_conn_t));
if (conn != NULL) {
memset(conn, 0, sizeof(xmpp_conn_t));
conn->ctx = ctx;
conn->type = XMPP_UNKNOWN;
conn->state = XMPP_STATE_DISCONNECTED;
conn->xsock = NULL;
conn->sock = INVALID_SOCKET;
conn->ka_timeout = KEEPALIVE_TIMEOUT;
conn->ka_interval = KEEPALIVE_INTERVAL;
conn->ka_count = KEEPALIVE_COUNT;
conn->tls = NULL;
conn->timeout_stamp = 0;
conn->error = 0;
conn->stream_error = NULL;
/* default send parameters */
conn->blocking_send = 0;
conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
conn->send_queue_len = 0;
conn->send_queue_user_len = 0;
conn->send_queue_head = NULL;
conn->send_queue_tail = NULL;
/* default timeouts */
conn->connect_timeout = CONNECT_TIMEOUT;
@@ -171,49 +165,14 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
strophe_free(conn->ctx, conn);
return NULL;
}
conn->domain = NULL;
conn->jid = NULL;
conn->pass = NULL;
conn->stream_id = NULL;
conn->bound_jid = NULL;
conn->is_raw = 0;
conn->tls_support = 0;
conn->tls_disabled = 0;
conn->tls_mandatory = 0;
conn->tls_legacy_ssl = 0;
conn->tls_trust = 0;
conn->tls_failed = 0;
conn->tls_cafile = NULL;
conn->tls_capath = NULL;
conn->tls_client_cert = NULL;
conn->tls_client_key = NULL;
conn->sasl_support = 0;
conn->auth_legacy_enabled = 0;
conn->secured = 0;
conn->certfail_handler = NULL;
conn->password_callback = NULL;
conn->password_callback_userdata = NULL;
tls_clear_password_cache(conn);
conn->password_retries = 1;
conn->bind_required = 0;
conn->session_required = 0;
conn->sm_state = NULL;
conn->parser =
parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
_handle_stream_stanza, conn);
conn->reset_parser = 0;
conn->authenticated = 0;
conn->conn_handler = NULL;
conn->userdata = NULL;
conn->timed_handlers = NULL;
/* we own (and will free) the hash values */
conn->id_handlers = hash_new(conn->ctx, 32, NULL);
conn->handlers = NULL;
conn->sockopt_cb = NULL;
/* give the caller a reference to connection */
conn->ref = 1;
@@ -798,7 +757,8 @@ int xmpp_connect_component(xmpp_conn_t *conn,
return XMPP_EINVOP;
/* XEP-0114 does not support TLS */
xmpp_conn_disable_tls(conn);
(void)xmpp_conn_set_flags(conn, xmpp_conn_get_flags(conn) |
XMPP_CONN_FLAG_DISABLE_TLS);
if (!conn->tls_disabled) {
strophe_error(conn->ctx, "conn",
"Failed to disable TLS. "
@@ -862,9 +822,10 @@ void conn_established(xmpp_conn_t *conn)
if (conn->is_raw) {
handler_reset_timed(conn, 0);
/* we skip authentication for a "raw" connection, but the event loop
ignores user's handlers when conn->authenticated is not set. */
conn->authenticated = 1;
/* we skip all the mandatory steps of the stream negotiation for a "raw"
connection, but the event loop ignores user's handlers when
conn->stream_negotiation_completed is not set. */
conn->stream_negotiation_completed = 1;
conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
conn->userdata);
} else {
@@ -958,6 +919,7 @@ void conn_disconnect(xmpp_conn_t *conn)
{
strophe_debug(conn->ctx, "xmpp", "Closing socket.");
conn->state = XMPP_STATE_DISCONNECTED;
conn->stream_negotiation_completed = 0;
if (conn->tls) {
tls_stop(conn->tls);
tls_free(conn->tls);
@@ -1026,7 +988,7 @@ void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
{
va_list ap;
if (conn->state != XMPP_STATE_CONNECTED)
if (!_is_connected(conn, XMPP_QUEUE_USER))
return;
va_start(ap, fmt);
@@ -1101,6 +1063,23 @@ void conn_open_stream(xmpp_conn_t *conn)
strophe_free(conn->ctx, from);
}
int conn_interface_write(struct conn_interface *intf,
const void *buff,
size_t len)
{
int ret = intf->write(intf, buff, len);
if (ret < 0 && !intf->error_is_recoverable(intf, intf->get_error(intf))) {
intf->conn->error = intf->get_error(intf);
}
return ret;
}
int conn_int_nop(struct conn_interface *intf)
{
UNUSED(intf);
return 0;
}
int conn_tls_start(xmpp_conn_t *conn)
{
int rc;
@@ -1114,14 +1093,18 @@ int conn_tls_start(xmpp_conn_t *conn)
}
if (conn->tls != NULL) {
struct conn_interface old_intf = conn->intf;
conn->intf = tls_intf;
conn->intf.conn = conn;
if (tls_start(conn->tls)) {
conn->secured = 1;
} else {
rc = XMPP_EINT;
conn->error = tls_error(conn->tls);
conn->error = tls_error(&conn->intf);
tls_free(conn->tls);
conn->tls = NULL;
conn->tls_failed = 1;
conn->intf = old_intf;
}
}
if (rc != 0) {
@@ -1145,11 +1128,15 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
{
long flags;
flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
flags =
XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
return flags;
}
@@ -1163,12 +1150,14 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
*
* Supported flags are:
*
* - XMPP_CONN_FLAG_DISABLE_TLS
* - XMPP_CONN_FLAG_MANDATORY_TLS
* - XMPP_CONN_FLAG_LEGACY_SSL
* - XMPP_CONN_FLAG_TRUST_TLS
* - XMPP_CONN_FLAG_LEGACY_AUTH
* - XMPP_CONN_FLAG_DISABLE_SM
* - \ref XMPP_CONN_FLAG_DISABLE_TLS
* - \ref XMPP_CONN_FLAG_MANDATORY_TLS
* - \ref XMPP_CONN_FLAG_LEGACY_SSL
* - \ref XMPP_CONN_FLAG_TRUST_TLS
* - \ref XMPP_CONN_FLAG_LEGACY_AUTH
* - \ref XMPP_CONN_FLAG_DISABLE_SM
* - \ref XMPP_CONN_FLAG_ENABLE_COMPRESSION
* - \ref XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
*
* @param conn a Strophe connection object
* @param flags ORed connection flags
@@ -1198,29 +1187,23 @@ int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
conn->compression.allowed =
(flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
conn->compression.dont_reset =
(flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
XMPP_CONN_FLAG_ENABLE_COMPRESSION |
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
if (flags) {
strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
return XMPP_EINVOP;
}
return 0;
}
/** Disable TLS for this connection, called by users of the library.
* Occasionally a server will be misconfigured to send the starttls
* feature, but will not support the handshake.
*
* @param conn a Strophe connection object
*
* @note this function is deprecated
* @see xmpp_conn_set_flags()
*
* @ingroup Connections
*/
void xmpp_conn_disable_tls(xmpp_conn_t *conn)
{
long flags = xmpp_conn_get_flags(conn);
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
(void)xmpp_conn_set_flags(conn, flags);
}
/** Return whether TLS session is established or not.
*
* @return TRUE if TLS session is established and FALSE otherwise
@@ -1239,17 +1222,26 @@ int xmpp_conn_is_secured(xmpp_conn_t *conn)
*/
int xmpp_conn_is_connecting(xmpp_conn_t *conn)
{
return conn->state == XMPP_STATE_CONNECTING;
return conn->state == XMPP_STATE_CONNECTING ||
(conn->state == XMPP_STATE_CONNECTED &&
conn->stream_negotiation_completed == 0);
}
static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner)
{
return conn->state == XMPP_STATE_CONNECTED &&
(owner != XMPP_QUEUE_USER ||
conn->stream_negotiation_completed == 1);
}
/**
* @return TRUE if connection is in connected state and FALSE otherwise
* @return TRUE if connection is established and FALSE otherwise
*
* @ingroup Connections
*/
int xmpp_conn_is_connected(xmpp_conn_t *conn)
{
return conn->state == XMPP_STATE_CONNECTED;
return _is_connected(conn, XMPP_QUEUE_USER);
}
/**
@@ -1262,6 +1254,365 @@ int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
return conn->state == XMPP_STATE_DISCONNECTED;
}
/**
* This sets the Stream Management callback function
*
* After setting this API, the library will call the given callback function
* each time when the internal SM state is updated.
*
* This can be used in conjunction with \ref xmpp_conn_restore_sm_state to
* e.g. implement a mechanism that retains an SM state over potential
* application terminations.
*
* @param conn a Strophe connection object
* @param cb a callback function or NULL to disable
* @param ctx a context that will be passed on invocation of the callback
* function
*
* @ingroup Connections
*/
void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
xmpp_sm_callback cb,
void *ctx)
{
conn->sm_callback = cb;
conn->sm_callback_ctx = ctx;
}
struct sm_restore {
xmpp_conn_t *conn;
const unsigned char *state;
const unsigned char *const state_end, *const orig;
};
static int sm_load_u32(struct sm_restore *sm, uint8_t type, uint32_t *val)
{
if (*sm->state != type) {
strophe_error(
sm->conn->ctx, "conn",
"Invalid CBOR type at position %u: 0x%02x, expected: 0x%02x",
sm->state - sm->orig, *sm->state, type);
return XMPP_EINVOP;
}
sm->state++;
if ((sm->state + 4) > sm->state_end) {
strophe_error(sm->conn->ctx, "conn",
"Provided sm_state data is too short");
return XMPP_EINVOP;
}
uint32_t v;
memcpy(&v, sm->state, 4);
sm->state += 4;
*val = ntohl(v);
return 0;
}
static int sm_load_string(struct sm_restore *sm, char **val, size_t *len)
{
uint32_t l;
int ret = sm_load_u32(sm, 0x7a, &l);
if (ret)
return ret;
if ((sm->state + l) > sm->state_end) {
strophe_error(sm->conn->ctx, "conn",
"Provided sm_state data is too short");
return XMPP_EINVOP;
}
*val = strophe_alloc(sm->conn->ctx, l + 1);
if (!*val)
return XMPP_EMEM;
memcpy(*val, sm->state, l);
(*val)[l] = '\0';
sm->state += l;
*len = l;
return 0;
}
/**
* This restores the serialized Stream Management state
*
* After setting this API, the library will call the given callback function
* each time when the internal SM state is updated.
*
* This can be used in conjunction with \ref xmpp_conn_restore_sm_state to
* e.g. implement a mechanism that retains an SM state over potential
* application terminations.
*
* @param conn a Strophe connection object
* @param sm_state a buffer as passed to the SM callback
* @param sm_state_len the length of `sm_state`
*
* @ingroup Connections
*/
int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
const unsigned char *sm_state,
size_t sm_state_len)
{
/* We can only set the SM state when we're disconnected */
if (conn->state != XMPP_STATE_DISCONNECTED) {
strophe_error(conn->ctx, "conn",
"SM state can only be set the when we're disconnected");
return XMPP_EINVOP;
}
if (conn->sm_state) {
strophe_error(conn->ctx, "conn", "SM state is already set!");
return XMPP_EINVOP;
}
if (sm_state_len < 5 * 6) {
strophe_error(conn->ctx, "conn", "Provided sm_state data is too short");
return XMPP_EINVOP;
}
struct sm_restore sm = {.conn = conn,
.state = sm_state,
.state_end = sm_state + sm_state_len,
.orig = sm_state};
/* Check for pointer wrap-around, which should never happen */
if (sm.state_end < sm.state) {
strophe_error(conn->ctx, "conn",
"Internal error, pointer wrapped around");
return XMPP_EINVOP;
}
if (memcmp(sm.state, "\x1a\x00\x00\x00\x00", 5) != 0) {
strophe_error(conn->ctx, "conn", "Unknown sm_state version");
return XMPP_EINVOP;
}
sm.state += 5;
conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
if (!conn->sm_state)
return XMPP_EMEM;
memset(conn->sm_state, 0, sizeof(*conn->sm_state));
conn->sm_state->ctx = conn->ctx;
conn->sm_state->sm_support = 1;
conn->sm_state->sm_enabled = 1;
conn->sm_state->can_resume = 1;
conn->sm_state->resume = 1;
int ret;
ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_sent_nr);
if (ret)
goto err_reload;
ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_handled_nr);
if (ret)
goto err_reload;
size_t id_len;
ret = sm_load_string(&sm, &conn->sm_state->id, &id_len);
if (ret)
goto err_reload;
uint32_t len, i;
ret = sm_load_u32(&sm, 0x9a, &len);
if (ret)
goto err_reload;
conn->send_queue_user_len = conn->send_queue_len = len;
for (i = 0; i < len; i++) {
xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
if (!item) {
ret = XMPP_EMEM;
goto err_reload;
}
memset(item, 0, sizeof(*item));
if (!conn->send_queue_tail) {
conn->send_queue_head = item;
conn->send_queue_tail = item;
} else {
conn->send_queue_tail->next = item;
conn->send_queue_tail = item;
}
ret = sm_load_string(&sm, &item->data, &item->len);
if (ret)
goto err_reload;
item->owner = XMPP_QUEUE_USER;
}
ret = sm_load_u32(&sm, 0xba, &len);
if (ret)
goto err_reload;
for (i = 0; i < len; i++) {
xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
if (!item) {
ret = XMPP_EMEM;
goto err_reload;
}
memset(item, 0, sizeof(*item));
add_queue_back(&conn->sm_state->sm_queue, item);
ret = sm_load_u32(&sm, 0x1a, &item->sm_h);
if (ret)
goto err_reload;
ret = sm_load_string(&sm, &item->data, &item->len);
if (ret)
goto err_reload;
item->owner = XMPP_QUEUE_USER;
}
return XMPP_EOK;
err_reload:
xmpp_free_sm_state(conn->sm_state);
return ret;
}
static int sm_store_u32(unsigned char **next_,
const unsigned char *const end,
uint8_t type,
uint32_t val)
{
unsigned char *next = *next_;
if (next + 5 > end)
return 1;
*next++ = type;
uint32_t v = htonl(val);
memcpy(next, &v, 4);
next += 4;
*next_ = next;
return 0;
}
static size_t sm_state_serialize(xmpp_conn_t *conn, unsigned char **buf)
{
if (!conn->sm_state->sm_support || !conn->sm_state->sm_enabled ||
!conn->sm_state->can_resume) {
*buf = NULL;
return 0;
}
uint32_t id_len = strlen(conn->sm_state->id);
xmpp_send_queue_t *peek = conn->sm_state->sm_queue.head;
size_t sm_queue_len = 0;
size_t sm_queue_size = 0;
while (peek) {
sm_queue_len++;
sm_queue_size += 10 + peek->len;
peek = peek->next;
}
uint32_t send_queue_len = 0;
size_t send_queue_size = 0;
peek = conn->send_queue_head;
while (peek) {
send_queue_len++;
send_queue_size += 5 + peek->len;
peek = peek->next;
}
size_t buf_size =
5 + 5 + 5 + 5 + id_len + 5 + send_queue_size + 5 + sm_queue_size;
*buf = strophe_alloc(conn->ctx, buf_size);
if (*buf == NULL)
return 0;
unsigned char *next = *buf;
const unsigned char *const end = next + buf_size;
/* Check for pointer wrap-around, which should never happen */
if (end < next) {
strophe_error(conn->ctx, "conn",
"Internal error, pointer wrapped around");
return 0;
}
memcpy(next, "\x1a\x00\x00\x00\x00", 5); // Version
next += 5;
if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_sent_nr))
goto err_serialize;
if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_handled_nr))
goto err_serialize;
if (sm_store_u32(&next, end, 0x7a, id_len))
goto err_serialize;
memcpy(next, conn->sm_state->id, id_len);
next += id_len;
if (sm_store_u32(&next, end, 0x9a, send_queue_len))
goto err_serialize;
peek = conn->send_queue_head;
while (peek) {
if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
goto err_serialize;
if (next + peek->len > end)
goto err_serialize;
memcpy(next, peek->data, peek->len);
next += peek->len;
peek = peek->next;
}
if (sm_store_u32(&next, end, 0xba, sm_queue_len))
goto err_serialize;
peek = conn->sm_state->sm_queue.head;
while (peek) {
if (sm_store_u32(&next, end, 0x1a, peek->sm_h))
goto err_serialize;
if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
goto err_serialize;
if (next + peek->len > end)
goto err_serialize;
memcpy(next, peek->data, peek->len);
next += peek->len;
peek = peek->next;
}
return buf_size;
err_serialize:
strophe_error(conn->ctx, "conn", "Can't serialize more data, buffer full");
strophe_free(conn->ctx, buf);
return 0;
}
void trigger_sm_callback(xmpp_conn_t *conn)
{
if (!conn || !conn->sm_callback)
return;
unsigned char *buf;
size_t size = sm_state_serialize(conn, &buf);
conn->sm_callback(conn, conn->sm_callback_ctx, buf, size);
strophe_free(conn->ctx, buf);
}
static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
{
xmpp_sm_state_t *s = conn->sm_state;
if (s->previd) {
strophe_free(conn->ctx, s->previd);
s->previd = NULL;
}
if (s->can_resume) {
s->previd = s->id;
s->id = NULL;
s->bound_jid = conn->bound_jid;
conn->bound_jid = NULL;
} else if (s->id) {
strophe_free(conn->ctx, s->id);
s->id = NULL;
}
s->r_sent = s->sm_enabled = s->sm_support = s->resume = 0;
if (s->bind) {
xmpp_stanza_release(s->bind);
s->bind = NULL;
}
}
/**
* This returns the Stream Management state of a connection object after
* it has been disconnected.
@@ -1293,34 +1644,6 @@ xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
return ret;
}
static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
{
xmpp_sm_state_t *s = conn->sm_state;
if (s->previd) {
strophe_free(conn->ctx, s->previd);
s->previd = NULL;
}
if (s->can_resume) {
s->previd = s->id;
s->id = NULL;
s->bound_jid = conn->bound_jid;
conn->bound_jid = NULL;
} else if (s->id) {
strophe_free(conn->ctx, s->id);
s->id = NULL;
}
s->sm_enabled = s->sm_support = s->resume = 0;
if (s->bind) {
xmpp_stanza_release(s->bind);
s->bind = NULL;
}
}
/**
* @param conn a Strophe connection object
* @param sm_state A Stream Management state returned from a call to
@@ -1333,8 +1656,11 @@ static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
{
/* We can only set the SM state when we're disconnected */
if (conn->state != XMPP_STATE_DISCONNECTED)
if (conn->state != XMPP_STATE_DISCONNECTED) {
strophe_error(conn->ctx, "conn",
"SM state can only be set the when we're disconnected");
return XMPP_EINVOP;
}
if (conn->sm_state) {
strophe_error(conn->ctx, "conn", "SM state is already set!");
@@ -1353,6 +1679,21 @@ int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
return XMPP_EOK;
}
void reset_sm_state(xmpp_sm_state_t *sm_state)
{
xmpp_ctx_t *ctx = sm_state->ctx;
strophe_free_and_null(ctx, sm_state->id);
strophe_free_and_null(ctx, sm_state->previd);
strophe_free_and_null(ctx, sm_state->bound_jid);
if (sm_state->bind)
xmpp_stanza_release(sm_state->bind);
sm_state->bind = NULL;
sm_state->sm_handled_nr = 0;
sm_state->sm_sent_nr = 0;
sm_state->r_sent = 0;
}
/** c.f. \ref xmpp_conn_get_sm_state for usage documentation
*
* @param sm_state A Stream Management state returned from a call to
@@ -1362,8 +1703,8 @@ int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
*/
void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
{
xmpp_ctx_t *ctx;
xmpp_send_queue_t *smq;
xmpp_ctx_t *ctx;
if (!sm_state || !sm_state->ctx)
return;
@@ -1374,15 +1715,7 @@ void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
strophe_free(ctx, queue_element_free(ctx, smq));
}
if (sm_state->bind)
xmpp_stanza_release(sm_state->bind);
if (sm_state->id)
strophe_free(ctx, sm_state->id);
if (sm_state->previd)
strophe_free(ctx, sm_state->previd);
if (sm_state->bound_jid)
strophe_free(ctx, sm_state->bound_jid);
memset(sm_state, 0, sizeof(*sm_state));
reset_sm_state(sm_state);
strophe_free(ctx, sm_state);
}
@@ -1436,6 +1769,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
xmpp_queue_element_t which)
{
xmpp_send_queue_t *t;
int disconnected = conn->state == XMPP_STATE_DISCONNECTED;
/* Fast return paths */
/* empty queue */
@@ -1444,7 +1778,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
/* one element in queue */
if (conn->send_queue_head == conn->send_queue_tail) {
/* head is already sent out partially */
if (conn->send_queue_head->wip)
if (conn->send_queue_head->wip && !disconnected)
return NULL;
/* the element is no USER element */
if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
@@ -1468,7 +1802,7 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
return NULL;
/* 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;
/* search forward to find the first USER element */
@@ -1482,10 +1816,15 @@ char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
/* In case there exists a SM stanza that is linked to the
* 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));
/* reset the flag, so we restart to send `<r>` stanzas */
conn->sm_state->r_sent = 0;
}
/* Finally drop the element */
return _drop_send_queue_element(conn, t);
char *r = _drop_send_queue_element(conn, t);
trigger_sm_callback(conn);
return r;
}
/* timed handler for cleanup if normal disconnect procedure takes too long */
@@ -1679,6 +2018,7 @@ static void _handle_stream_end(char *name, void *userdata)
strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
/* the session has been terminated properly, i.e. it can't be resumed */
conn->sm_state->can_resume = 0;
trigger_sm_callback(conn);
conn_disconnect_clean(conn);
}
@@ -1746,12 +2086,15 @@ static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
while (conn->sm_state->sm_queue.head &&
conn->sm_state->sm_queue.head->sm_h < ul_h) {
e = pop_queue_front(&conn->sm_state->sm_queue);
strophe_debug_verbose(2, conn->ctx, "conn", "SM_Q_DROP: %p", e);
strophe_debug_verbose(2, conn->ctx, "conn",
"SM_Q_DROP: %p, h=%lu", e, e->sm_h);
c = queue_element_free(conn->ctx, e);
strophe_free(conn->ctx, c);
}
conn->sm_state->r_sent = 0;
}
}
trigger_sm_callback(conn);
}
static unsigned short _conn_default_port(xmpp_conn_t *conn,
@@ -1788,6 +2131,11 @@ static void _conn_reset(xmpp_conn_t *conn)
return;
}
compression_free(conn);
conn->intf = sock_intf;
conn->intf.conn = conn;
/* free queued */
sq = conn->send_queue_head;
while (sq) {
@@ -1802,22 +2150,14 @@ static void _conn_reset(xmpp_conn_t *conn)
if (conn->stream_error) {
xmpp_stanza_release(conn->stream_error->stanza);
if (conn->stream_error->text)
strophe_free(ctx, conn->stream_error->text);
strophe_free(ctx, conn->stream_error);
conn->stream_error = NULL;
strophe_free_and_null(ctx, conn->stream_error->text);
strophe_free_and_null(ctx, conn->stream_error);
}
if (conn->domain)
strophe_free(ctx, conn->domain);
if (conn->bound_jid)
strophe_free(ctx, conn->bound_jid);
if (conn->stream_id)
strophe_free(ctx, conn->stream_id);
conn->domain = NULL;
conn->bound_jid = NULL;
conn->stream_id = NULL;
conn->authenticated = 0;
strophe_free_and_null(ctx, conn->domain);
strophe_free_and_null(ctx, conn->bound_jid);
strophe_free_and_null(ctx, conn->stream_id);
conn->stream_negotiation_completed = 0;
conn->secured = 0;
conn->tls_failed = 0;
conn->error = 0;
@@ -1904,7 +2244,7 @@ static void _send_valist(xmpp_conn_t *conn,
char buf[1024]; /* small buffer for common case */
char *bigbuf;
if (conn->state != XMPP_STATE_CONNECTED)
if (!_is_connected(conn, owner))
return;
va_copy(apdup, ap);
@@ -1952,7 +2292,7 @@ void send_stanza(xmpp_conn_t *conn,
char *buf = NULL;
size_t len;
if (conn->state != XMPP_STATE_CONNECTED)
if (!_is_connected(conn, owner))
goto out;
if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
@@ -1979,6 +2319,11 @@ void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)
}
}
xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue)
{
return queue->head;
}
xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue)
{
xmpp_send_queue_t *ret = queue->head;
@@ -2034,8 +2379,12 @@ static int _send_raw(xmpp_conn_t *conn,
conn->send_queue_user_len++;
strophe_debug_verbose(3, conn->ctx, "conn", "QUEUED: %s", data);
strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled) {
if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled &&
!conn->sm_state->r_sent) {
conn->sm_state->r_sent = 1;
send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
} else {
trigger_sm_callback(conn);
}
return XMPP_EOK;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* crypto.c
* strophe XMPP client library -- public interface for digests, encodings
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* ctx.c
** strophe XMPP client library -- run-time context implementation
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -464,6 +465,8 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
if (ctx != NULL) {
memset(ctx, 0, sizeof(xmpp_ctx_t));
if (mem != NULL)
ctx->mem = mem;
else
@@ -474,12 +477,9 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
else
ctx->log = log;
ctx->connlist = NULL;
ctx->timed_handlers = NULL;
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
ctx->rand = xmpp_rand_new(ctx);
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
ctx->verbosity = 0;
if (ctx->rand == NULL) {
strophe_free(ctx, ctx);
ctx = NULL;

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* deprecated.c
** strophe XMPP client library -- File with deprecated API functions.
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -254,3 +255,22 @@ void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
conn->ka_count = 0;
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
}
/** Disable TLS for this connection, called by users of the library.
* Occasionally a server will be misconfigured to send the starttls
* feature, but will not support the handshake.
*
* @param conn a Strophe connection object
*
* @note this function is deprecated
* @see xmpp_conn_set_flags()
*
* @ingroup Deprecated
*/
void xmpp_conn_disable_tls(xmpp_conn_t *conn)
{
long flags = xmpp_conn_get_flags(conn);
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
(void)xmpp_conn_set_flags(conn, flags);
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* event.c
** strophe XMPP client library -- event loop and management
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -38,7 +39,7 @@
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#define _sleep(x) usleep((x)*1000)
#define _sleep(x) usleep((x) * 1000)
#else
#include <winsock2.h>
#ifndef ETIMEDOUT
@@ -57,8 +58,10 @@
#include "common.h"
#include "parser.h"
#ifndef STROPHE_MESSAGE_BUFFER_SIZE
/** Max buffer size for receiving messages. */
#define STROPE_MESSAGE_BUFFER_SIZE 4096
#define STROPHE_MESSAGE_BUFFER_SIZE 4096
#endif
static int _connect_next(xmpp_conn_t *conn)
{
@@ -89,13 +92,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
{
xmpp_connlist_t *connitem;
xmpp_conn_t *conn;
struct conn_interface *intf;
fd_set rfds, wfds;
sock_t max = 0;
int ret;
struct timeval tv;
xmpp_send_queue_t *sq, *tsq;
int towrite;
char buf[STROPE_MESSAGE_BUFFER_SIZE];
char buf[STROPHE_MESSAGE_BUFFER_SIZE];
uint64_t next;
uint64_t usec;
int tls_read_bytes = 0;
@@ -111,16 +115,18 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
connitem = connitem->next;
continue;
}
intf = &conn->intf;
/* if we're running tls, there may be some remaining data waiting to
* be sent, so push that out */
if (conn->tls) {
ret = tls_clear_pending_write(conn->tls);
ret = tls_clear_pending_write(intf);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
/* an error occurred */
strophe_debug(ctx, "xmpp",
"Send error occurred, disconnecting.");
strophe_debug(
ctx, "xmpp",
"Send error of pending data occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
goto next_item;
@@ -132,15 +138,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
while (sq) {
towrite = sq->len - sq->written;
if (conn->tls) {
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls)))
conn->error = tls_error(conn->tls);
} else {
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
if (ret < 0 && !sock_is_recoverable(sock_error()))
conn->error = sock_error();
}
ret = conn_interface_write(intf, &sq->data[sq->written], towrite);
if (ret > 0 && ret < towrite)
sq->written += ret; /* not all data could be sent now */
sq->wip = 1;
@@ -158,7 +156,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
if (!(tsq->owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled) {
tsq->sm_h = conn->sm_state->sm_sent_nr;
conn->sm_state->sm_sent_nr++;
strophe_debug_verbose(1, ctx, "xmpp", "SM_Q_MOVE: %p", tsq);
strophe_debug_verbose(1, ctx, "xmpp", "SM_Q_MOVE: %p, h=%lu",
tsq, tsq->sm_h);
add_queue_back(&conn->sm_state->sm_queue, tsq);
tsq = NULL;
}
@@ -175,7 +174,9 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
/* if we've sent everything update the tail */
if (!sq)
conn->send_queue_tail = NULL;
trigger_sm_callback(conn);
}
intf->flush(intf);
/* tear down connection on error */
if (conn->error) {
@@ -210,6 +211,7 @@ next_item:
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
@@ -244,7 +246,7 @@ next_item:
/* Check if there is something in the SSL buffer. */
if (conn->tls)
tls_read_bytes += tls_pending(conn->tls);
tls_read_bytes += tls_pending(intf);
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
max = conn->sock;
@@ -263,9 +265,9 @@ next_item:
/* select errored */
if (ret < 0) {
if (!sock_is_recoverable(sock_error()))
if (!sock_is_recoverable(NULL, sock_error(NULL)))
strophe_error(ctx, "xmpp", "event watcher internal error %d",
sock_error());
sock_error(NULL));
return;
}
@@ -277,6 +279,7 @@ next_item:
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
@@ -304,14 +307,9 @@ next_item:
break;
case XMPP_STATE_CONNECTED:
if (FD_ISSET(conn->sock, &rfds) ||
(conn->tls && tls_pending(conn->tls))) {
if (conn->tls) {
ret = tls_read(conn->tls, buf, STROPE_MESSAGE_BUFFER_SIZE);
} else {
ret =
sock_read(conn->sock, buf, STROPE_MESSAGE_BUFFER_SIZE);
}
if (FD_ISSET(conn->sock, &rfds) || intf->pending(intf)) {
ret = intf->read(intf, buf, STROPHE_MESSAGE_BUFFER_SIZE);
if (ret > 0) {
ret = parser_feed(conn->parser, buf, ret);
@@ -321,15 +319,13 @@ next_item:
"parse error");
}
} else {
if (conn->tls) {
if (!tls_is_recoverable(tls_error(conn->tls))) {
strophe_debug(ctx, "xmpp",
"Unrecoverable TLS error, %d.",
tls_error(conn->tls));
conn->error = tls_error(conn->tls);
conn_disconnect(conn);
}
} else {
int err = intf->get_error(intf);
if (!intf->error_is_recoverable(intf, err)) {
strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
err);
conn->error = err;
conn_disconnect(conn);
} else if (!conn->tls) {
/* return of 0 means socket closed by server */
strophe_debug(ctx, "xmpp",
"Socket closed by remote host.");

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* handler.c
** strophe XMPP client library -- event handler management
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -24,8 +25,6 @@
#include "common.h"
#include "ostypes.h"
typedef int (*xmpp_void_handler)();
/* Remove item from the list pointed by head, but don't free it.
* There can be a situation when user's handler deletes another handler which
* is the previous in the list. handler_fire_stanza() and handler_fire_timed()
@@ -80,9 +79,9 @@ void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
item = head;
while (item) {
/* don't fire user handlers until authentication succeeds and
/* don't fire user handlers until stream negotiation has completed
and skip newly added handlers */
if ((item->user_handler && !conn->authenticated) ||
if ((item->user_handler && !conn->stream_negotiation_completed) ||
!item->enabled) {
item = item->next;
continue;
@@ -116,9 +115,10 @@ void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
item = conn->handlers;
while (item) {
/* don't fire user handlers until authentication succeeds and
/* don't fire user handlers until stream negotiation has completed and
skip newly added handlers */
if ((item->user_handler && !conn->authenticated) || !item->enabled) {
if ((item->user_handler && !conn->stream_negotiation_completed) ||
!item->enabled) {
item = item->next;
continue;
}
@@ -174,9 +174,9 @@ uint64_t handler_fire_timed(xmpp_ctx_t *ctx)
item = conn->timed_handlers;
while (item) {
/* don't fire user handlers until authentication succeeds and
skip newly added handlers */
if ((item->user_handler && !conn->authenticated) ||
/* don't fire user handlers until stream negotiation has completed
and skip newly added handlers */
if ((item->user_handler && !conn->stream_negotiation_completed) ||
!item->enabled) {
item = item->next;
continue;

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* hash.c
** strophe XMPP client library -- hash table implementation
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* hash.h
** strophe XMPP client library -- hash table interface
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* jid.c
** strophe XMPP client library -- helper functions for parsing JIDs
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -37,7 +37,7 @@
#define PUT_32BIT_LSB_FIRST(cp, value) \
do { \
(cp)[0] = (value)&0xFF; \
(cp)[0] = (value) & 0xFF; \
(cp)[1] = ((value) >> 8) & 0xFF; \
(cp)[2] = ((value) >> 16) & 0xFF; \
(cp)[3] = ((value) >> 24) & 0xFF; \

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* ostypes.h
** strophe XMPP client library -- type definitions for platforms
** without stdint.h
@@ -7,7 +8,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.h
** strophe XMPP client library -- parser structures and functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.c
** strophe XMPP client library -- xml parser handlers and utility functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.c
** strophe XMPP client library -- xml parser handlers and utility functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* rand.c
* strophe XMPP client library -- pseudo-random number generator
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -67,9 +68,9 @@ struct _xmpp_rand_t {
};
/* returns smallest number mupliple of y that not less than x */
#define round_up(x, y) (((x) + (y)-1) / (y) * (y))
#define round_up(x, y) (((x) + (y) - 1) / (y) * (y))
/* returns smallest integer number that not less than x/y */
#define div_round_up(x, y) (((x) + (y)-1) / (y))
#define div_round_up(x, y) (((x) + (y) - 1) / (y))
/* adds two arrays as numbers in big-endian representation and stores
* result in the first one.

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* resolver.c
* strophe XMPP client library -- DNS resolver
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -629,7 +630,7 @@ static void netbuf_add_16bitnum(unsigned char *buf,
/* assuming big endian */
*p++ = (num >> 8) & 0xff;
*p++ = (num)&0xff;
*p++ = (num) & 0xff;
*offset += 2;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* resolver.h
* strophe XMPP client library -- DNS resolver
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sasl.c
** strophe XMPP client library -- SASL authentication helpers
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -374,6 +375,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
/** generate auth response string for the SASL SCRAM mechanism */
char *sasl_scram(xmpp_ctx_t *ctx,
const struct hash_alg *alg,
const char *channel_binding,
const char *challenge,
const char *first_bare,
const char *jid,
@@ -397,6 +399,7 @@ char *sasl_scram(xmpp_ctx_t *ctx,
char *result = NULL;
size_t response_len;
size_t auth_len;
int l;
UNUSED(jid);
@@ -427,37 +430,44 @@ char *sasl_scram(xmpp_ctx_t *ctx,
}
ival = strtol(i, &saveptr, 10);
auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
auth = strophe_alloc(ctx, auth_len);
if (!auth) {
/* "c=<channel_binding>," + r + ",p=" + sign_b64 + '\0' */
response_len = 3 + strlen(channel_binding) + strlen(r) + 3 +
((alg->digest_size + 2) / 3 * 4) + 1;
response = strophe_alloc(ctx, response_len);
if (!response) {
goto out_sval;
}
/* "c=biws," + r + ",p=" + sign_b64 + '\0' */
response_len = 7 + strlen(r) + 3 + ((alg->digest_size + 2) / 3 * 4) + 1;
response = strophe_alloc(ctx, response_len);
if (!response) {
goto out_auth;
auth_len = 3 + response_len + strlen(first_bare) + strlen(challenge);
auth = strophe_alloc(ctx, auth_len);
if (!auth) {
goto out_response;
}
strophe_snprintf(response, response_len, "c=biws,%s", r);
strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge,
response);
l = strophe_snprintf(response, response_len, "c=%s,%s", channel_binding, r);
if (l < 0 || (size_t)l >= response_len) {
goto out_auth;
}
l = strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare, challenge,
response);
if (l < 0 || (size_t)l >= auth_len) {
goto out_auth;
}
SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval,
sval_len, (uint32_t)ival, key);
SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign);
SCRAM_ClientProof(alg, sign, key, sign);
SCRAM_ClientProof(alg, key, sign, sign);
sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size);
if (!sign_b64) {
goto out_response;
goto out_auth;
}
/* Check for buffer overflow */
if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
strophe_free(ctx, sign_b64);
goto out_response;
goto out_auth;
}
strcat(response, ",p=");
strcat(response, sign_b64);
@@ -466,14 +476,14 @@ char *sasl_scram(xmpp_ctx_t *ctx,
response_b64 =
xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response));
if (!response_b64) {
goto out_response;
goto out_auth;
}
result = response_b64;
out_response:
strophe_free(ctx, response);
out_auth:
strophe_free(ctx, auth);
out_response:
strophe_free(ctx, response);
out_sval:
strophe_free(ctx, sval);
out:

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sasl.h
** strophe XMPP client library -- SASL authentication helpers
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -28,6 +29,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
const char *password);
char *sasl_scram(xmpp_ctx_t *ctx,
const struct hash_alg *alg,
const char *channel_binding,
const char *challenge,
const char *first_bare,
const char *jid,

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* scram.c
* strophe XMPP client library
*
@@ -9,7 +10,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -41,6 +42,15 @@ const struct hash_alg scram_sha1 = {
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
const struct hash_alg scram_sha1_plus = {
"SCRAM-SHA-1-PLUS",
SASL_MASK_SCRAMSHA1_PLUS,
SHA1_DIGEST_SIZE,
(void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
(void (*)(void *))crypto_SHA1_Init,
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
const struct hash_alg scram_sha256 = {
"SCRAM-SHA-256",
SASL_MASK_SCRAMSHA256,
@@ -50,6 +60,15 @@ const struct hash_alg scram_sha256 = {
(void (*)(void *, const uint8_t *, size_t))sha256_process,
(void (*)(void *, uint8_t *))sha256_done};
const struct hash_alg scram_sha256_plus = {
"SCRAM-SHA-256-PLUS",
SASL_MASK_SCRAMSHA256_PLUS,
SHA256_DIGEST_SIZE,
(void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
(void (*)(void *))sha256_init,
(void (*)(void *, const uint8_t *, size_t))sha256_process,
(void (*)(void *, uint8_t *))sha256_done};
const struct hash_alg scram_sha512 = {
"SCRAM-SHA-512",
SASL_MASK_SCRAMSHA512,
@@ -59,6 +78,43 @@ const struct hash_alg scram_sha512 = {
(void (*)(void *, const uint8_t *, size_t))sha512_process,
(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
* tried if the server supports them.
* Their order is derived from
* https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
*/
const struct hash_alg *scram_algs[] = {
/* *1 */
&scram_sha512_plus,
/* *1 */
&scram_sha256_plus,
/* *1 */
&scram_sha1_plus,
/* *1 */
&scram_sha512,
/* *1 */
&scram_sha256,
/* *1 */
&scram_sha1,
};
/* *1 - I want to use clang-format here, but by default it will put multiple
* elements per line if there's no comment. Currently clang-format also doesn't
* have an option to enforce it to behave like that, besides by putting comments
* between each element (or I couldn't find a way to do it). In order to prevent
* clang-format from re-formatting the array I added the comments above and
* wrote this lengthy description.
*/
const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]);
union common_hash_ctx {
SHA1_CTX sha1;
sha256_context sha256;

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* scram.h
* strophe XMPP client library -- SCRAM helper functions
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -33,9 +34,8 @@ struct hash_alg {
void (*final)(void *, uint8_t *);
};
extern const struct hash_alg scram_sha1;
extern const struct hash_alg scram_sha256;
extern const struct hash_alg scram_sha512;
extern const struct hash_alg *scram_algs[];
extern const size_t scram_algs_num;
void SCRAM_ClientKey(const struct hash_alg *alg,
const uint8_t *password,

View File

@@ -53,7 +53,7 @@ extern "C" {
(y)[0] = (uint8_t)(((x) >> 24) & 255); \
(y)[1] = (uint8_t)(((x) >> 16) & 255); \
(y)[2] = (uint8_t)(((x) >> 8) & 255); \
(y)[3] = (uint8_t)((x)&255); \
(y)[3] = (uint8_t)((x) & 255); \
} while (0)
#define LOAD32H(x, y) \
@@ -72,7 +72,7 @@ extern "C" {
(y)[4] = (uint8_t)(((x) >> 24) & 255); \
(y)[5] = (uint8_t)(((x) >> 16) & 255); \
(y)[6] = (uint8_t)(((x) >> 8) & 255); \
(y)[7] = (uint8_t)((x)&255); \
(y)[7] = (uint8_t)((x) & 255); \
} while (0)
#define LOAD64H(x, y) \
@@ -96,13 +96,13 @@ extern "C" {
#define CONST64(n) n##ULL
#endif
#define RORc(x, y) \
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | \
((uint32_t)(x) << (uint32_t)((32 - ((y)&31)) & 31))) & \
#define RORc(x, y) \
(((((uint32_t)(x) & 0xFFFFFFFFUL) >> (uint32_t)((y) & 31)) | \
((uint32_t)(x) << (uint32_t)((32 - ((y) & 31)) & 31))) & \
0xFFFFFFFFUL)
#define ROR64c(x, y) \
(((((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y)&CONST64(63))) | \
((x) << (((uint64_t)64 - ((y)&63)) & 63))) & \
#define ROR64c(x, y) \
(((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y) & CONST64(63))) | \
((x) << (((uint64_t)64 - ((y) & 63)) & 63))) & \
CONST64(0xFFFFFFFFFFFFFFFF))
#ifdef __cplusplus

View File

@@ -9,7 +9,7 @@
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
#define Maj(x, y, z) (((x | y) & z) | (x & y))
#define S(x, n) RORc((x), (n))
#define R(x, n) (((x)&0xFFFFFFFFUL) >> (n))
#define R(x, n) (((x) & 0xFFFFFFFFUL) >> (n))
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))

View File

@@ -52,7 +52,7 @@ static const uint64_t K[80] = {
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
#define Maj(x, y, z) (((x | y) & z) | (x & y))
#define S(x, n) ROR64c(x, n)
#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sock.c
** strophe XMPP client library -- socket abstraction implementation
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -37,6 +38,18 @@
#include "common.h"
#include "resolver.h"
const struct conn_interface sock_intf = {
sock_read,
sock_write,
/* no flush */
conn_int_nop,
/* no pending */
conn_int_nop,
sock_error,
sock_is_recoverable,
NULL,
};
struct _xmpp_sock_t {
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
@@ -63,8 +76,9 @@ void sock_shutdown(void)
#endif
}
int sock_error(void)
int sock_error(struct conn_interface *intf)
{
UNUSED(intf);
#ifdef _WIN32
return WSAGetLastError();
#else
@@ -109,7 +123,8 @@ static void sock_getaddrinfo(xmpp_sock_t *xsock)
rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
&xsock->ainfo_list);
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);
xsock->ainfo_list = NULL;
}
@@ -193,7 +208,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
{
struct addrinfo *ainfo;
sock_t sock;
int rc = 0;
int rc;
char buf[64];
do {
@@ -214,6 +229,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock != INVALID_SOCKET) {
rc = 0;
if (xsock->conn->sockopt_cb) {
/* Don't allow user to overwrite sockfd value. */
sock_t sock_copy = sock;
@@ -221,7 +237,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
if (rc != 0) {
strophe_debug(xsock->ctx, "sock",
"User's setsockopt callback"
"failed with %d (errno=%d)",
" failed with %d (errno=%d)",
rc, errno);
}
}
@@ -230,7 +246,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
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())) {
if (rc != 0 && !_in_progress(sock_error(NULL))) {
sock_close(sock);
sock = INVALID_SOCKET;
}
@@ -375,18 +391,19 @@ int sock_set_nonblocking(sock_t sock)
return _sock_set_blocking_mode(sock, 0);
}
int sock_read(sock_t sock, void *buff, size_t len)
int sock_read(struct conn_interface *intf, void *buff, size_t len)
{
return recv(sock, buff, len, 0);
return recv(intf->conn->sock, buff, len, 0);
}
int sock_write(sock_t sock, const void *buff, size_t len)
int sock_write(struct conn_interface *intf, const void *buff, size_t len)
{
return send(sock, buff, len, 0);
return send(intf->conn->sock, buff, len, 0);
}
int sock_is_recoverable(int error)
int sock_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
#ifdef _WIN32
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
error == WSAEINPROGRESS);
@@ -415,15 +432,15 @@ int sock_connect_error(sock_t sock)
/* it's possible that the error wasn't ENOTCONN, so if it wasn't,
* return that */
#ifdef _WIN32
if (sock_error() != WSAENOTCONN)
return sock_error();
if (sock_error(NULL) != WSAENOTCONN)
return sock_error(NULL);
#else
if (sock_error() != ENOTCONN)
return sock_error();
if (sock_error(NULL) != ENOTCONN)
return sock_error(NULL);
#endif
/* load the correct error into errno through error slippage */
recv(sock, &temp, 1, 0);
return sock_error();
return sock_error(NULL);
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sock.h
** strophe XMPP client library -- socket abstraction header
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -29,12 +30,14 @@ typedef int sock_t;
typedef SOCKET sock_t;
#endif
extern const struct conn_interface sock_intf;
typedef struct _xmpp_sock_t xmpp_sock_t;
void sock_initialize(void);
void sock_shutdown(void);
int sock_error(void);
int sock_error(struct conn_interface *intf);
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain,
@@ -46,9 +49,9 @@ int sock_close(sock_t sock);
int sock_set_blocking(sock_t sock);
int sock_set_nonblocking(sock_t sock);
int sock_read(sock_t sock, void *buff, size_t len);
int sock_write(sock_t sock, const void *buff, size_t len);
int sock_is_recoverable(int error);
int sock_read(struct conn_interface *intf, void *buff, size_t len);
int sock_write(struct conn_interface *intf, const void *buff, size_t len);
int sock_is_recoverable(struct conn_interface *intf, int error);
/* checks for an error after connect, return 0 if connect successful */
int sock_connect_error(sock_t sock);
int sock_set_keepalive(sock_t sock,

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* stanza.c
** strophe XMPP client library -- XMPP stanza object and utilities
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -41,15 +42,10 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
stanza = strophe_alloc(ctx, sizeof(xmpp_stanza_t));
if (stanza != NULL) {
memset(stanza, 0, sizeof(xmpp_stanza_t));
stanza->ref = 1;
stanza->ctx = ctx;
stanza->type = XMPP_STANZA_UNKNOWN;
stanza->prev = NULL;
stanza->next = NULL;
stanza->children = NULL;
stanza->parent = NULL;
stanza->data = NULL;
stanza->attributes = NULL;
}
return stanza;

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.c
** strophe XMPP client library -- generic TLS functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -29,6 +30,17 @@
#include "common.h"
const struct conn_interface tls_intf = {
tls_read,
tls_write,
tls_clear_pending_write,
tls_pending,
tls_error,
tls_is_recoverable,
/* init conn */
NULL,
};
struct _dnsname_t {
char **data;
size_t cur, max;

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.h
** strophe XMPP client library -- TLS abstraction header
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -44,20 +45,24 @@ unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn);
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn);
int tls_set_credentials(tls_t *tls, const char *cafilename);
int tls_init_channel_binding(tls_t *tls,
const char **binding_prefix,
size_t *binding_prefix_len);
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size);
int tls_start(tls_t *tls);
int tls_stop(tls_t *tls);
int tls_error(tls_t *tls);
int tls_pending(struct conn_interface *intf);
int tls_read(struct conn_interface *intf, void *buff, size_t len);
int tls_write(struct conn_interface *intf, const void *buff, size_t len);
int tls_clear_pending_write(struct conn_interface *intf);
int tls_pending(tls_t *tls);
int tls_read(tls_t *tls, void *buff, size_t len);
int tls_write(tls_t *tls, const void *buff, size_t len);
int tls_clear_pending_write(tls_t *tls);
int tls_is_recoverable(int error);
int tls_error(struct conn_interface *intf);
int tls_is_recoverable(struct conn_interface *intf, int error);
/* provided by tls.c */
extern const struct conn_interface tls_intf;
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls_dummy.c
** strophe XMPP client library -- TLS abstraction dummy impl.
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -74,6 +75,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
return -1;
}
int tls_init_channel_binding(tls_t *tls,
const char **binding_prefix,
size_t *binding_prefix_len)
{
UNUSED(tls);
UNUSED(binding_prefix);
UNUSED(binding_prefix_len);
return -1;
}
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
{
UNUSED(tls);
UNUSED(size);
return NULL;
}
int tls_start(tls_t *tls)
{
UNUSED(tls);
@@ -86,43 +104,43 @@ int tls_stop(tls_t *tls)
return -1;
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
UNUSED(tls);
/* todo: some kind of error polling/dump */
UNUSED(intf);
return 0;
}
int tls_pending(tls_t *tls)
int tls_pending(struct conn_interface *intf)
{
UNUSED(tls);
UNUSED(intf);
return 0;
}
int tls_read(tls_t *tls, void *buff, size_t len)
int tls_read(struct conn_interface *intf, void *buff, size_t len)
{
UNUSED(tls);
UNUSED(intf);
UNUSED(buff);
UNUSED(len);
return -1;
}
int tls_write(tls_t *tls, const void *buff, size_t len)
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{
UNUSED(tls);
UNUSED(intf);
UNUSED(buff);
UNUSED(len);
return -1;
}
int tls_clear_pending_write(tls_t *tls)
int tls_clear_pending_write(struct conn_interface *intf)
{
UNUSED(tls);
UNUSED(intf);
return -1;
}
int tls_is_recoverable(int error)
int tls_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
UNUSED(error);
return 0;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.c
** strophe XMPP client library -- TLS abstraction header
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -24,12 +25,26 @@
#include "tls.h"
#include "sock.h"
#if GNUTLS_VERSION_NUMBER < 0x030702
/* This is a very dirty workaround to make this file compile on older GnuTLS
* We simply define our own value for the later introduced
* `GNUTLS_CB_TLS_EXPORTER` which makes it compile, but it will fail on runtime
* when the combination of the following options happen:
* * GnuTLS < 3.7.2
* * TLS 1.3
* * A SCRAM PLUS variant to authenticate
* We will think about fixing this once a user pops up who has that problem.
*/
#define GNUTLS_CB_TLS_EXPORTER 3
#endif
struct _tls {
xmpp_ctx_t *ctx; /* do we need this? */
xmpp_conn_t *conn;
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
gnutls_x509_crt_t client_cert;
gnutls_datum_t channel_binding;
int lasterror;
};
@@ -506,8 +521,8 @@ error_out:
void tls_free(tls_t *tls)
{
if (tls->client_cert)
gnutls_x509_crt_deinit(tls->client_cert);
gnutls_free(tls->channel_binding.data);
gnutls_x509_crt_deinit(tls->client_cert);
gnutls_deinit(tls->session);
gnutls_certificate_free_credentials(tls->cred);
strophe_free(tls->ctx, tls);
@@ -554,6 +569,55 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
return err == GNUTLS_E_SUCCESS;
}
int tls_init_channel_binding(tls_t *tls,
const char **binding_prefix,
size_t *binding_prefix_len)
{
gnutls_channel_binding_t binding_type;
gnutls_protocol_t tls_version = gnutls_protocol_get_version(tls->session);
switch (tls_version) {
case GNUTLS_SSL3:
case GNUTLS_TLS1_0:
case GNUTLS_TLS1_1:
case GNUTLS_TLS1_2:
*binding_prefix = "tls-unique";
*binding_prefix_len = strlen("tls-unique");
binding_type = GNUTLS_CB_TLS_UNIQUE;
break;
case GNUTLS_TLS1_3:
*binding_prefix = "tls-exporter";
*binding_prefix_len = strlen("tls-exporter");
binding_type = GNUTLS_CB_TLS_EXPORTER;
break;
default:
strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s",
gnutls_protocol_get_name(tls_version));
return -1;
}
if (tls->channel_binding.data) {
gnutls_free(tls->channel_binding.data);
tls->channel_binding.data = NULL;
}
int ret = gnutls_session_channel_binding(tls->session, binding_type,
&tls->channel_binding);
if (ret) {
strophe_error(tls->ctx, "tls", "could not get channel binding: %s",
gnutls_strerror(ret));
}
return ret;
}
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
{
if (!tls->channel_binding.data || !tls->channel_binding.size) {
strophe_error(tls->ctx, "tls", "No channel binding data available");
}
*size = tls->channel_binding.size;
return tls->channel_binding.data;
}
int tls_start(tls_t *tls)
{
sock_set_blocking(tls->conn->sock);
@@ -569,24 +633,26 @@ int tls_stop(tls_t *tls)
return tls->lasterror == GNUTLS_E_SUCCESS;
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
return tls->lasterror;
return intf->conn->tls->lasterror;
}
int tls_is_recoverable(int error)
int tls_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
return !gnutls_error_is_fatal(error);
}
int tls_pending(tls_t *tls)
int tls_pending(struct conn_interface *intf)
{
return gnutls_record_check_pending(tls->session);
return gnutls_record_check_pending(intf->conn->tls->session);
}
int tls_read(tls_t *tls, void *buff, size_t len)
int tls_read(struct conn_interface *intf, void *buff, size_t len)
{
int ret;
tls_t *tls = intf->conn->tls;
ret = gnutls_record_recv(tls->session, buff, len);
tls->lasterror = ret < 0 ? ret : 0;
@@ -594,9 +660,10 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return ret;
}
int tls_write(tls_t *tls, const void *buff, size_t len)
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{
int ret;
tls_t *tls = intf->conn->tls;
ret = gnutls_record_send(tls->session, buff, len);
tls->lasterror = ret < 0 ? ret : 0;
@@ -604,8 +671,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
return ret;
}
int tls_clear_pending_write(tls_t *tls)
int tls_clear_pending_write(struct conn_interface *intf)
{
UNUSED(tls);
UNUSED(intf);
return 0;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls_openssl.c
** strophe XMPP client library -- TLS abstraction openssl impl.
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -50,8 +51,10 @@
#if OPENSSL_VERSION_NUMBER < 0x30000000L
#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
#define STROPHE_ERR_func_error_string(e) ""
#define STROPHE_SSL_get1_peer_certificate(s) SSL_get1_peer_certificate(s)
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -106,6 +109,9 @@ struct _tls {
SSL_CTX *ssl_ctx;
SSL *ssl;
X509 *client_cert;
void *channel_binding_data;
size_t channel_binding_size;
FILE *keylogfile;
int lasterror;
};
@@ -280,9 +286,9 @@ void tls_shutdown(void)
#endif
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
return tls->lasterror;
return intf->conn->tls->lasterror;
}
/** Search through the SubjectAlternativeNames and return the next
@@ -411,8 +417,10 @@ _get_alg(const xmpp_ctx_t *ctx, X509 *err_cert, xmpp_cert_element_t el)
alg_nid = OBJ_obj2nid(err_cert->sig_alg->algorithm);
#else
const X509_ALGOR *palg;
const ASN1_OBJECT *obj;
X509_get0_signature(NULL, &palg, err_cert);
alg_nid = OBJ_obj2nid(palg->algorithm);
X509_ALGOR_get0(&obj, NULL, NULL, palg);
alg_nid = OBJ_obj2nid(obj);
#endif
} break;
default:
@@ -551,6 +559,36 @@ static int _tls_password_callback(char *buf, int size, int rwflag, void *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 = strophe_alloc(conn->ctx, sizeof(*tls));
@@ -694,6 +732,8 @@ tls_t *tls_new(xmpp_conn_t *conn)
ret = SSL_set_fd(tls->ssl, conn->sock);
if (ret <= 0)
goto err_free_ssl;
_try_open_keylogfile(tls);
}
return tls;
@@ -712,6 +752,9 @@ err:
void tls_free(tls_t *tls)
{
if (tls->keylogfile)
fclose(tls->keylogfile);
strophe_free(tls->ctx, tls->channel_binding_data);
SSL_free(tls->ssl);
X509_free(tls->client_cert);
SSL_CTX_free(tls->ssl_ctx);
@@ -721,7 +764,7 @@ void tls_free(tls_t *tls)
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
{
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) {
xmpp_tlscert_t *tlscert = _x509_to_tlscert(conn->ctx, cert);
X509_free(cert);
@@ -738,6 +781,83 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
return -1;
}
int tls_init_channel_binding(tls_t *tls,
const char **binding_prefix,
size_t *binding_prefix_len)
{
const char *label = NULL;
size_t labellen = 0;
int ssl_version = SSL_version(tls->ssl);
switch (ssl_version) {
case SSL3_VERSION:
*binding_prefix = "tls-unique";
*binding_prefix_len = strlen("tls-unique");
tls->channel_binding_size = 36;
break;
case TLS1_VERSION:
case TLS1_1_VERSION:
case TLS1_2_VERSION:
*binding_prefix = "tls-unique";
*binding_prefix_len = strlen("tls-unique");
tls->channel_binding_size = 12;
break;
#ifdef TLS1_3_VERSION
case TLS1_3_VERSION:
label = "EXPORTER-Channel-Binding";
labellen = 24;
*binding_prefix = "tls-exporter";
*binding_prefix_len = strlen("tls-exporter");
tls->channel_binding_size = 32;
break;
#endif
default:
strophe_error(tls->ctx, "tls", "Unsupported TLS/SSL Version: %s",
SSL_get_version(tls->ssl));
return -1;
}
strophe_free_and_null(tls->ctx, tls->channel_binding_data);
tls->channel_binding_data =
strophe_alloc(tls->ctx, tls->channel_binding_size);
if (!tls->channel_binding_data)
return -1;
if (ssl_version <= TLS1_2_VERSION) {
size_t len;
if (SSL_session_reused(tls->ssl)) {
len = SSL_get_peer_finished(tls->ssl, tls->channel_binding_data,
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;
}
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
{
if (!tls->channel_binding_data || !tls->channel_binding_size) {
strophe_error(tls->ctx, "tls", "No channel binding data available");
}
*size = tls->channel_binding_size;
return tls->channel_binding_data;
}
int tls_start(tls_t *tls)
{
int error;
@@ -750,7 +870,7 @@ int tls_start(tls_t *tls)
ret = SSL_connect(tls->ssl);
error = ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0;
if (ret == -1 && tls_is_recoverable(error)) {
if (ret == -1 && tls_is_recoverable(NULL, error)) {
/* wait for something to happen on the sock before looping back */
_tls_sock_wait(tls, error);
continue;
@@ -791,7 +911,7 @@ int tls_stop(tls_t *tls)
++retries;
ret = SSL_shutdown(tls->ssl);
error = ret < 0 ? SSL_get_error(tls->ssl, ret) : 0;
if (ret == 1 || !tls_is_recoverable(error) ||
if (ret == 1 || !tls_is_recoverable(NULL, error) ||
retries >= TLS_SHUTDOWN_MAX_RETRIES) {
break;
}
@@ -810,21 +930,23 @@ int tls_stop(tls_t *tls)
return ret <= 0 ? 0 : 1;
}
int tls_is_recoverable(int error)
int tls_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ ||
error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_CONNECT ||
error == SSL_ERROR_WANT_ACCEPT);
}
int tls_pending(tls_t *tls)
int tls_pending(struct conn_interface *intf)
{
return SSL_pending(tls->ssl);
return SSL_pending(intf->conn->tls->ssl);
}
int tls_read(tls_t *tls, void *buff, size_t len)
int tls_read(struct conn_interface *intf, void *buff, size_t len)
{
int ret;
tls_t *tls = intf->conn->tls;
ret = SSL_read(tls->ssl, buff, len);
_tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
@@ -832,9 +954,10 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return ret;
}
int tls_write(tls_t *tls, const void *buff, size_t len)
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{
int ret;
tls_t *tls = intf->conn->tls;
ret = SSL_write(tls->ssl, buff, len);
_tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
@@ -842,9 +965,9 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
return ret;
}
int tls_clear_pending_write(tls_t *tls)
int tls_clear_pending_write(struct conn_interface *intf)
{
UNUSED(tls);
UNUSED(intf);
return 0;
}
@@ -882,7 +1005,7 @@ static const char *_tls_error_str(int error, const char **tbl, size_t tbl_size)
static void _tls_set_error(tls_t *tls, int error)
{
if (error != 0 && !tls_is_recoverable(error)) {
if (error != 0 && !tls_is_recoverable(NULL, error)) {
strophe_debug(tls->ctx, "tls", "error=%s(%d) errno=%d lasterror=%d",
TLS_ERROR_STR(error, tls_errors), error, errno,
tls->lasterror);
@@ -913,7 +1036,7 @@ static void _tls_dump_cert_info(tls_t *tls)
X509 *cert;
char *name;
cert = SSL_get_peer_certificate(tls->ssl);
cert = STROPHE_SSL_get1_peer_certificate(tls->ssl);
if (cert == NULL)
strophe_debug(tls->ctx, "tls", "Certificate was not presented by peer");
else {

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls_schannel.c
** strophe XMPP client library -- TLS abstraction schannel impl.
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -236,6 +237,23 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
return -1;
}
int tls_init_channel_binding(tls_t *tls,
const char **binding_prefix,
size_t *binding_prefix_len)
{
UNUSED(tls);
UNUSED(binding_prefix);
UNUSED(binding_prefix_len);
return -1;
}
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
{
UNUSED(tls);
UNUSED(size);
return NULL;
}
int tls_start(tls_t *tls)
{
ULONG ctxtreq = 0, ctxtattr = 0;
@@ -244,9 +262,11 @@ int tls_start(tls_t *tls)
SECURITY_STATUS ret;
int sent;
char *name;
struct conn_interface *intf;
/* use the domain there as our name */
name = tls->conn->domain;
intf = tls->conn->intf;
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
@@ -295,9 +315,9 @@ int tls_start(tls_t *tls)
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
sent = sock_write(tls->sock, writebuff, writelen);
sent = sock_write(intf, writebuff, writelen);
if (sent == -1) {
tls->lasterror = sock_error();
tls->lasterror = sock_error(intf);
} else {
writebuff += sent;
writelen -= sent;
@@ -335,13 +355,13 @@ int tls_start(tls_t *tls)
select(tls->sock, &fds, NULL, NULL, &tv);
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len);
inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
if (inbytes > 0) {
len += inbytes;
p += inbytes;
} else {
tls->lasterror = sock_error();
tls->lasterror = sock_error(intf);
}
}
@@ -356,9 +376,9 @@ int tls_start(tls_t *tls)
if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
sent = sock_write(tls->sock, writebuff, writelen);
sent = sock_write(intf, writebuff, writelen);
if (sent == -1) {
tls->lasterror = sock_error();
tls->lasterror = sock_error(intf);
} else {
writebuff += sent;
writelen -= sent;
@@ -405,20 +425,22 @@ int tls_stop(tls_t *tls)
return -1;
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
return tls->lasterror;
return intf->conn->tls->lasterror;
}
int tls_is_recoverable(int error)
int tls_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
error == WSAEINPROGRESS);
}
int tls_pending(tls_t *tls)
int tls_pending(struct conn_interface *intf)
{
tls_t *tls = intf->conn->tls;
// There are 3 cases:
// - there is data in ready buffer, so it is by default pending
// - there is data in recv buffer. If it is not decrypted yet, means it
@@ -434,9 +456,10 @@ int tls_pending(tls_t *tls)
return 0;
}
int tls_read(tls_t *tls, void *buff, size_t len)
int tls_read(struct conn_interface *intf, void *buff, size_t len)
{
int bytes;
tls_t *tls = intf->conn->tls;
/* first, if we've got some ready data, put that in the buffer */
if (tls->readybufferpos < tls->readybufferlen) {
@@ -459,7 +482,7 @@ int tls_read(tls_t *tls, void *buff, size_t len)
read = tls_read(tls, newbuff, len - bytes);
if (read == -1) {
if (tls_is_recoverable(tls->lasterror)) {
if (tls_is_recoverable(intf, tls->lasterror)) {
return bytes;
}
@@ -471,7 +494,7 @@ int tls_read(tls_t *tls, void *buff, size_t len)
}
/* next, top up our recv buffer */
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos,
bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
tls->recvbuffermaxlen - tls->recvbufferpos);
if (bytes == 0) {
@@ -480,8 +503,8 @@ int tls_read(tls_t *tls, void *buff, size_t len)
}
if (bytes == -1) {
if (!tls_is_recoverable(sock_error())) {
tls->lasterror = sock_error();
if (!tls_is_recoverable(intf, sock_error(intf))) {
tls->lasterror = sock_error(intf);
return -1;
}
}
@@ -556,16 +579,17 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return -1;
}
int tls_clear_pending_write(tls_t *tls)
int tls_clear_pending_write(struct conn_interface *intf)
{
tls_t *tls = intf->conn->tls;
if (tls->sendbufferpos < tls->sendbufferlen) {
int bytes;
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
tls->sendbufferlen - tls->sendbufferpos);
if (bytes == -1) {
tls->lasterror = sock_error();
tls->lasterror = sock_error(intf);
return -1;
} else if (bytes > 0) {
tls->sendbufferpos += bytes;
@@ -579,12 +603,13 @@ int tls_clear_pending_write(tls_t *tls)
return 1;
}
int tls_write(tls_t *tls, const void *buff, size_t len)
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{
SecBufferDesc sbdenc;
SecBuffer sbenc[4];
const unsigned char *p = buff;
int sent = 0, ret, remain = len;
tls_t *tls = intf->conn->tls;
ret = tls_clear_pending_write(tls);
if (ret <= 0) {
@@ -644,7 +669,7 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
ret = tls_clear_pending_write(tls);
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) {
if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
return -1;
}
@@ -656,7 +681,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
remain = 0;
}
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
if (ret == 0 ||
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
return sent;
}
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* util.c
** strophe XMPP client library -- various utility functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* util.h
** strophe XMPP client library -- various utility functions
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* uuid.c
* strophe XMPP client library -- UUID generation
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* strophe.h
** strophe XMPP client library C API
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is dual licensed under the MIT and GPLv3 licenses.
** This software is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -17,6 +18,7 @@
#define __LIBSTROPHE_STROPHE_H__
#include <stddef.h> /* size_t */
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
@@ -83,6 +85,14 @@ extern "C" {
* Namespace definition for Stream Management.
*/
#define XMPP_NS_SM "urn:xmpp:sm:3"
/** @def XMPP_NS_COMPRESSION
* Namespace definition for Stream Compression.
*/
#define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress"
/** @def XMPP_NS_FEATURE_COMPRESSION
* Namespace definition for Stream Compression.
*/
#define XMPP_NS_FEATURE_COMPRESSION "http://jabber.org/features/compress"
/* error defines */
/** @def XMPP_EOK
@@ -190,6 +200,15 @@ typedef struct _xmpp_sm_t xmpp_sm_state_t;
* Disable Stream-Management XEP-0198.
*/
#define XMPP_CONN_FLAG_DISABLE_SM (1UL << 5)
/** @def XMPP_CONN_FLAG_ENABLE_COMPRESSION
* Enable Stream-Compression XEP-0138.
*/
#define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6)
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
* Don't reset the compressed stream after each stanza.
* Only enable this flag if you know what you're doing.
*/
#define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
/* connect callback */
typedef enum {
@@ -381,7 +400,6 @@ char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
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);
void xmpp_conn_disable_tls(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);
@@ -397,8 +415,38 @@ typedef enum {
char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
xmpp_queue_element_t which);
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state);
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
/** The function which will be called when Strophe updates its internal SM
* state.
*
* Please note that you have to create a copy of the buffer, since the library
* will free the buffer right after return of the callback function.
*
*
* @param conn The Strophe connection object this callback
* originates from.
* @param ctx The `ctx` pointer as passed to
* \ref xmpp_conn_set_sm_callback
* @param sm_state A pointer to a buffer containing the serialized SM
* state.
* @param sm_state_len The length of `sm_state`.
*
* @return 0 on success, -1 on error
*
* @ingroup Connections
*/
typedef void (*xmpp_sm_callback)(xmpp_conn_t *conn,
void *ctx,
const unsigned char *sm_state,
size_t sm_state_len);
void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
xmpp_sm_callback cb,
void *ctx);
int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
const unsigned char *sm_state,
size_t sm_state_len);
void xmpp_free_sm_state(xmpp_sm_state_t *sm_state);
@@ -690,11 +738,18 @@ 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);
/**
/*
* Formerly "private but exported" functions made public for now to announce
* deprecation */
#include <stdarg.h>
/**
* 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 (__GNUC__ * 100 + __GNUC_MINOR__ >= 405)
#define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x)))
@@ -742,6 +797,8 @@ void xmpp_debug_verbose(
XMPP_DEPRECATED(xmpp_conn_set_sockopt_callback)
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval);
XMPP_DEPRECATED(xmpp_conn_set_flags)
void xmpp_conn_disable_tls(xmpp_conn_t *conn);
#ifdef __cplusplus
}

View File

@@ -1,6 +1,7 @@
#!/bin/sh
logfile="../../testbuild.log"
errfile="../../testerr.log"
err_out() {
tail $logfile
@@ -8,6 +9,6 @@ err_out() {
}
./bootstrap.sh
./configure >> $logfile || err_out
make -j$(( `nproc` * 2 + 1 )) >> $logfile || err_out
make check >> $logfile || err_out
./configure >> $logfile 2>> $errfile || err_out
make -j$(( `nproc` * 2 + 1 )) >> $logfile 2>> $errfile || err_out
make check >> $logfile 2>> $errfile || err_out

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +1,19 @@
-----BEGIN CERTIFICATE-----
MIICfjCCAjSgAwIBAgIIW5u5Vwn2Mv0wCgYIKoZIzj0EAwIwSzFJMEcGA1UEAwxA
MIIDIDCCAqagAwIBAgIIddFxfQ2VcRIwCgYIKoZIzj0EAwQwSzFJMEcGA1UEAwxA
dmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxlbmd0aC5pcy5hLnZhbGlk
LmFzY2lpLmNoYXJhY3RlcjAgFw0yMTAzMDExOTExMDBaGA8yMTIxMDMwMTE5MTEw
LmFzY2lpLmNoYXJhY3RlcjAgFw0yMzA4MDkxMzI3MDBaGA8yMTI0MDgwOTEzMjcw
MFowSzFJMEcGA1UEAwxAdmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxl
bmd0aC5pcy5hLnZhbGlkLmFzY2lpLmNoYXJhY3RlcjBJMBMGByqGSM49AgEGCCqG
SM49AwEBAzIABI/tSR5cZ9iHCVw0JwmKLvV3TbxPg3kDdouB0R2WKEdEWwXMIHkE
85RReISg+9/JDaOCAQ4wggEKMAkGA1UdEwQCMAAwHQYDVR0OBBYEFPjLGyHxqlOV
nXJQ9YBrnureO8LiMB8GA1UdIwQYMBaAFPjLGyHxqlOVnXJQ9YBrnureO8LiMAsG
A1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjCBmgYDVR0RBIGSMIGPoE4G
CCsGAQUFBwgFoEIMQHZlcnkubG9uZy51c2VybmFtZUBzby50aGUuYXNuMS5sZW5n
dGguaXMuYS52YWxpZC5hc2NpaS5jaGFyYWN0ZXKBDnlldEBub3RoZXIuc2Fugg53
d3cuc3Ryb3BoZS5pbaAdBggrBgEFBQcIBaARDA9zZWNvbmRAeG1wcC5qaWQwCgYI
KoZIzj0EAwIDOAAwNQIZAJmfqBhBMdaKCiWEuXu84K4+lznfzlRfmwIYCGRD1xk0
9cRa3V6PVwYWz6HcdRdMUBUj
bmd0aC5pcy5hLnZhbGlkLmFzY2lpLmNoYXJhY3RlcjB2MBAGByqGSM49AgEGBSuB
BAAiA2IABHfN9a5xAEPYWz+0O6Jfe95WGUeuq6Eca5po4UGcJgs78L6d4KT+FR/a
yDSi/BmS0ZAksXbshkSjQA6XmAwWkNInJF/buAwh/hFVAOXwx3eKtguPskxg1xYj
0WZ3WrY9oKOCAVMwggFPMAkGA1UdEwQCMAAwHQYDVR0OBBYEFNeonZqrLTGCm3Vk
+X3TtMfDQVf4MGQGA1UdIwRdMFuhT6RNMEsxSTBHBgNVBAMMQHZlcnkubG9uZy51
c2VybmFtZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFy
YWN0ZXKCCHXRcX0NlXESMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
AjCBmgYDVR0RBIGSMIGPoE4GCCsGAQUFBwgFoEIMQHZlcnkubG9uZy51c2VybmFt
ZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFyYWN0ZXKB
DnlldEBub3RoZXIuc2Fugg53d3cuc3Ryb3BoZS5pbaAdBggrBgEFBQcIBaARDA9z
ZWNvbmRAeG1wcC5qaWQwCgYIKoZIzj0EAwQDaAAwZQIxAJTzJY/q/zFws0FPPfVI
VXc+kaGdxwzM1FKlVMwxl+PzXAoeL0Z7FtGSkaCWyaokaAIwLaYxuQhgisetYxbU
qTyv0MdS2uUy6NNcVNztWCMS350TuuVm3f9wmwOjfaeoHft3
-----END CERTIFICATE-----

Binary file not shown.

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* check_parser.h
** strophe XMPP client library -- parser tests
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdlib.h>

View File

@@ -1,5 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MF8CAQEEGF7SsbTSOjzb97nFc7Qbt4sic+1nZk+ETqAKBggqhkjOPQMBAaE0AzIA
BI/tSR5cZ9iHCVw0JwmKLvV3TbxPg3kDdouB0R2WKEdEWwXMIHkE85RReISg+9/J
DQ==
MIGkAgEBBDBZ90AXrC5n1ZxFGq4WpqMgvH7qw2YddaUzzPFtf9qDRDrRe3xW6nTr
Iywpxdh26GWgBwYFK4EEACKhZANiAAR3zfWucQBD2Fs/tDuiX3veVhlHrquhHGua
aOFBnCYLO/C+neCk/hUf2sg0ovwZktGQJLF27IZEo0AOl5gMFpDSJyRf27gMIf4R
VQDl8Md3irYLj7JMYNcWI9Fmd1q2PaA=
-----END EC PRIVATE KEY-----

View File

@@ -1,8 +1,9 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,30B7F2152A39184AEEF290ED2B1DBC0E
DEK-Info: AES-256-CBC,D1537075EE9323A68A08B79257D87D53
IojJAF1hGHL9qnu5MCzl5l1PzsnxLj38JjLBLPXaNekiAbag5H9Tzr7y5nfyviPM
hi6syrkXgLOBF+mZWJouOxCNx7+t6eWVfsDZcnzsw6HrTN/2xwkYP/3lYfzlnt6q
/AKYQIUUPc/31J6xg3kGRQ==
faIortcMN9tCcmwidAJlFDOS68OJlAH1Bzjh2sqhZDhFJEU/w3bOihTDk1cV0yg1
JHSxolcX7aYLaBfhSdbnUDaeNhyvfF97O6lMHXbiSdkWBMKrOjSwpW5BcemQgbpI
gjCi7KYWb2Z3CqcELR9624u4vRgytJQliqLW1jjnGMELcuq7HaoB2wP0XmXp9/BV
QMQU504S6V8JbogFZeNGCOq+EW5C8tEBCz82y1PRUr0=
-----END EC PRIVATE KEY-----

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* res_query_dump.c
* Simple program to dump res_query(3) response
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* Linux and OSX:

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test.c
* strophe XMPP client library -- common routines for tests
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test.h
** libstrophe XMPP client library -- common routines for tests
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#ifndef __LIBSTROPHE_TEST_H__
@@ -34,46 +35,48 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
#define COMPARE(v1, v2) \
do { \
const char *__v1 = v1; \
const char *__v2 = v2; \
if ((__v1 == NULL) && (__v2 == NULL)) { \
/* noop */ \
} else if (!__v1 || !__v2 || strcmp(__v1, __v2) != 0) { \
printf("Error: %s\n" \
"Expected: %s\n" \
"Got: %s\n", \
#v1 " != " #v2, __v1, __v2); \
exit(1); \
} \
#define COMPARE(should, is) \
do { \
const char *__should = should; \
const char *__is = is; \
if ((__should == NULL) && (__is == NULL)) { \
/* noop */ \
} else if (!__should || !__is || strcmp(__should, __is) != 0) { \
printf("Error: %s\n" \
"Expected: %s\n" \
"Got: %s\n", \
#should " != " #is, __should, __is); \
exit(1); \
} \
} while (0)
#define COMPARE_BUF(v1, len1, v2, len2) \
do { \
const uint8_t *__v1 = (uint8_t *)(v1); \
const uint8_t *__v2 = (uint8_t *)(v2); \
size_t __len1 = len1; \
size_t __len2 = len2; \
if (__len1 != __len2 || memcmp(__v1, __v2, __len1) != 0) { \
printf("Error: %s\n", #v1 " != " #v2); \
printf("Expected: 0x%s\n", test_bin_to_hex(__v1, __len1)); \
printf("Got: 0x%s\n", test_bin_to_hex(__v2, __len2)); \
exit(1); \
} \
#define COMPARE_BUF(should, should_len, is, is_len) \
do { \
const uint8_t *__should = (uint8_t *)(should); \
const uint8_t *__is = (uint8_t *)(is); \
size_t __should_len = should_len; \
size_t __is_len = is_len; \
if (__should_len != __is_len || \
memcmp(__should, __is, __should_len) != 0) { \
printf("Error: %s\n", #should " != " #is); \
printf("Expected: 0x%s\n", \
test_bin_to_hex(__should, __should_len)); \
printf("Got: 0x%s\n", test_bin_to_hex(__is, __is_len)); \
exit(1); \
} \
} while (0)
#define ENSURE_EQ(v1, v2) \
do { \
int __v1 = v1; \
int __v2 = v2; \
if (__v1 != __v2) { \
printf("Error: %s\n" \
"Expected: %d\n" \
"Got: %d\n", \
#v1 " != " #v2, __v2, __v1); \
exit(1); \
} \
#define ENSURE_EQ(should, is) \
do { \
int __should = should; \
int __is = is; \
if (__should != __is) { \
printf("Error: %s\n" \
"Expected: %d\n" \
"Got: %d\n", \
#should " != " #is, __is, __should); \
exit(1); \
} \
} while (0)
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len);

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_base64.c
** libstrophe XMPP client library -- test routines for the base64 codec
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_ctx.c
** libstrophe XMPP client library -- test routines for the library run-time
*context
@@ -7,7 +8,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_hash.c
** libstrophe XMPP client library -- self-test for the hash-table implementation
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdlib.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_jid.c
** libstrophe XMPP client library -- test routines for the jid utils
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_md5.c
* strophe XMPP client library -- test vectors for MD5
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* gcc -o test_md5 -I./src tests/test_md5.c tests/test.c src/md5.c */

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* check_rand.c
* strophe XMPP client library -- test vectors for Hash_DRBG
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* gcc -o test_rand -I./src tests/test_rand.c tests/test.c src/sha1.c */

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_resolver.c
* strophe XMPP client library -- tests for resolver
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_sasl.c
** libstrophe XMPP client library -- test routines for the SASL implementation
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_scram.c
* strophe XMPP client library -- test vectors for SCRAM-SHA1
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* gcc -o test_scram -I./src tests/test_scram.c tests/test.c src/sha1.c */

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_send_queue.c
** libstrophe XMPP client library -- test routines for the send queue
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>
@@ -39,48 +40,48 @@ int main()
xmpp_conn_set_sm_state(conn, sm_state);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
ENSURE_EQ(0, xmpp_conn_send_queue_len(conn));
state = conn->state;
conn->state = XMPP_STATE_CONNECTED;
xmpp_send_raw(conn, "foo", 3);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
ENSURE_EQ(1, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "bar", 3);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 2);
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "baz", 3);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 3);
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "baan", 4);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 4);
ENSURE_EQ(4, xmpp_conn_send_queue_len(conn));
conn->send_queue_head->wip = 1;
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 3);
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_OLDEST);
COMPARE("bar", ret);
xmpp_free(ctx, ret);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 2);
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
conn->send_queue_head->wip = 0;
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 3);
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_OLDEST);
COMPARE("foo", ret);
xmpp_free(ctx, ret);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 2);
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_YOUNGEST);
COMPARE("baan", ret);
xmpp_free(ctx, ret);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
ENSURE_EQ(1, xmpp_conn_send_queue_len(conn));
ret = xmpp_conn_send_queue_drop_element(conn, XMPP_QUEUE_YOUNGEST);
COMPARE("baz", ret);
xmpp_free(ctx, ret);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
ENSURE_EQ(0, xmpp_conn_send_queue_len(conn));
conn->state = state;

166
tests/test_serialize_sm.c Normal file
View File

@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_serialize_sm.c
** libstrophe XMPP client library -- test routines for the send queue
**
** Copyright (C) 2024 Stephen Paul Weber
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "strophe.h"
#include "common.h"
#include "test.h"
void callback(xmpp_conn_t *conn,
void *ctx,
const unsigned char *sm_state,
size_t sm_state_len)
{
int *callback_count = ctx;
(*callback_count)++;
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 10, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x04SMID", 9, sm_state + 15, 9);
if (*callback_count == 1) {
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x02", 5, sm_state + 24, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 29, 8);
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
sm_state + 37, 31);
COMPARE_BUF("\xba\x00\x00\x00\x00", 5, sm_state + 68, 5);
ENSURE_EQ(sm_state_len, 73);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 0);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 2);
ENSURE_EQ(newconn->send_queue_user_len, 2);
ENSURE_EQ((size_t)(newconn->sm_state->sm_queue.head), 0);
xmpp_conn_release(newconn);
}
if (*callback_count == 2) {
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x01", 5, sm_state + 24, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x1a<r xmlns='urn:xmpp:sm:3'/>", 31,
sm_state + 29, 31);
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 60, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 65, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 70, 8);
ENSURE_EQ(sm_state_len, 78);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 1);
ENSURE_EQ(newconn->send_queue_user_len, 1);
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
xmpp_conn_release(newconn);
}
if (*callback_count == 3) {
COMPARE_BUF("\x1a\x00\x00\x00\x01", 5, sm_state + 5, 5);
COMPARE_BUF("\x9a\x00\x00\x00\x00", 5, sm_state + 24, 5);
COMPARE_BUF("\xba\x00\x00\x00\x01", 5, sm_state + 29, 5);
COMPARE_BUF("\x1a\x00\x00\x00\x00", 5, sm_state + 34, 5);
COMPARE_BUF("\x7a\x00\x00\x00\x03"
"foo",
8, sm_state + 39, 8);
ENSURE_EQ(sm_state_len, 47);
xmpp_conn_t *newconn = xmpp_conn_new(conn->ctx);
xmpp_conn_restore_sm_state(newconn, sm_state, sm_state_len);
ENSURE_EQ(newconn->sm_state->sm_sent_nr, 1);
ENSURE_EQ(newconn->sm_state->sm_handled_nr, 0);
COMPARE(newconn->sm_state->id, "SMID");
ENSURE_EQ(newconn->send_queue_len, 0);
ENSURE_EQ(newconn->send_queue_user_len, 0);
ENSURE_EQ(newconn->sm_state->sm_queue.head->sm_h, 0);
xmpp_conn_release(newconn);
}
}
int fake_read(struct conn_interface *intf, void *buff, size_t len)
{
return 0;
}
int fake_write(struct conn_interface *intf, const void *buff, size_t len)
{
return len;
}
int fake_flush(struct conn_interface *intf)
{
return 0;
}
int fake_error_is_recoverable(struct conn_interface *intf, int err)
{
return 0;
}
int main()
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
xmpp_conn_state_t state;
xmpp_sm_state_t *sm_state;
char *ret;
unsigned int n;
int callback_count = 0;
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
sm_state = strophe_alloc(ctx, sizeof(*sm_state));
memset(sm_state, 0, sizeof(*sm_state));
sm_state->ctx = ctx;
sm_state->sm_support = sm_state->sm_enabled = sm_state->can_resume = 1;
sm_state->id = strophe_strdup(ctx, "SMID");
xmpp_conn_set_sm_state(conn, sm_state);
xmpp_conn_set_sm_callback(conn, callback, &callback_count);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 0);
struct conn_interface intf = {fake_read, fake_write,
fake_flush, fake_flush,
fake_flush, fake_error_is_recoverable,
conn};
conn->intf = intf;
state = conn->state;
conn->state = XMPP_STATE_CONNECTED;
conn->sock = 123;
xmpp_send_raw(conn, "foo", 3);
ENSURE_EQ(xmpp_conn_send_queue_len(conn), 1);
ENSURE_EQ(callback_count, 1);
xmpp_run_once(ctx, 0);
ENSURE_EQ(callback_count, 3);
conn->state = state;
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

View File

@@ -7,6 +7,7 @@
#include <stdio.h>
#include <string.h>
#include <limits.h>
#undef HAVE_VSNPRINTF
#undef HAVE_SNPRINTF
@@ -28,7 +29,14 @@ int main(void)
char *int_fmt[] = {"%-1.5d", "%1.5d", "%123.9d", "%5.5d",
"%10.5d", "% 10.5d", "%+22.33d", "%01.3d",
"%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 fail = 0;
int num = 0;
@@ -60,6 +68,19 @@ int main(void)
}
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);
return fail != 0 ? 1 : 0;
}

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_sock.c
** libstrophe XMPP client library -- test routines for the socket abstraction
**
@@ -6,7 +7,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_stanza.c
* libstrophe XMPP client library -- test routines for stanza functions
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* gcc -o test_stanza -I./src tests/test_stanza.c -lstrophe */
@@ -174,7 +175,7 @@ static void test_stanza_error(xmpp_ctx_t *ctx)
xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL);
assert(error != NULL);
mood = xmpp_stanza_new_from_string(ctx, str_mood);
assert(stanza != NULL);
assert(mood != NULL);
assert(xmpp_stanza_get_to(error) != NULL);
COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error));

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_string.c
* strophe XMPP client library -- tests for re-implemented string functions
*
@@ -6,7 +7,7 @@
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
* This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>

View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_xmppaddr.c
** libstrophe XMPP client library -- test routines for the xmppaddr
** certificate API's
@@ -7,7 +8,7 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <assert.h>