191 Commits
cafile ... next

Author SHA1 Message Date
Stephen Paul Weber
f8cda0f65c Actually set the tlscert conn reference
Previously it was always NULL
2025-08-12 12:25:13 +02:00
Steffen Jaeckel
649a3b4fca Bump ubuntu version of release-test CI job
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-07-15 16:22:30 +02:00
Steffen Jaeckel
a2b3b6b9e2 Add xmpp_tlscert_get_userdata()
This fixes #249

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
2025-04-02 10:17:21 +02:00
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
Steffen Jaeckel
f13ea6e556 Release libstrophe-0.12.3
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-02 15:42:54 +02:00
Steffen Jaeckel
29f68c0dd7 Update AUTHORS file
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-08-02 15:42:54 +02:00
Steffen Jaeckel
3c0966d74e Run CI also on next branch
Like that we can test stuff without opening a PR, before it necessarily
lands on `master`.

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

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

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

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

Reproducing this was done with netcat and profanity:

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

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

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

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

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

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

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

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

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

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

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

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

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

This issue was discovered in CI job [0].

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

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

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

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

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2023-01-09 15:55:23 +01:00
Anna “CyberTailor”
96ece6fbdb test configure script with dash in CI 2022-11-12 13:23:20 +01:00
Anna “CyberTailor”
a60d2ea19d configure: fix non-portable "==" tests 2022-11-12 12:58:35 +01:00
Steffen Jaeckel
70a908e18b Release libstrophe-0.12.2
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-08 11:48:47 +02:00
Steffen Jaeckel
004a5bf7fc fix infitite looping in resolver
...this time for real ...hopefully :)

fixup of 870f2174d5

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-05 14:54:50 +02:00
Steffen Jaeckel
e9bd06354a always print the broken message in the resolver tests
If Valgrind complains in this testcase we found a bug in the internal
resolver implementation, but as we didn't have the broken message we also
couldn't reproduce the failure.
Now we can reproduce it if it happens again.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-05 13:59:17 +02:00
Steffen Jaeckel
54b92c0255 re-use _auth_success() instead of copying ...
... and forgetting to 1. clear the password cache and 2. print a message
of what exactly happened ...

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-05 12:36:31 +02:00
Steffen Jaeckel
6881a2e770 add option to use clock_gettime() in perf example
This is done to support architectures where `rdtsc()` is not implemented.
In case it is available use `clock_gettime()`. Otherwise still fall back
to use `clock()`.

The first approach was to always use `clock_gettime()` if it is available
but the overhead it brings compared to `rdtsc()` is high, so I chose to
only try to use it if `rdtsc()` is not implemented.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-05 12:36:22 +02:00
Steffen Jaeckel
e29f422511 reset relevant parts of SM state on disconnect
Prevent assuming SM is enabled if the connection gets re-used without a
complete destroy&re-create of the connection object.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-08-04 11:54:36 +02:00
Tu Duong Quyet
1a8be1d617 Make it right to check/implement va_copy 2022-08-04 11:52:35 +02:00
Steffen Jaeckel
8ea15cf656 disable ASM in libressl
Valgrind spits a false-positive when the AESNI extension is used from ASM.

https://github.com/libressl-portable/portable/issues/760#issuecomment-1175461956

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-07-11 13:05:57 +02:00
Steffen Jaeckel
a8807feb5c Release libstrophe-0.12.1
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-07-05 00:08:47 +02:00
Steffen Jaeckel
4d15f1db0b disable some LibreSSL builds
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-07-04 20:13:00 +02:00
Steffen Jaeckel
957725348e also run CI tests with OpenSSL 3.0
... and extend build matrix of libressl.

Most of those new libressl build jobs are marked as `continue: true` since
either they don't build or they cause memory leaks (3.5/OPENBSD_7_1)
The `OPENBSD_X_Y` branches are also marked like that since they will
evolve and maybe break at a later point in time. Currently they represent
the buildable versions of their respective releases (3.3.6 resp. 3.4.3).

Also change CI distro to ubuntu-22.04 as using OpenSSL 3 on 20.04 fails.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-07-04 20:12:54 +02:00
Stu Tomlinson
357edbd367 Add XMPP_CONN_FLAG_DISABLE_SM to docs 2022-06-23 17:24:38 +01:00
Stu Tomlinson
3d01915407 Accept PKCS#12 file in either key or cert 2022-06-23 17:24:38 +01:00
Stu Tomlinson
e90a6e9f71 Add getpass() for Windows 2022-06-23 09:44:00 +01:00
Stu Tomlinson
3891b73c44 Open PKCS12 files as binary
Opening as text on Windows causes read failures:
tls DEBUG error:0680008E:asn1 encoding routines::not enough data
2022-06-23 09:44:00 +01:00
Stu Tomlinson
a66c3234d7 Fix tests with OpenSSL 3
RC2-40-CBC is disabled in OpenSSL 3, so recreate cert.emptypass.pfx
using AES-256-CBC
2022-06-23 09:44:00 +01:00
Stu Tomlinson
2a81327ab0 Ignore coding style change in git blame
github supports .git-blame-ignore-revs to ignore certain commits in
blame view, add the coding style change commit to this file

Can be used locally too with:
$ git blame --ignore-revs-file .git-blame-ignore-revs
2022-06-23 09:44:00 +01:00
Stu Tomlinson
1d6714c59b Fix 'make check' when builddir != srcdir 2022-06-23 09:44:00 +01:00
Stu Tomlinson
a771b3d4fe Support building docs when builddir != srcdir 2022-06-23 09:44:00 +01:00
Stu Tomlinson
700abd95b6 Add schannel option to autotools build 2022-06-23 09:43:59 +01:00
Fabrice Fontaine
476dd4c4f7 add --disable-examples
Allow the user to disable build of examples

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2022-06-11 23:31:08 +02:00
Michael Vetter
f0383d50cd Include unistd.h for ssize_t
When compiling on buildroot I get:
`src/tls.c:235:28: error: ‘ssize_t’ undeclared (first use in this
function); did you mean ‘size_t’? `

To have this POSIX type available we will need to include
`sys/types.h` or `unistd.h`.
2022-06-08 10:36:35 +02:00
Steffen Jaeckel
044398eeba update README
* fix link to CI
* improve "Documentation" part
* add comment regarding signing key

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-05-10 13:08:29 +02:00
Steffen Jaeckel
675ba5b3fc Release libstrophe-0.12.0
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-05-09 15:40:09 +02:00
Steffen Jaeckel
62d1063560 prevent memory leak in xmpp_stanza_new_from_string()
Only remember the first stanza in case the string contains
multiple stanzas.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-05-05 16:34:44 +02:00
Steffen Jaeckel
d8c9ca49e1 Be user-friendly when opening a pfx/p12 file
Before this change the user had to provide a password callback, even if the
PKCS#12 encoded file has no or an empty password.

This changes the behavior, so we first try to open the file without a
password and only then ask the user to provide one.

This fixes #204.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-05-05 16:34:44 +02:00
Steffen Jaeckel
91d6dac985 add Stream-Management support to bot example
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 13:51:05 +02:00
Steffen Jaeckel
d7bd434f41 add client-cert support to bot example
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 13:51:05 +02:00
Steffen Jaeckel
5ac415986e finish implementing XEP-0198
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 13:51:05 +02:00
Steffen Jaeckel
266fd0b1d4 add send-queue tests
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 11:24:13 +02:00
Steffen Jaeckel
10760238b3 add xmpp_conn_send_queue_drop_element()
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 11:24:13 +02:00
Steffen Jaeckel
d9acad3d91 add xmpp_conn_send_queue_len()
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 11:24:13 +02:00
Steffen Jaeckel
27278c032b introduce owner concept in send_queue
In order to be able to distinguish between user-created entries and
library-internal entries, we mark each entry who it belongs to.
This allows then later to let the user manage only the queue entries
that were created by them.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-04-06 11:24:13 +02:00
Dmitry Podgorny
57bc4bbb75 WIP XEP-0198: implement stream management
Stream management is enabled when server supports it. There is no option
to disable it.
2022-03-31 12:45:39 +02:00
Steffen Jaeckel
3600756723 fix linter warnings
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-31 12:44:19 +02:00
Steffen Jaeckel
7111ee2f85 better deprecation warnings
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-23 19:05:48 +01:00
Steffen Jaeckel
dcee2eea91 also run valgrind in CI
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-23 19:05:48 +01:00
Steffen Jaeckel
4a0c71a840 add option to enable valgrind for tests
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-23 17:04:13 +01:00
Steffen Jaeckel
e1ee758b61 fix warning when compiling against OpenSSL 3.0.x
`ERR_func_error_string()` has been deprecated.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-23 17:04:13 +01:00
Steffen Jaeckel
4b5e103d9c move password cache into libstrophe
The cache is stored per connection object and is cleared on
* entry of wrong password
* release of connection object
* successful connection

It can be configured that libstrophe retries the password entry in case
the user entered a wrong password.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-23 17:04:13 +01:00
Steffen Jaeckel
bddb80a192 add support for a password callback
In order to be able to load password-protected key files a password
callback was added.

This also adds support for PKCS#12 containers instead of certificate+key.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-21 00:24:22 +01:00
Stu Tomlinson
06afe3d1c2 Add API to register callback to set socket options
The registered callback function will be called before connect() is
called on the socket, allowing tweaking of much more than just keepalive
settings.

This deprecates xmpp_conn_set_keepalive().

Includes helper/example callback function to set default keepalive
parameters that some library users may find useful.
2022-03-17 22:59:07 +00:00
Steffen Jaeckel
97b1452211 revive removed API's
In order to do proper deprecation of those API's we re-introduce them
so they can be removed in the next release. This will then also lead to
an ABI bump.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-16 14:39:20 +01:00
Steffen Jaeckel
fc6ba89c61 properly rename internal [v]snprintf() functions
Otherwise it clashes when we want to re-introduce the `xmpp_` prefix'ed
versions.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-16 14:39:20 +01:00
Stu Tomlinson
a92115ab9f Do not compile/link snprintf.o if it is not needed 2022-03-16 11:33:58 +00:00
Stu Tomlinson
08158ee8ad Support (cross)compiling using MinGW-w64 2022-03-16 11:32:24 +00:00
Steffen Jaeckel
528c16e6c8 fix build with OpenSSL 1.1.0l
Debian 9.13 (stretch) brings this version and building failed there.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-09 16:48:07 +01:00
Steffen Jaeckel
870f2174d5 fix potential infinite looping of resolver
In case a pointer to itself was received, the resolver looped inifinitely
until the stack overflows.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-09 16:48:07 +01:00
Steffen Jaeckel
1406198087 add fuzzing of resolver
based on the "random test" at the end of the regular resolver tests.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-09 12:08:15 +01:00
Steffen Jaeckel
53373261ad fix example/perf.c compilation
In case this ifdef-elseif-chain falls back to the else path, the macro
to `clock()` wasn't defined.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-09 12:08:11 +01:00
Steffen Jaeckel
c1c697b177 show error-logs on failure
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-03-09 12:08:07 +01:00
Stu Tomlinson
808a981e86 Compile fix
examples/perf.c: In function ‘perf_rand’:
examples/perf.c:42:5: error: ‘for’ loop initial declarations are only allowed in C99 mode
     for (size_t sz = 2; sz <= alloc_sz; sz <<= 1) {
2022-03-02 11:25:44 +00:00
Stu Tomlinson
879f40a0ae Minor compile warning fix
src/handler.c: In function 'handler_system_delete_all':
src/handler.c:706:25: warning: 'key2' may be used uninitialized in this function [-Wmaybe-uninitialized]
                 hash_add(conn->id_handlers, key2, head);
2022-03-02 11:25:05 +00:00
Steffen Jaeckel
0365f03342 fix --help string
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-22 15:02:47 +01:00
Steffen Jaeckel
1e8dc3caa8 move common code to static function
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-22 14:58:44 +01:00
Steffen Jaeckel
e32d39dbc1 forgot to update tls_schannel.c with new API's
Fixup of
93e04b8d03
a97714da18

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-10 11:39:25 +01:00
Steffen Jaeckel
2bc34b5847 add new CI job that does a test-release
This also re-defines that `make release` does the release of all tarballs
and will run the tests against them.

Latest LibreSSL test is also marked non-essential, as it tends to fail
from time to time ...

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-10 11:29:57 +01:00
Steffen Jaeckel
4cf7805e9e update files included in a release
Removal of `rand.h` is a fixup of
118087f2a1

Somehow autotools manages to know that all those files must be
included in a release, no need to track them manually.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-10 11:21:43 +01:00
Steffen Jaeckel
a1206435eb add CI test-run with getrandom disabled
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
69550f9be9 allow reading arbitrary number of bytes from xmpp_rand_bytes()
If we expose the API it shouldn't be limited.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
9c7b8580d8 add examples/perf
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
ec5e942705 test against latest LibreSSL
... and test against latest patch of the minor versions we use to test

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
404464fd40 auto-format
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
619bcc38e9 make [v]snprintf() private
Fixes #189

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
728e050a05 make strtok_r private
Fixes #189

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
93e04b8d03 make logging functions private
Fixes #189

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
a97714da18 make alloc-class of functions private
Fixes #189

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-06 12:36:38 +01:00
Steffen Jaeckel
118087f2a1 make RNG API (officially) public
Fixes #189

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
3a3b4faa10 use getrandom() on recent linux distros
Instead of using the internal DRBG-based RNG, use the `getrandom()` system
call where available.
Keep the existing version as fall-back for other systems that don't provide
that system call.

... and for cases where auto-detection fails or the user simply wants to
use the internal RNG a new configure-switch is provided.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
2850fd7792 fix build with libressl
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
db91f793dc port travis CI to GitHub actions
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
3225bafd54 make basic example basic again
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
47f4d883c8 fix Doxygen warnings
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
d8d0e75466 improve OpenSSL error logging
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
e9d983dec2 update Doxyfile
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
f3878b954a use ascii chars
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
f693efd532 add some new make targets
* `release` creates the tarballs
* `test-release` takes the tarballs, extracts them, builds the library
  and tests and runs the tests
* `docs` run doxygen

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-02-04 13:36:17 +01:00
Steffen Jaeckel
6a052ed0fc add Issue template
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-01-31 16:51:19 +01:00
Steffen Jaeckel
edb54ebe90 introduce library versioning
This is done by loosely following the description on [1]
and other examples.

Fixes #190

[1] https://phab.enlightenment.org/w/autotoolsintegration/

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2022-01-31 16:51:01 +01:00
Steffen Jaeckel
28f3ce19b8 add xmpp_stanza_get_child_by_path()
... to provide an easier way to find long paths.

After looking at some code in profanity-im/profanity#1605 I thought this
could help to make libstrophe-user code easier to write (and read).

Initially it started off as `xmpp_stanza_get_child_by_xpath()` but then
I was annoyed when it came to handling all those potential corner cases
like e.g. escaping URL's in the ns ... so here we go with a
vararg approach :)

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-12-03 15:17:54 +01:00
Steffen Jaeckel
b0631e322f use lower-case labels
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-12-03 12:04:29 +01:00
Steffen Jaeckel
c3c28cb4c1 add code coverage support
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-12-03 12:04:29 +01:00
Steffen Jaeckel
ae5484ebd5 handler: refactor some copy-pasta
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-12-03 12:04:29 +01:00
Steffen Jaeckel
01f2c3128a handler: no need to dup string
`hash_iter_next()` returns a pointer to an element of the data structure
which is persistent and not altered while calling `hash_iter_next()`.
Therefor we don't have to dup the string, but simply can remember the
pointer.
In the case where the element would be `hash_drop()`'ped which contains
the key, the key is not accessed after it has been free'd.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-12-03 12:04:29 +01:00
Dmitry Podgorny
07a4f778d3 ctx: initialize field 'verbosity'
Uninitialized field can contain a garbage and break expected logs
behavior.
2021-12-02 00:08:34 +02:00
Dmitry Podgorny
44a5556278 .gitignore: ignore tar.bz2 and tar.xz packages 2021-11-25 19:34:39 +02:00
Dmitry Podgorny
b3e355736f event: move xmpp_ctx_set_timeout() to the Event loop module
xmpp_ctx_set_timeout() belongs to the event loop. Move it to provide
better doxygen documentation.
2021-11-25 19:29:07 +02:00
Dmitry Podgorny
058576e768 autotools: add key.pem and cert.pem to EXTRA_DIST 2021-10-28 22:09:42 +03:00
Dmitry Podgorny
1c84ca3730 Release libstrophe-0.11.0 2021-10-28 21:49:50 +03:00
Steffen Jaeckel
bf1348e89d add required internal TLS API's for dummy and schannel
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-10-28 17:23:32 +02:00
Steffen Jaeckel
795675d1aa implement certificate verification API for GnuTLS
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-10-28 17:23:32 +02:00
Steffen Jaeckel
12009a009d implement certificate verification API for OpenSSL
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-10-28 17:23:32 +02:00
Steffen Jaeckel
f23ac83c95 add callback functionality on certificate verification failure
Based on the differences to libmesode this functionality has been added.

It allows a library-user to set a callback for cases where the TLS stack
can't verify a received certificate and let the end-user decide what to
do.

examples/basic implements an example handler of said functionality.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-10-28 14:42:11 +02:00
Steffen Jaeckel
2dc20d13fb declare function static
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-10-26 14:11:43 +02:00
Steffen Jaeckel
e26548375e move "SENT: ..." debug statement to the place where it belongs
This also adds a "QUEUED: ..." debug statement at verbosity level 2.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Steffen Jaeckel
e73cdb57f3 More debug printing of queue details
This adds a new API function xmpp_ctx_set_verbosity() to set increased
verbosity levels.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Steffen Jaeckel
fcc4f8c680 skip further processing on TLS error
While reading through the code this seemed logical to do.
I haven't seen this occuring in the wild!

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Steffen Jaeckel
a1de389e45 return early when nothing would be logged
This returns early in case

1. no log handler is set
2. the default log handler is set and the log level would result in nothing
   being printed.

No need to snprintf() and whatnot if there's no consumer.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Steffen Jaeckel
4ef5b2d449 re-factor sending
Put sending of data into its own function which takes ownership of the
data and therefor minimizes the number of allocations and frees.

This also slightly improves error handling and printing of debug
information.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Steffen Jaeckel
ad9e9d5dbd add xmpp_strndup()
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-07-22 23:44:40 +02:00
Dmitry Podgorny
4e53184eee tests/stanza: add test for xmpp_stanza_reply_error() 2021-07-17 01:23:16 +03:00
Dmitry Podgorny
f19ed572f0 stanza: remove trailing spaces 2021-07-17 01:21:49 +03:00
sshikaree
f27d95fe43 Checking for errros in a xmpp_stanza_reply_error() 2021-07-08 09:44:23 +03:00
Dmitry Podgorny
2bf1988b3c parser: fix memory leak in error path for depth more than 1
If parser has an unfinished stanza with depth > 1, it can happen that
parser->stanza points to a child. Releasing the child doesn't free the
parent stanza.

Instead of releasing parser->stanza in parser_reset() and parser_free(),
find the top most parent and release the whole tree. Also add a test
case for this memory leak.
2021-06-27 03:44:55 +03:00
Dmitry Podgorny
05620ab506 autotools: remove footer.html from EXTRA_DIST 2021-06-27 03:44:55 +03:00
Dmitry Podgorny
0ea9c1d3a2 autotools: fix style for fuzzing option
Fix spacing, fix help string layout.
2021-06-27 03:44:55 +03:00
Jordy Zomer
1f0a3f1ad1 Add basic fuzzing support for libstrophe 2021-06-26 17:05:53 -04:00
Dmitry Podgorny
2f491e0250 travis-ci: update travis config
- add gnutls option
- fix style stage: 'matrix' and 'jobs' don't work together
- switch to Ubuntu 20.04 (focal), because there is issue with
  gnutls-3.4.10 from Ubuntu 16.04 (see #177)
2021-03-27 22:35:10 +02:00
Steffen Jaeckel
2509ed8424 fixup minor gnutls things
* gcc complained about missing include
* remove unnecessary debug statement

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-24 14:47:18 +02:00
Dmitry Podgorny
f3e3e64a17 docs: remove footer.html
Use default footer instead.
2021-03-23 19:51:57 +02:00
Dmitry Podgorny
d7a28f97a6 ChangeLog: update with upcoming features 2021-03-23 02:30:11 +02:00
Dmitry Podgorny
2199d8d088 examples/basic: fix arguments check 2021-03-23 02:17:35 +02:00
Dmitry Podgorny
d1ce4b35ae tests/xmppaddr: improve error reports
COMPARE() assumes that the first argument is the reference. Swap
arguments to report right expected value.
2021-03-23 02:14:25 +02:00
Dmitry Podgorny
e77cd53215 conn: fix segfault in xmpp_connect_client() when jid not set 2021-03-23 02:12:55 +02:00
Dmitry Podgorny
588aea4f54 tls/gnutls: zeroize allocated tls object
Initialize tls object with 0. This also sets tls->client_cert to NULL.
2021-03-23 02:11:09 +02:00
Dmitry Podgorny
ee5f9fb77b tls/openssl: fix openssl-3.0.0 support
3.0.0 includes the "id-on-xmppAddr" object and OBJ_create() returns
NID_undef if we try to create a new one.
2021-03-23 02:08:22 +02:00
Dmitry Podgorny
4790a61437 tls/openssl: fix openssl-0.9.8 support
0.9.8 doesn't implement GENERAL_NAME_get0_otherName().
2021-03-23 02:07:00 +02:00
Steffen Jaeckel
25d3a2d459 add test for new API's
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Steffen Jaeckel
27be6acaeb add tls_id_on_xmppaddr() for GnuTLS
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Steffen Jaeckel
9a0e070c24 add missing API to other TLS implementations
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Steffen Jaeckel
e490011b68 add option to build against gnutls
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Steffen Jaeckel
3b4b664c28 add client-cert support to basic example
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Steffen Jaeckel
18c95fa7bd add support for client authentication via certificates
The SASL EXTERNAL method is implemented to make this possible.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2021-03-23 02:04:59 +02:00
Dmitry Podgorny
cf2f7147d9 tests/rand: don't define xmpp_snprintf when HAVE_SNPRINTF is set
If HAVE_SNPRINTF is set, xmpp_snprintf is replaced with snprintf symbol
and it can cause issues (e.g. on MacOS system).
2021-03-23 00:30:09 +02:00
Dmitry Podgorny
ecf6457cdb conn: send stream attribute "from" after TLS negotiation
RFC6120 states that client SHOULD send the "from" attribute in <stream>.

Because of privacy concerns attribute "from" should be sent only after
TLS negotiation. This step ensures that the server is verified by its
certificate and protects JID from MITM attack.

Fixes #163.
Also should be helpful for #162.
2021-03-21 17:09:35 +02:00
Dmitry Podgorny
db8a511f68 style: remove extra const keyword from interfaces
Const variables in prototypes don't add much value, but make the code
larger and redundant. Remove these const keywords.
Note, this doesn't apply to pointers to const memory.
2021-03-19 22:12:15 +02:00
Dmitry Podgorny
2d5424bcff Release libstrophe-0.10.1 2020-12-24 16:04:21 +02:00
Dmitry Podgorny
5792d52461 doxygen: set correct version number 2020-12-19 02:33:14 +02:00
Dmitry Podgorny
09229e2cd5 conn: remove extra ; symbol 2020-10-07 20:18:20 +03:00
103 changed files with 10022 additions and 2676 deletions

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# Unify coding style with clang-format
562a06425b96450481cdbc88e8872a2bf5a7d8aa

49
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,49 @@
<!--
Provide a general summary of the issue in the Title above
This is a very generic template, remove items that do not apply. For completed items, change [ ] to [x].
-->
### Prerequisites
* [ ] Checked that your issue isn't already filed: https://github.com/strophe/libstrophe/issues?q=
### Additional Information
<!--- Any additional information, configuration or data that might be necessary to reproduce the issue. -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior or even better, provide a functional code example reproducing your issue. -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Steps to Reproduce (for bugs)
<!--- Please either describe your issue in detail or even better, provide a functional code example reproducing your issue. -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
## Environment
<!---
Please provide the version of the library you're using.
You can find the version either in the first line of the `configure.ac` file or if you're using the Git repository, please run `git describe --always --tags --dirty`.
Also, please include the compiler, the compiler version, the architecture, the OS and what version of the OS you're experiencing the issue.
-->

161
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,161 @@
name: CI
on:
push:
branches:
- master
- next
pull_request:
branches: [master]
jobs:
linux-tests:
runs-on: ubuntu-24.04
strategy:
matrix:
valgrind:
- { configure: '' , cflags: '', make: 'check' }
- { configure: '--enable-valgrind' , cflags: '-O2', make: 'check-valgrind' }
options:
- { configure: '' }
- { configure: '--without-libxml2' }
- { configure: '--with-libxml2' }
- { configure: '--with-gnutls' }
- { configure: '--disable-tls' }
- { configure: '--enable-cares' }
- { configure: '--disable-getrandom' }
- { configure: '--disable-static' }
name: Regular Tests
steps:
- uses: actions/checkout@v2
- name: install dependencies
run: |
sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev valgrind
- name: Build the library
run: |
./bootstrap.sh
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3 ${{ matrix.valgrind.cflags }}"
make -j$(nproc)
- name: Run tests
run: |
make -j$(nproc) ${{ matrix.valgrind.make }}
- name: Error logs
if: ${{ failure() }}
run: |
cat test-suite*.log || true
xssl-tests:
runs-on: ubuntu-24.04
strategy:
matrix:
xssl_versions:
- { 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 }
# 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.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:
- uses: actions/checkout@v2
- name: install dependencies
run: |
sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev valgrind
- name: build&install the TLS stack
env:
XSSL_COMMITISH: ${{ matrix.xssl_versions.version }}
LIBRESSL: ${{ matrix.xssl_versions.libressl }}
run: |
./travis/before_script.sh
- name: Build the library
run: |
./bootstrap.sh
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
make -j$(nproc)
- name: Run tests
run: |
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: |
cat test-suite*.log || true
release-test:
runs-on: ubuntu-22.04
name: Check if release would work
steps:
- uses: actions/checkout@v2
- name: install dependencies & bootstrap
run: |
sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev dash
./bootstrap.sh
- name: Check if configure works with non-bash shells
# https://github.com/actions/runner/issues/241 requires us to use this following line...
shell: 'script --return --quiet --command "bash {0}"'
run: |
[ "`CONFIG_SHELL=/bin/dash ./configure 2>&1 1>/dev/null | tee /dev/tty | wc -l`" = "0" ]
- name: Re-run configure with the default shell
run: |
./configure
- name: Try release & tests
run: |
make test-release
- name: Show logs from release build
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-24.04
name: Check coding style
continue-on-error: true
steps:
- uses: actions/checkout@v2
- name: install dependencies
run: |
sudo apt update
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev dos2unix
- name: Configure
run: |
./bootstrap.sh
./configure
- name: Check style
run: |
make format
git diff --exit-code

23
.gitignore vendored
View File

@@ -20,6 +20,8 @@ autom4te.cache
.dirstamp
.deps
.sconsign*
src/*.gcda
src/*.gcno
src/*.o
src/*.lo
examples/*.o
@@ -27,6 +29,8 @@ examples/*.o
*.la
*.pc
*.tar.gz
*.tar.bz2
*.tar.xz
*.zip
docs/html
TAGS
@@ -34,19 +38,26 @@ tags
examples/active
examples/basic
examples/bot
examples/complex
examples/component
examples/perf
examples/register
examples/roster
examples/uuid
examples/vcard
testbuild*.log
testerr*.log
test-release/
test_stamp
test-suite.log
test-suite*.log
tests/*.o
tests/*.log
tests/*.trs
tests/check_parser
tests/test_base64
tests/test_ctx
tests/test_fuzz_parser
tests/test_fuzz_resolver
tests/test_hash
tests/test_jid
tests/test_md5
@@ -54,6 +65,8 @@ tests/test_rand
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
@@ -61,7 +74,9 @@ tests/test_snprintf
tests/test_sock
tests/test_stanza
tests/test_string
m4/
tests/test_xmppaddr
m4/lt*
m4/libtool*
libstrophe.project
libs/
obj/
@@ -69,3 +84,7 @@ expat/
.settings/
.project
.cproject
coverage/
coverage.info
configure~
fuzz-*.log

View File

@@ -1,26 +0,0 @@
language: c
install:
- sudo apt-get update
- sudo apt-get -y install libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libc-ares-dev dos2unix
stages:
- style
- test
before_script:
- ./travis/before_script.sh
env:
- CONFIGURE_OPT="--without-libxml2"
- CONFIGURE_OPT="--with-libxml2"
- CONFIGURE_OPT="--disable-tls"
- CONFIGURE_OPT="--enable-cares"
- CONFIGURE_OPT="PKG_CONFIG_PATH=${HOME}/libressl/lib/pkgconfig" LIBRESSL=yes LIBRESSL_COMMIT="v3.1.4"
- CONFIGURE_OPT="PKG_CONFIG_PATH=${HOME}/libressl/lib/pkgconfig" LIBRESSL=yes LIBRESSL_COMMIT="v2.1.7"
script: ./bootstrap.sh && ./configure ${CONFIGURE_OPT} CFLAGS="-Werror" && make && make check
jobs:
include:
- stage: style
name: "Check code style"
script: ./bootstrap.sh && ./configure && make format && git diff --exit-code
env:
- CONFIGURE_OPT=""
matrix:
fast_finish: true

View File

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

124
ChangeLog
View File

@@ -1,5 +1,129 @@
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)
- Fix configure script for non-bash shells (#218)
- Parse JID's according to RFC7622 (#219)
- Fix potential memory leak in internal DNS resolver (#219)
- Fix potential memory leaks in `xmpp_conn_set_cafile()` and `xmpp_conn_set_capath()` (#219)
- Internal improvements (#219)
0.12.2
- Fix reconnect issues when Stream Management is enabled (#211)
- Fix resolver ... this time for real hopefully (fixup of #200) (#214)
- Fix clearing of password cache on resumed connection (#214)
- Improve detection&implementation of `va_copy` (#213)
- Fix Valgrind CI builds against LibreSSL (#212)
- Fix perf example on platforms where an `rdtsc()` equivalent isn't implemented (#212)
0.12.1
- Fix compilation in buildroot (#207)
- Fixes regarding OpenSSL (#208)
- 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 (#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.
0.12.0
- Fix potential infinite loop in resolver (#200)
- Prevent potential memory leak in `xmpp_stanza_new_from_string()` (#205)
- Do proper SO versioning
- Add code coverage support (#188)
- Add support for password-protected TLS key & PKCS#12/PFX files (#195, #205)
- Stream-Management support - XEP-0198 (#184)
- New API:
- xmpp_conn_send_queue_len()
- xmpp_conn_send_queue_drop_element()
- xmpp_conn_get_sm_state()
- xmpp_conn_set_sm_state()
- xmpp_free_sm_state()
- xmpp_conn_get_keyfile()
- xmpp_conn_set_password_callback()
- xmpp_conn_set_password_retries()
- xmpp_stanza_get_child_by_path()
- xmpp_conn_set_sockopt_callback()
- xmpp_sockopt_cb_keepalive()
- The following APIs were public in the shared library, but not in `strophe.h`.
Now they are officially public API:
- xmpp_rand_new()
- xmpp_rand_free()
- xmpp_rand()
- xmpp_rand_bytes()
- xmpp_rand_nonce()
- xmpp_rand_bytes()
- The following APIs were public in the shared library, but not in `strophe.h`.
In a future version of the library they will be private without replacement:
- xmpp_alloc()
- xmpp_realloc()
- xmpp_strdup()
- xmpp_strndup()
- xmpp_strtok_r()
- xmpp_snprintf()
- xmpp_vsnprintf()
- xmpp_log()
- xmpp_error()
- xmpp_warn()
- xmpp_info()
- xmpp_debug()
- xmpp_debug_verbose()
- Deprecated the following API:
- xmpp_conn_set_keepalive() - replaced by xmpp_conn_set_sockopt_callback()
0.11.0
- SASL EXTERNAL support (XEP-0178)
- Client certificate can be provided for TLS negotiation. If the
certificate contains a single xmppAddr and JID is not provided with
xmpp_conn_set_jid(), the xmppAddr is chosen as JID
- <stream> element contains "from" attribute over TLS connections now
- GnuTLS can be selected optionally with configure script
- Support for manual certificate verification
- New API:
- xmpp_conn_set_client_cert()
- xmpp_conn_cert_xmppaddr_num()
- xmpp_conn_cert_xmppaddr()
- xmpp_conn_set_cafile()
- xmpp_conn_set_capath()
- xmpp_conn_set_certfail_handler()
- xmpp_conn_get_peer_cert()
- xmpp_tlscert_get_ctx()
- xmpp_tlscert_get_conn()
- xmpp_tlscert_get_pem()
- xmpp_tlscert_get_dnsname()
- xmpp_tlscert_get_string()
- xmpp_tlscert_get_description()
- xmpp_tlscert_free()
0.10.1
- Fixed compilation error when LibreSSL is used
- Fixed crash when NULL is provided as password
0.10.0
- Coding style has been unified

933
Doxyfile

File diff suppressed because it is too large Load Diff

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

@@ -2,32 +2,50 @@ AUTOMAKE_OPTIONS = subdir-objects
ACLOCAL_AMFLAGS = -I m4
COVERAGE_CFLAGS=@COVERAGE_CFLAGS@
COVERAGE_LDFLAGS=@COVERAGE_LDFLAGS@
COVERAGE_PRE=@COVERAGE_PRE@
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@
else
if !TLS_WITH_SCHANNEL
SSL_CFLAGS = @openssl_CFLAGS@
SSL_LIBS = @openssl_LIBS@
endif
endif
MINGW_LIBS = @MINGW_LIBS@
RESOLV_CFLAGS = @RESOLV_CFLAGS@
RESOLV_LIBS = @RESOLV_LIBS@
WARNING_FLAGS = @WARNING_FLAGS@
STROPHE_FLAGS = -I$(top_srcdir) $(WARNING_FLAGS)
STROPHE_LIBS = libstrophe.la
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)
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_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_'
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
libstrophe_la_SOURCES = \
src/auth.c \
src/conn.c \
src/crypto.c \
src/ctx.c \
src/deprecated.c \
src/event.c \
src/handler.c \
src/hash.c \
@@ -40,9 +58,9 @@ libstrophe_la_SOURCES = \
src/sha1.c \
src/sha256.c \
src/sha512.c \
src/snprintf.c \
src/sock.c \
src/stanza.c \
src/tls.c \
src/util.c \
src/uuid.c
libstrophe_la_SOURCES += \
@@ -51,7 +69,6 @@ libstrophe_la_SOURCES += \
src/md5.h \
src/ostypes.h \
src/parser.h \
src/rand.h \
src/resolver.h \
src/sasl.h \
src/scram.h \
@@ -64,11 +81,29 @@ libstrophe_la_SOURCES += \
src/tls.h \
src/util.h
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
if TLS_WITH_GNUTLS
libstrophe_la_SOURCES += src/tls_gnutls.c
else
if TLS_WITH_SCHANNEL
libstrophe_la_SOURCES += src/tls_schannel.c
else
libstrophe_la_SOURCES += src/tls_openssl.c
endif
endif
endif
if PARSER_EXPAT
libstrophe_la_SOURCES += src/parser_expat.c
@@ -88,24 +123,32 @@ EXTRA_DIST = \
MIT-LICENSE.txt \
bootstrap.sh \
build-android.sh \
docs/footer.html \
testbuild.sh \
examples/README.md \
jni/Android.mk \
jni/Application.mk \
src/tls_gnutls.c \
src/tls_schannel.c \
m4/ax_valgrind_check.m4 \
tests/cert.pem \
tests/cert.pfx \
tests/cert.emptypass.pfx \
tests/cert.nopass.pfx \
tests/key.pem \
tests/key_encrypted.pem \
tests/res_query_dump.c
if EXAMPLES
## Examples
noinst_PROGRAMS = \
examples/active \
examples/basic \
examples/bot \
examples/complex \
examples/component \
examples/perf \
examples/register \
examples/roster \
examples/uuid \
examples/vcard \
examples/register
examples/vcard
examples_active_SOURCES = examples/active.c
examples_active_CFLAGS = $(STROPHE_FLAGS)
@@ -116,9 +159,18 @@ examples_basic_LDADD = $(STROPHE_LIBS)
examples_bot_SOURCES = examples/bot.c
examples_bot_CFLAGS = $(STROPHE_FLAGS)
examples_bot_LDADD = $(STROPHE_LIBS)
examples_complex_SOURCES = examples/complex.c
examples_complex_CFLAGS = $(STROPHE_FLAGS)
examples_complex_LDADD = $(STROPHE_LIBS)
examples_component_SOURCES = examples/component.c
examples_component_CFLAGS = $(STROPHE_FLAGS)
examples_component_LDADD = $(STROPHE_LIBS)
examples_perf_SOURCES = examples/perf.c
examples_perf_CFLAGS = $(STROPHE_FLAGS)
examples_perf_LDADD = $(STROPHE_LIBS)
examples_register_SOURCES = examples/register.c
examples_register_CFLAGS = $(STROPHE_FLAGS)
examples_register_LDADD = $(STROPHE_LIBS)
examples_roster_SOURCES = examples/roster.c
examples_roster_CFLAGS = $(STROPHE_FLAGS)
examples_roster_LDADD = $(STROPHE_LIBS)
@@ -128,31 +180,57 @@ examples_uuid_LDADD = $(STROPHE_LIBS)
examples_vcard_SOURCES = examples/vcard.c
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
examples_vcard_LDADD = $(STROPHE_LIBS)
examples_register_SOURCES = examples/register.c
examples_register_CFLAGS = $(STROPHE_FLAGS)
examples_register_LDADD = $(STROPHE_LIBS)
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_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
endif
check_PROGRAMS = $(TESTS)
if FUZZ
check_PROGRAMS += tests/test_fuzz_parser tests/test_fuzz_resolver
tests_test_fuzz_parser_SOURCES = tests/test_fuzz_parser.c
tests_test_fuzz_parser_CFLAGS = -fsanitize=fuzzer,address $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
-I$(top_srcdir)/src
tests_test_fuzz_parser_LDADD = $(STROPHE_LIBS)
tests_test_fuzz_parser_LDFLAGS = -static
tests_test_fuzz_resolver_SOURCES = tests/test_fuzz_resolver.c
tests_test_fuzz_resolver_CFLAGS = -fsanitize=fuzzer,address $(resolver_CFLAGS) $(STROPHE_FLAGS) \
-I$(top_srcdir)/src
tests_test_fuzz_resolver_LDADD = $(STROPHE_LIBS)
tests_test_fuzz_resolver_LDFLAGS = -static
endif
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
tests_check_parser_CFLAGS = $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
-I$(top_srcdir)/src
@@ -203,6 +281,16 @@ tests_test_sha512_CFLAGS = -I$(top_srcdir)/src
tests_test_md5_SOURCES = tests/test_md5.c tests/test.c src/md5.c
tests_test_md5_CFLAGS = -I$(top_srcdir)/src
tests_test_send_queue_SOURCES = tests/test_send_queue.c
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
@@ -211,12 +299,56 @@ tests_test_string_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_string_LDADD = $(STROPHE_LIBS)
tests_test_string_LDFLAGS = -static
tests_test_stanza_SOURCES = tests/test_stanza.c
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS)
tests_test_stanza_SOURCES = tests/test_stanza.c tests/test.h
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_stanza_LDADD = $(STROPHE_LIBS)
tests_test_stanza_LDFLAGS = -static
tests_test_xmppaddr_SOURCES = tests/test_xmppaddr.c
tests_test_xmppaddr_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_xmppaddr_LDADD = $(STROPHE_LIBS)
tests_test_xmppaddr_LDFLAGS = -static
format:
@echo " * run clang-format on all sources"
@dos2unix -q src/*.[ch] *.h tests/*.[ch] examples/*.c
@clang-format -i src/*.[ch] *.h tests/*.[ch] examples/*.c
@dos2unix -k -q $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
@clang-format -i $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
if COVERAGE
MOSTLYCLEANFILES = src/*.gcno src/*.gcda coverage.info
clean-local:
-rm -rf coverage/
coverage: check
@lcov --capture --no-external --directory src -q --output-file coverage.info
@genhtml coverage.info --output-directory coverage -q
endif
release: test-release-all
dist-archives:
$(MAKE) dist
$(MAKE) dist-bzip2
$(MAKE) dist-xz
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
test-release-all: dist-archives
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
tar xJf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.xz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
tar xjf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
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
docs:
@SRCDIR=$(top_srcdir) doxygen -q $(top_srcdir)/Doxyfile
@VALGRIND_CHECK_RULES@
.PHONY: docs test-release

View File

@@ -1,11 +1,11 @@
libstrophe [![Build Status](https://travis-ci.org/strophe/libstrophe.png?branch=master)](https://travis-ci.org/strophe/libstrophe)
libstrophe [![Build Status](https://github.com/strophe/libstrophe/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/strophe/libstrophe/actions/workflows/main.yml?query=branch%3Amaster+++)
==========
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
------------------
@@ -42,17 +42,30 @@ another path use the `--prefix` option during configure, e.g.:
Run script `build-android.sh` and follow the instructions. You will
need expat sources and android-ndk.
### Code Coverage
If you want to create a code coverage report, run:
./configure --enable-coverage
make coverage
The coverage report can be found in `./coverage/index.html`.
Requirements
------------
libstrophe requires:
- expat or libxml2 - expat is the default; use --with-libxml2 to
- expat or libxml2 - expat is the default; use `--with-libxml2` to
switch
- openssl on UNIX systems
- openssl or GnuTLS on UNIX systems - openssl is default; use
`--with-gnutls` to switch
To build libstrophe using autotools you will need autoconf, automake,
libtool and pkg-config.
To build libstrophe using autotools you will need `autoconf`,
`automake`, `libtool` and `pkg-config`.
To run code coverage analysis you will need `gcov` and `lcov`.
Installation
------------
@@ -69,4 +82,17 @@ by running:
doxygen
or if you have everything configured properly:
make docs
Then open `docs/html/index.html`.
An online version of the documentation of the latest release is available on https://strophe.im/libstrophe/
Releases
--------
Releases are signed with the GPG key with ID `F8ADC1F9A68A7AFF0E2C89E4391A5EFC2D1709DE`.
It can be found e.g. on https://keys.openpgp.org/

View File

@@ -8,6 +8,7 @@ BinPackParameters: 'false'
BreakBeforeBraces: Linux
ColumnLimit: '80'
DerivePointerAlignment: 'false'
IndentGotoLabels: false
IndentWidth: '4'
PointerAlignment: Right
SortIncludes: 'false'

View File

@@ -1,4 +1,13 @@
AC_INIT([libstrophe], [0.10.0], [jack@metajack.im])
m4_define([v_maj], [0])
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))
m4_define([lt_rev], v_patch)
m4_define([lt_age], v_min)
AC_INIT([libstrophe], [project_version], [jack@metajack.im])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign])
LT_INIT([dlopen])
@@ -9,13 +18,16 @@ 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"],
[*haiku*], [PLATFORM="haiku"],
[*mingw*], [PLATFORM="win32"
MINGW_LIBS="-lws2_32"],
[PLATFORM="nix"])
WARNING_FLAGS="-Wall"
@@ -24,18 +36,111 @@ AS_CASE([$PLATFORM],
[haiku], [],
[WARNING_FLAGS="$WARNING_FLAGS -Wextra"])
AC_ARG_ENABLE([examples],
[AS_HELP_STRING([--disable-examples], [turn off examples])],
[case "${enableval}" in yes) examples=true ;; no) examples=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --disable-examples]) ;; esac],[examples=true])
AM_CONDITIONAL([EXAMPLES], [test x$examples = xtrue])
AC_ARG_WITH([libxml2],
[AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing, expat is the default])])
AC_ARG_WITH([gnutls],
[AS_HELP_STRING([--with-gnutls], [use GnuTLS for TLS support, OpenSSL is the default])])
AC_ARG_WITH([schannel],
[AS_HELP_STRING([--with-schannel], [use Windows Schannel for TLS support, OpenSSL is the default])])
AC_ARG_ENABLE([tls],
[AS_HELP_STRING([--disable-tls], [disable TLS support])])
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])],
[case "${enableval}" in yes) fuzzing=true ;; no) fuzzing=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-fuzzing]) ;; esac],[fuzzing=false])
AM_CONDITIONAL([FUZZ], [test x$fuzzing = xtrue])
if test "x$enable_fuzzing" = "xyes" ; then
if test "x$CC" != "xclang" ; then
AC_MSG_ERROR(["You need to set CC=clang to use --enable-fuzzing, used $CC"])
fi
fi
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage], [turn on coverage for tests])],
[case "${enableval}" in yes) coverage=true ;; no) coverage=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;; esac],[coverage=false])
AM_CONDITIONAL([COVERAGE], [test x$coverage = xtrue])
if test "$enable_coverage" = "yes"; then
COVERAGE_CFLAGS="--coverage -g"
COVERAGE_LDFLAGS="--coverage -lgcov"
COVERAGE_PRE="-Wl,--whole-archive"
COVERAGE_POST="-Wl,--no-whole-archive"
else
COVERAGE_CFLAGS=""
COVERAGE_LDFLAGS=""
COVERAGE_PRE=""
COVERAGE_POST=""
fi
m4_include([m4/ax_valgrind_check.m4])
AX_VALGRIND_DFLT([drd], [off])
AX_VALGRIND_DFLT([helgrind], [off])
AX_VALGRIND_DFLT([sgcheck], [off])
AX_VALGRIND_CHECK
AC_SEARCH_LIBS([clock_gettime], [rt], AC_DEFINE(HAVE_CLOCK_GETTIME,1))
AC_SEARCH_LIBS([socket], [network socket])
AC_CHECK_FUNCS([snprintf vsnprintf])
AC_CHECK_DECLS([va_copy], [], [], [#include <stdarg.h>])
AC_CHECK_FUNCS([snprintf vsnprintf], [], [have_snprintf=no])
if test "x$enable_tls" != xno; then
dnl Checking for va_copy availability
AC_MSG_CHECKING([for va_copy])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
va_list ap1,ap2;]], [[va_copy(ap1,ap2);]])],
have_va_copy=yes,
have_va_copy=no)
AC_MSG_RESULT($have_va_copy)
if test x"$have_va_copy" = x"yes"; then
AC_DEFINE(HAVE_VA_COPY,1,[Whether va_copy() is available])
else
AC_MSG_CHECKING([for __va_copy])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
va_list ap1,ap2;]], [[__va_copy(ap1,ap2);]])],
have___va_copy=yes,
have___va_copy=no)
AC_MSG_RESULT($have___va_copy)
if test x"$have___va_copy" = x"yes"; then
AC_DEFINE(HAVE___VA_COPY,1,[Whether __va_copy() is available])
fi
fi
if test x"$have_va_copy$have___va_copy" = x"nono"; then
dnl Checking whether va_list is an array type
AC_MSG_CHECKING([whether va_list is an array type])
AC_TRY_COMPILE2([
#include <stdarg.h>
void a(va_list * ap) {}],[
va_list ap1, ap2; a(&ap1); ap2 = (va_list) ap1],[
AC_MSG_RESULT(no)],[
AC_MSG_RESULT(yes)
AC_DEFINE([VA_LIST_IS_ARRAY], [1],[Define if va_list is an array type])])
fi
if test "x$enable_tls" != xno -a "x$with_gnutls" = xyes; then
PKG_CHECK_MODULES([gnutls], [gnutls],
[PC_REQUIRES="gnutls ${PC_REQUIRES}"],
[AC_CHECK_HEADER([gnutls/gnutls.h],
[
gnutls_LIBS="-lgnutls"
PC_LIBS="${gnutls_LIBS} ${PC_LIBS}"
],
[AC_MSG_ERROR([gnutls not found; gnutls required])]
)])
elif test "x$enable_tls" != xno -a "x$with_schannel" = xyes; then
if test "x$PLATFORM" != xwin32; then
AC_MSG_ERROR([schannel is only supported on Windows])
fi
elif test "x$enable_tls" != xno; then
PKG_CHECK_MODULES([openssl], [openssl],
[PC_REQUIRES="openssl ${PC_REQUIRES}"],
[AC_CHECK_HEADER([openssl/ssl.h],
@@ -47,6 +152,12 @@ if test "x$enable_tls" != xno; then
)])
fi
AC_CHECK_FUNCS([getrandom], [], [enable_getrandom=no])
if test "$enable_getrandom" = "no"; then
AC_DEFINE(DONT_USE_GETRANDOM)
AC_MSG_NOTICE([libstrophe will not use the getrandom() system call])
fi
with_parser=""
if test "x$with_libxml2" != xyes; then
PKG_CHECK_MODULES([expat], [expat >= 2.0.0],
@@ -113,8 +224,10 @@ AS_CASE([$PLATFORM],
[solaris], [RESOLV_LIBS="-lresolv -lsocket -lnsl"],
[android], [RESOLV_LIBS=""],
[haiku], [RESOLV_LIBS="-lnetwork"],
[win32], [RESOLV_LIBS=""],
[RESOLV_LIBS="-lresolv"])
if test "x$PLATFORM" != xwin32; then
LIBS_TMP="${LIBS}"
LIBS="${RESOLV_LIBS}"
AC_LINK_IFELSE([AC_LANG_SOURCE([
@@ -133,9 +246,22 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
[AC_MSG_ERROR([res_query() not found with LIBS="${LIBS}"])])
LIBS="${LIBS_TMP}"
PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}"
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],
@@ -144,16 +270,43 @@ 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])
# define while compiling
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MAJOR, [v_maj], [Major version])
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MINOR, [v_min], [Minor version])
VMAJ=v_maj
VMIN=v_min
AC_SUBST(VMAJ)
AC_SUBST(VMIN)
# define in package-config file
PC_CFLAGS="${PC_CFLAGS} -DLIBXMPP_VERSION_MAJOR=${VMAJ} -DLIBXMPP_VERSION_MINOR=${VMIN}"
# set the SO version of the installed library
VERSION_INFO="lt_cur:lt_rev:lt_age"
AC_SUBST([VERSION_INFO])
AC_SUBST([PC_REQUIRES], [${PC_REQUIRES}])
AC_SUBST([PC_CFLAGS], [${PC_CFLAGS}])
AC_SUBST([PC_LIBS], [${PC_LIBS}])
AC_SUBST(COVERAGE_CFLAGS)
AC_SUBST(COVERAGE_LDFLAGS)
AC_SUBST(COVERAGE_PRE)
AC_SUBST(COVERAGE_POST)
AC_SUBST(MINGW_LIBS)
AC_SUBST(PARSER_CFLAGS)
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

0
docs/.keep Normal file
View File

View File

@@ -1,2 +0,0 @@
</body>
</html>

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
@@ -19,9 +20,7 @@
#include <strophe.h>
int handle_reply(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *query, *item;
const char *type;
@@ -46,11 +45,11 @@ int handle_reply(xmpp_conn_t *const conn,
return 0;
}
void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *iq, *query;

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,36 +7,29 @@
** 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>
#include <stdlib.h>
#include <string.h>
#include <strophe.h>
/* hardcoded TCP keepalive timeout and interval */
#define KA_TIMEOUT 60
#define KA_INTERVAL 1
/* define a handler for connection events */
void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
static void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
int secured;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
secured = xmpp_conn_is_secured(conn);
fprintf(stderr, "DEBUG: connection is %s.\n",
secured ? "secured" : "NOT secured");
xmpp_disconnect(conn);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
@@ -43,20 +37,39 @@ void conn_handler(xmpp_conn_t *const conn,
}
}
static void usage(int exit_code)
{
fprintf(stderr,
"Usage: basic [options] [<host> [<port>]]\n\n"
"Options:\n"
" --jid <jid> The JID to use to authenticate.\n"
" --pass <pass> The password of the JID.\n"
" --disable-tls Disable TLS.\n"
" --mandatory-tls Deny plaintext connection.\n"
" --trust-tls Trust TLS certificate.\n"
" --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
"Note: --disable-tls conflicts with --mandatory-tls or "
"--legacy-ssl\n");
exit(exit_code);
}
int main(int argc, char **argv)
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
char *jid, *pass, *host = NULL;
const char *cafile = NULL;
char *jid = NULL, *password = NULL, *host = NULL;
long flags = 0;
int tcp_keepalive = 0;
int i;
unsigned long port = 0;
/* take a jid and password on the command line */
for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--disable-tls") == 0)
if (strcmp(argv[i], "--help") == 0)
usage(0);
else if (strcmp(argv[i], "--disable-tls") == 0)
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
else if (strcmp(argv[i], "--mandatory-tls") == 0)
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
@@ -66,32 +79,21 @@ 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], "--tcp-keepalive") == 0)
tcp_keepalive = 1;
else if (strncmp(argv[i], "--cafile=", strlen("--cafile=")) == 0)
cafile = argv[i] + strlen("--cafile=");
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
jid = argv[i];
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
password = argv[i];
else
break;
}
if ((argc - i) < 2 || (argc - i) > 3) {
fprintf(stderr, "Usage: basic [options] <jid> <pass> [<host>]\n\n"
"Options:\n"
" --cafile=<PATH> CAfile for cert verification.\n"
" --disable-tls Disable TLS.\n"
" --mandatory-tls Deny plaintext connection.\n"
" --trust-tls Trust TLS certificate.\n"
" --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
" --tcp-keepalive Configure TCP keepalive.\n\n"
"Note: --disable-tls conflicts with --mandatory-tls or "
"--legacy-ssl\n");
return 1;
if ((!jid) || (argc - i) > 2) {
usage(1);
}
jid = argv[i];
pass = argv[i + 1];
if (i + 2 < argc)
host = argv[i + 2];
if (i < argc)
host = argv[i];
if (i + 1 < argc)
port = strtoul(argv[i + 1], NULL, 0);
/*
* Note, this example doesn't handle errors. Applications should check
@@ -111,24 +113,20 @@ int main(int argc, char **argv)
/* configure connection properties (optional) */
xmpp_conn_set_flags(conn, flags);
/* configure TCP keepalive (optional) */
if (tcp_keepalive)
xmpp_conn_set_keepalive(conn, KA_TIMEOUT, KA_INTERVAL);
/* add external CAfile if needed */
if (cafile)
xmpp_conn_tls_cafile(conn, cafile);
/* setup authentication information */
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
if (jid)
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_pass(conn, password);
/* initiate connection */
xmpp_connect_client(conn, host, 0, conn_handler, ctx);
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
}
/* release our connection and context */
xmpp_conn_release(conn);

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
@@ -19,12 +20,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef _WIN32
#include <conio.h>
#include <ctype.h>
#endif
#include <strophe.h>
int version_handler(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int reconnect;
int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *reply, *query, *name, *version, *text;
const char *ns;
@@ -70,15 +77,19 @@ int version_handler(xmpp_conn_t *const conn,
return 1;
}
int message_handler(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
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)
@@ -98,7 +109,11 @@ int message_handler(xmpp_conn_t *const conn,
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;
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
} else {
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
strcpy(replytext, intext);
@@ -111,18 +126,15 @@ int message_handler(xmpp_conn_t *const conn,
xmpp_stanza_release(reply);
free(replytext);
if (quit)
xmpp_disconnect(conn);
return 1;
}
/* define a handler for connection events */
void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
@@ -146,21 +158,123 @@ void conn_handler(xmpp_conn_t *const conn,
}
}
#ifdef _WIN32
static char *getpassword(const char *prompt, size_t maxlen)
{
char *b, *buffer = malloc(maxlen);
size_t i = 0;
b = buffer;
fputs(prompt, stderr);
for (i = 0; i < maxlen; i++, b++) {
char c = _getch();
if (c == '\r' || c == '\n')
break;
*b = c;
}
*b = '\0';
fputs("\n", stderr);
return buffer;
}
#else
#define getpassword(prompt, maxlen) getpass(prompt)
#endif
static int
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
char *pass = getpassword("Please enter password: ", pw_max);
if (!pass)
return -1;
size_t passlen = strlen(pass);
int ret;
if (passlen >= pw_max) {
ret = -1;
goto out;
}
ret = passlen + 1;
memcpy(pw, pass, ret);
out:
memset(pass, 0, passlen);
return ret;
}
static void usage(int exit_code)
{
fprintf(stderr,
"Usage: bot [options] <jid> <pass>\n\n"
"Options:\n"
" --jid <jid> The JID to use to authenticate.\n"
" --pass <pass> The password of the JID.\n"
" --tls-cert <cert> Path to client certificate.\n"
" --tls-key <key> Path to private key or P12 file.\n\n"
" --tcp-keepalive Configure TCP keepalive.\n"
" --disable-tls Disable TLS.\n"
" --mandatory-tls Deny plaintext connection.\n"
" --trust-tls Trust TLS certificate.\n"
" --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
"Note: --disable-tls conflicts with --mandatory-tls or "
"--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);
}
int main(int argc, char **argv)
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
char *jid, *pass;
xmpp_sm_state_t *sm_state = NULL;
char *jid = NULL, *password = NULL, *host = NULL, *cert = NULL, *key = NULL;
long flags = 0;
int i, tcp_keepalive = 0;
unsigned long port = 0;
/* take a jid and password on the command line */
if (argc != 3) {
fprintf(stderr, "Usage: bot <jid> <pass>\n\n");
return 1;
for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--help") == 0)
usage(0);
else if (strcmp(argv[i], "--disable-tls") == 0)
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
else if (strcmp(argv[i], "--mandatory-tls") == 0)
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
else if (strcmp(argv[i], "--trust-tls") == 0)
flags |= XMPP_CONN_FLAG_TRUST_TLS;
else if (strcmp(argv[i], "--legacy-ssl") == 0)
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))
password = argv[i];
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
cert = argv[i];
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
key = argv[i];
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
tcp_keepalive = 1;
else
break;
}
if ((!jid && !key) || (argc - i) > 2) {
usage(1);
}
jid = argv[1];
pass = argv[2];
if (i < argc)
host = argv[i];
if (i + 1 < argc)
port = strtoul(argv[i + 1], NULL, 0);
/* init library */
xmpp_initialize();
@@ -170,29 +284,55 @@ int main(int argc, char **argv)
/* create a context */
ctx = xmpp_ctx_new(NULL, log);
create_connection:
reconnect = 0;
/* create a connection */
conn = xmpp_conn_new(ctx);
/*
* also you can disable TLS support or force legacy SSL
* connection without STARTTLS
*
* see xmpp_conn_set_flags() or examples/basic.c
*/
/* configure connection properties (optional) */
xmpp_conn_set_flags(conn, flags);
/* ask for a password if key is protected */
xmpp_conn_set_password_callback(conn, password_callback, NULL);
/* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_password_retries(conn, 3);
/* setup authentication information */
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
if (key)
xmpp_conn_set_client_cert(conn, cert, key);
if (jid)
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_pass(conn, password);
/* enable TCP keepalive, using canned callback function */
if (tcp_keepalive)
xmpp_conn_set_sockopt_callback(conn, xmpp_sockopt_cb_keepalive);
/* set Stream-Mangement state if available */
if (sm_state) {
xmpp_conn_set_sm_state(conn, sm_state);
sm_state = NULL;
}
/* initiate connection */
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
}
/* release our connection and context */
/* save the Stream-Mangement state if we should re-connect */
if (reconnect)
sm_state = xmpp_conn_get_sm_state(conn);
/* release our connection */
xmpp_conn_release(conn);
if (reconnect)
goto create_connection;
/* release our context */
xmpp_ctx_free(ctx);
/* final shutdown of the library */

372
examples/complex.c Normal file
View File

@@ -0,0 +1,372 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* complex.c
** libstrophe XMPP client library -- more complex usage example
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef _WIN32
#include <conio.h>
#include <ctype.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <mstcpip.h> /* tcp_keepalive */
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif
#include <strophe.h>
/* hardcoded TCP keepalive timeout and interval */
#define KA_TIMEOUT 60
#define KA_INTERVAL 30
#define KA_COUNT 3
#define USER_TIMEOUT 150
static void print_tlscert(const xmpp_tlscert_t *cert)
{
const char *name;
size_t n;
for (n = 0; n < (unsigned)XMPP_CERT_ELEMENT_MAX; ++n) {
printf("\t%32s: %s\n", xmpp_tlscert_get_description(n),
xmpp_tlscert_get_string(cert, n));
}
n = 0;
while ((name = xmpp_tlscert_get_dnsname(cert, n++)) != NULL)
printf("\t%32s: %s\n", "dnsName", name);
printf("PEM:\n%s\n", xmpp_tlscert_get_pem(cert));
}
/* define a handler for connection events */
static void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
int secured;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
secured = xmpp_conn_is_secured(conn);
fprintf(stderr, "DEBUG: connection is %s.\n",
secured ? "secured" : "NOT secured");
if (secured) {
xmpp_tlscert_t *cert = xmpp_conn_get_peer_cert(conn);
print_tlscert(cert);
xmpp_tlscert_free(cert);
}
xmpp_disconnect(conn);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
}
static int certfail_handler(const xmpp_tlscert_t *cert,
const char *const errormsg)
{
char read_char[16] = {0};
printf("Received certificate can't be validated!\n");
printf("Reason: %s\n", errormsg);
print_tlscert(cert);
printf("Do you agree to connect?\n[y(es)|n(o)]: ");
fflush(stdout);
if (fgets(read_char, sizeof(read_char), stdin) == NULL) {
printf("fgets() failed\n");
return 0;
}
printf("\n");
return read_char[0] == 'y' || read_char[0] == 'Y';
}
#ifdef _WIN32
static char *getpassword(const char *prompt, size_t maxlen)
{
char *b, *buffer = malloc(maxlen);
size_t i = 0;
b = buffer;
fputs(prompt, stderr);
for (i = 0; i < maxlen; i++, b++) {
char c = _getch();
if (c == '\r' || c == '\n')
break;
*b = c;
}
*b = '\0';
fputs("\n", stderr);
return buffer;
}
#else
#define getpassword(prompt, maxlen) getpass(prompt)
#endif
static int
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
char *pass = getpassword("Please enter password: ", pw_max);
if (!pass)
return -1;
size_t passlen = strlen(pass);
int ret;
if (passlen >= pw_max) {
ret = -1;
goto out;
}
ret = passlen + 1;
memcpy(pw, pass, ret);
out:
memset(pass, 0, passlen);
return ret;
}
static int sockopt_cb(xmpp_conn_t *conn, void *socket)
{
int timeout = KA_TIMEOUT;
int interval = KA_INTERVAL;
int count = KA_COUNT;
unsigned int user_timeout = USER_TIMEOUT;
int ret;
int optval = (timeout && interval) ? 1 : 0;
(void)conn;
#ifdef _WIN32
(void)count;
(void)user_timeout;
SOCKET sock = *((SOCKET *)socket);
struct tcp_keepalive ka;
DWORD dw = 0;
ka.onoff = optval;
ka.keepalivetime = timeout * 1000;
ka.keepaliveinterval = interval * 1000;
ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
NULL, NULL);
#else
int sock = *((int *)socket);
fprintf(stderr, "DEBUG: setting socket options\n");
ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
if (ret < 0)
return ret;
if (optval) {
#ifdef TCP_KEEPIDLE
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
sizeof(timeout));
#elif defined(TCP_KEEPALIVE)
/* QNX receives `struct timeval' as argument, but it seems OSX does int
*/
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
sizeof(timeout));
#endif /* TCP_KEEPIDLE */
if (ret < 0)
return ret;
#ifdef TCP_KEEPINTVL
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
sizeof(interval));
if (ret < 0)
return ret;
#endif /* TCP_KEEPINTVL */
}
if (count) {
#ifdef TCP_KEEPCNT
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
if (ret < 0)
return ret;
#endif /* TCP_KEEPCNT */
}
if (user_timeout) {
#ifdef TCP_USER_TIMEOUT
ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
sizeof(user_timeout));
if (ret < 0)
return ret;
#elif defined(TCP_RXT_CONNDROPTIME)
int rxt = user_timeout / 1000;
ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
sizeof(rxt));
if (ret < 0)
return ret;
#endif /* TCP_USER_TIMEOUT */
}
#endif /* _WIN32 */
return ret;
}
static void usage(int exit_code)
{
fprintf(stderr,
"Usage: complex [options] [<host> [<port>]]\n\n"
"Options:\n"
" --jid <jid> The JID to use to authenticate.\n"
" --pass <pass> The password of the JID.\n"
" --tls-cert <cert> Path to client certificate.\n"
" --tls-key <key> Path to private key or P12 file.\n\n"
" --capath <path> Path to an additional CA trust store "
"(directory).\n"
" --cafile <path> Path to an additional CA trust store "
"(single file).\n"
" --disable-tls Disable TLS.\n"
" --mandatory-tls Deny plaintext connection.\n"
" --trust-tls Trust TLS certificate.\n"
" --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 "
"--legacy-ssl\n");
exit(exit_code);
}
int main(int argc, char **argv)
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
char *jid = NULL, *password = NULL, *cert = NULL, *key = NULL, *host = NULL,
*capath = NULL, *cafile = NULL;
long flags = 0;
int tcp_keepalive = 0, verbosity = 0, certfail = 0;
int i;
unsigned long port = 0;
/* take a jid and password on the command line */
for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--help") == 0)
usage(0);
else if (strcmp(argv[i], "--disable-tls") == 0)
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
else if (strcmp(argv[i], "--mandatory-tls") == 0)
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
else if (strcmp(argv[i], "--trust-tls") == 0)
flags |= XMPP_CONN_FLAG_TRUST_TLS;
else if (strcmp(argv[i], "--legacy-ssl") == 0)
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)
tcp_keepalive = 1;
else if (strcmp(argv[i], "--enable-certfail") == 0)
certfail = 1;
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
jid = argv[i];
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
password = argv[i];
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
cert = argv[i];
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
key = argv[i];
else if ((strcmp(argv[i], "--capath") == 0) && (++i < argc))
capath = argv[i];
else if ((strcmp(argv[i], "--cafile") == 0) && (++i < argc))
cafile = argv[i];
else
break;
}
if ((!jid && !key) || (argc - i) > 2) {
usage(1);
}
if (i < argc)
host = argv[i];
if (i + 1 < argc)
port = strtoul(argv[i + 1], NULL, 0);
/*
* Note, this example doesn't handle errors. Applications should check
* return values of non-void functions.
*/
/* init library */
xmpp_initialize();
/* pass NULL instead to silence output */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */
ctx = xmpp_ctx_new(NULL, log);
xmpp_ctx_set_verbosity(ctx, verbosity);
/* create a connection */
conn = xmpp_conn_new(ctx);
/* configure connection properties (optional) */
xmpp_conn_set_flags(conn, flags);
/* configure TCP keepalive (optional) */
if (tcp_keepalive)
xmpp_conn_set_sockopt_callback(conn, sockopt_cb);
/* ask for a password if key is protected */
xmpp_conn_set_password_callback(conn, password_callback, NULL);
/* try at max 3 times in case the user enters the password wrong */
xmpp_conn_set_password_retries(conn, 3);
/* setup authentication information */
if (key) {
xmpp_conn_set_client_cert(conn, cert, key);
}
if (jid)
xmpp_conn_set_jid(conn, jid);
if (password)
xmpp_conn_set_pass(conn, password);
if (certfail)
xmpp_conn_set_certfail_handler(conn, certfail_handler);
if (capath)
xmpp_conn_set_capath(conn, capath);
if (cafile)
xmpp_conn_set_cafile(conn, cafile);
/* initiate connection */
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
}
/* release our connection and context */
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
/* final shutdown of the library */
xmpp_shutdown();
return 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
@@ -20,11 +21,11 @@
#include <strophe.h>
/* define a handler for connection events */
void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;

190
examples/perf.c Normal file
View File

@@ -0,0 +1,190 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* perf.c
* strophe XMPP client library -- performance measure
*
* Copyright (C) 2022 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
* performance measure
*
* Timing code shamelessly borrowed from libtomcrypt/demos/timing.c
*/
#include <strophe.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
static void init_timer(void);
static void t_start(void);
static uint64_t t_read(void);
static const char *perf_time_unit;
#define NUM_SAMPLES 1000u
static void perf_rand(xmpp_ctx_t *ctx)
{
xmpp_rand_t *rng = xmpp_rand_new(ctx);
uint64_t t1, t2, t3 = 0;
unsigned int n;
const size_t alloc_sz = 0x1000u;
size_t sz;
unsigned char *buf = malloc(alloc_sz);
/* pre-heat */
for (n = 1; n < 4; ++n) {
xmpp_rand_bytes(rng, buf, alloc_sz / n);
}
for (sz = 2; sz <= alloc_sz; sz <<= 1) {
t2 = 0;
for (n = 0; n < NUM_SAMPLES; ++n) {
t_start();
t1 = t_read();
xmpp_rand_bytes(rng, buf, sz);
t1 = t_read() - t1;
t2 += t1;
}
t2 /= NUM_SAMPLES;
fprintf(stderr, "Reading %6zu bytes from PRNG took %8" PRIu64 " %s\n",
sz, t2, perf_time_unit);
if (t3) {
double d3 = (double)t3, d2 = (double)t2;
fprintf(stderr, " +%.2f%%\n",
(d2 - d3) / ((d2 + d3) * 0.5) * 100.);
}
t3 = t2;
}
free(buf);
xmpp_rand_free(ctx, rng);
}
int main()
{
/* pass NULL instead to silence output */
xmpp_log_t *log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */
xmpp_ctx_t *ctx = xmpp_ctx_new(NULL, log);
init_timer();
perf_rand(ctx);
return 0;
}
#define TIMES 100000
static uint64_t timer, skew = 0;
/* RDTSC from Scott Duplichan */
static uint64_t rdtsc(void)
{
#if defined __GNUC__
#if defined(__i386__) || defined(__x86_64__)
/* version from http://www.mcs.anl.gov/~kazutomo/rdtsc.html
* the old code always got a warning issued by gcc, clang did not
* complain...
*/
unsigned hi, lo;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return ((uint64_t)lo) | (((uint64_t)hi) << 32);
#elif defined(__POWERPC__)
unsigned long a, b;
__asm__ __volatile__("mftbu %1 \nmftb %0\n" : "=r"(a), "=r"(b));
return (((uint64_t)b) << 32ULL) | ((uint64_t)a);
#elif defined(__ia64__) /* gcc-IA64 version */
unsigned long result;
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
while (__builtin_expect((int)result == -1, 0))
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
return result;
#elif defined(__sparc__)
#if defined(__arch64__)
uint64_t a;
asm volatile("rd %%tick,%0" : "=r"(a));
return a;
#else
register unsigned long x, y;
__asm__ __volatile__("rd %%tick, %0; clruw %0, %1; srlx %0, 32, %0"
: "=r"(x), "=r"(y)
: "0"(x), "1"(y));
return ((unsigned long long)x << 32) | y;
#endif
#elif defined(__aarch64__)
uint64_t CNTVCT_EL0;
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(CNTVCT_EL0));
return CNTVCT_EL0;
#elif defined HAVE_CLOCK_GETTIME
#define USE_CLOCK_GETTIME
struct timespec result;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
return result.tv_sec * 1000000000 + result.tv_nsec;
#else
return clock();
#endif /* __GNUC__ */
/* Microsoft and Intel Windows compilers */
#elif defined _M_IX86
__asm rdtsc
#elif defined _M_AMD64
return __rdtsc();
#elif defined _M_IA64
#if defined __INTEL_COMPILER
#include <ia64intrin.h>
#endif
return __getReg(3116);
#elif defined HAVE_CLOCK_GETTIME
#define USE_CLOCK_GETTIME
struct timespec result;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
return result.tv_sec * 1000000000 + result.tv_nsec;
#else
return clock();
#endif
}
#if defined USE_CLOCK_GETTIME
static const char *perf_time_unit = "ns";
#else
static const char *perf_time_unit = "cycles";
#endif
static void t_start(void)
{
timer = rdtsc();
}
static uint64_t t_read(void)
{
return rdtsc() - timer;
}
static void init_timer(void)
{
uint64_t c1, c2, t1, t2;
unsigned long y1;
c1 = c2 = (uint64_t)-1;
for (y1 = 0; y1 < TIMES * 100; y1++) {
t_start();
t1 = t_read();
t2 = (t_read() - t1) >> 1;
c1 = (t1 > c1) ? t1 : c1;
c2 = (t2 > c2) ? t2 : c2;
}
skew = c2 - c1;
fprintf(stderr, "Clock Skew: %lu\n", (unsigned long)skew);
}

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.
*/
/*
@@ -102,9 +103,7 @@ iq_reg_send_form(xmpp_reg_t *reg, xmpp_conn_t *conn, xmpp_stanza_t *stanza)
}
}
static int iq_reg2_cb(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int iq_reg2_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
const char *type;
@@ -129,9 +128,7 @@ quit:
return 0;
}
static int iq_reg_cb(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int iq_reg_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
xmpp_stanza_t *registered = NULL;
@@ -166,9 +163,8 @@ quit:
return 0;
}
static int _handle_error(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int
_handle_error(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
(void)stanza;
(void)userdata;
@@ -179,9 +175,9 @@ static int _handle_error(xmpp_conn_t *const conn,
return 0;
}
static int _handle_proceedtls_default(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int _handle_proceedtls_default(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata)
{
const char *name = xmpp_stanza_get_name(stanza);
@@ -201,8 +197,7 @@ static int _handle_proceedtls_default(xmpp_conn_t *const conn,
return 0;
}
static int _handle_missing_features(xmpp_conn_t *const conn,
void *const userdata)
static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
@@ -212,9 +207,8 @@ static int _handle_missing_features(xmpp_conn_t *const conn,
return 0;
}
static int _handle_features(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
xmpp_ctx_t *ctx = reg->ctx;
@@ -265,11 +259,11 @@ static int _handle_features(xmpp_conn_t *const conn,
return 0;
}
static void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
static void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
int secured;

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
@@ -18,9 +19,7 @@
#include <strophe.h>
int handle_reply(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *query, *item;
const char *type, *name;
@@ -51,11 +50,11 @@ int handle_reply(xmpp_conn_t *const conn,
return 0;
}
void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *iq, *query;

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>
@@ -160,7 +161,7 @@ exit:
return cb;
}
static int timedout(xmpp_conn_t *const conn, void *const userdata)
static int timedout(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
@@ -170,9 +171,7 @@ static int timedout(xmpp_conn_t *const conn, void *const userdata)
return 0;
}
static int recv_vcard(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata)
static int recv_vcard(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
vcard_t *vc = userdata;
vcard_cb_t cb;
@@ -217,11 +216,11 @@ static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id)
xmpp_conn_get_bound_jid(conn), to, id);
}
static void conn_handler(xmpp_conn_t *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata)
static void conn_handler(xmpp_conn_t *conn,
xmpp_conn_event_t status,
int error,
xmpp_stream_error_t *stream_error,
void *userdata)
{
vcard_t *vc = userdata;

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@

28
m4/ac_try_compile2.m4 Normal file
View File

@@ -0,0 +1,28 @@
dnl Like AC_TRY_EVAL but also errors out if the compiler generates
dnl _any_ output. Some compilers might issue warnings which we want
dnl to catch.
AC_DEFUN([AC_TRY_EVAL2],
[{ (eval echo configure:__oline__: \"[$]$1\") 1>&AS_MESSAGE_LOG_FD; dnl
(eval [$]$1) 2>&AS_MESSAGE_LOG_FD; _out=`eval [$]$1 2>&1` && test "x$_out" = x; }])
dnl Like AC_TRY_COMPILE but calls AC_TRY_EVAL2 instead of AC_TRY_EVAL
AC_DEFUN([AC_TRY_COMPILE2],
[cat > conftest.$ac_ext <<EOF
[#]line __oline__ "configure"
#include "confdefs.h"
[$1]
int main(void) {
[$2]
; return 0; }
EOF
if AC_TRY_EVAL2(ac_compile); then
ifelse([$3], , :, [rm -rf conftest*
$3])
else
echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
cat conftest.$ac_ext >&AS_MESSAGE_LOG_FD
ifelse([$4], , , [ rm -rf conftest*
$4
])dnl
fi
rm -f conftest*])

239
m4/ax_valgrind_check.m4 Normal file
View File

@@ -0,0 +1,239 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off)
# AX_VALGRIND_CHECK()
#
# DESCRIPTION
#
# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows
# running `make check` under a variety of Valgrind tools to check for
# memory and threading errors.
#
# Defines VALGRIND_CHECK_RULES which should be substituted in your
# Makefile; and $enable_valgrind which can be used in subsequent configure
# output. VALGRIND_ENABLED is defined and substituted, and corresponds to
# the value of the --enable-valgrind option, which defaults to being
# enabled if Valgrind is installed and disabled otherwise. Individual
# Valgrind tools can be disabled via --disable-valgrind-<tool>, the
# default is configurable via the AX_VALGRIND_DFLT command or is to use
# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT
# calls must be made before the call to AX_VALGRIND_CHECK.
#
# If unit tests are written using a shell script and automake's
# LOG_COMPILER system, the $(VALGRIND) variable can be used within the
# shell scripts to enable Valgrind, as described here:
#
# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html
#
# Usage example:
#
# configure.ac:
#
# AX_VALGRIND_DFLT([sgcheck], [off])
# AX_VALGRIND_CHECK
#
# in each Makefile.am with tests:
#
# @VALGRIND_CHECK_RULES@
# VALGRIND_SUPPRESSIONS_FILES = my-project.supp
# EXTRA_DIST = my-project.supp
#
# This results in a "check-valgrind" rule being added. Running `make
# check-valgrind` in that directory will recursively run the module's test
# suite (`make check`) once for each of the available Valgrind tools (out
# of memcheck, helgrind and drd) while the sgcheck will be skipped unless
# enabled again on the commandline with --enable-valgrind-sgcheck. The
# results for each check will be output to test-suite-$toolname.log. The
# target will succeed if there are zero errors and fail otherwise.
#
# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in
# memcheck, helgrind, drd and sgcheck. These are useful because often only
# some of those tools can be ran cleanly on a codebase.
#
# The macro supports running with and without libtool.
#
# LICENSE
#
# Copyright (c) 2014, 2015, 2016 Philip Withnall <philip.withnall@collabora.co.uk>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 23
dnl Configured tools
m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]])
m4_set_add_all([valgrind_exp_tool_set], [sgcheck])
m4_foreach([vgtool], [valgrind_tool_list],
[m4_define([en_dflt_valgrind_]vgtool, [on])])
AC_DEFUN([AX_VALGRIND_DFLT],[
m4_define([en_dflt_valgrind_$1], [$2])
])dnl
AC_DEFUN([AX_VALGRIND_CHECK],[
AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
m4_foreach([vgtool], [valgrind_tool_list],
[AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
dnl Check for --enable-valgrind
AC_ARG_ENABLE([valgrind],
[AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])],
[enable_valgrind=$enableval],[enable_valgrind=])
AS_IF([test "$enable_valgrind" != "no"],[
# Check for Valgrind.
AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind])
AS_IF([test "$VALGRIND" = ""],[
AS_IF([test "$enable_valgrind" = "yes"],[
AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind])
],[
enable_valgrind=no
])
],[
enable_valgrind=yes
])
])
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind])
# Check for Valgrind tools we care about.
[valgrind_enabled_tools=]
m4_foreach([vgtool],[valgrind_tool_list],[
AC_ARG_ENABLE([valgrind-]vgtool,
m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl
[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl
[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]),
[enable_valgrind_]vgtool[=$enableval],
[enable_valgrind_]vgtool[=])
AS_IF([test "$enable_valgrind" = "no"],[
enable_valgrind_]vgtool[=no],
[test "$enable_valgrind_]vgtool[" ]dnl
m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[
AC_CACHE_CHECK([for Valgrind tool ]vgtool,
[ax_cv_valgrind_tool_]vgtool,[
ax_cv_valgrind_tool_]vgtool[=no
m4_set_contains([valgrind_exp_tool_set],vgtool,
[m4_define([vgtoolx],[exp-]vgtool)],
[m4_define([vgtoolx],vgtool)])
AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[
ax_cv_valgrind_tool_]vgtool[=yes
])
])
AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool)
],[
enable_valgrind_]vgtool[=no
])
],[
enable_valgrind_]vgtool[=yes
])
])
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])["
])
AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool)
])
AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["])
AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools])
[VALGRIND_CHECK_RULES='
# Valgrind check
#
# Optional:
# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions
# files to load. (Default: empty)
# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools.
# (Default: --num-callers=30)
# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of:
# memcheck, helgrind, drd, sgcheck). (Default: various)
# Optional variables
VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES))
VALGRIND_FLAGS ?= --num-callers=30
VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no
VALGRIND_helgrind_FLAGS ?= --history-level=approx
VALGRIND_drd_FLAGS ?=
VALGRIND_sgcheck_FLAGS ?=
# Internal use
valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools)))
valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS)
valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS)
valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS)
valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS)
valgrind_quiet = $(valgrind_quiet_$(V))
valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
valgrind_quiet_0 = --quiet
valgrind_v_use = $(valgrind_v_use_$(V))
valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY))
valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-local,%,$''@):;
# Support running with and without libtool.
ifneq ($(LIBTOOL),)
valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
else
valgrind_lt =
endif
# Use recursive makes in order to ignore errors during check
check-valgrind-local:
ifeq ($(VALGRIND_ENABLED),yes)
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \
$(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool))
else
@echo "Need to reconfigure with --enable-valgrind"
endif
# Valgrind running
VALGRIND_TESTS_ENVIRONMENT = \
$(TESTS_ENVIRONMENT) \
env VALGRIND=$(VALGRIND) \
G_SLICE=always-malloc,debug-blocks \
G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly
VALGRIND_LOG_COMPILER = \
$(valgrind_lt) \
$(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
define valgrind_tool_rule
check-valgrind-$(1)-local:
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
ifneq ($$(TESTS),)
$$(valgrind_v_use)$$(MAKE) check-TESTS \
TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \
LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \
LOG_FLAGS="$$(valgrind_$(1)_flags)" \
TEST_SUITE_LOG=test-suite-$(1).log
endif
else ifeq ($$(VALGRIND_ENABLED),yes)
@echo "Need to reconfigure with --enable-valgrind-$(1)"
else
@echo "Need to reconfigure with --enable-valgrind"
endif
endef
$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool))))
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind
MOSTLYCLEANFILES ?=
MOSTLYCLEANFILES += $(valgrind_log_files)
.PHONY: check-valgrind $(addprefix check-valgrind-,$(valgrind_tools))
']
AC_SUBST([VALGRIND_CHECK_RULES])
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])])
])

1260
src/auth.c

File diff suppressed because it is too large Load Diff

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
@@ -26,16 +27,20 @@
#include "hash.h"
#include "util.h"
#include "parser.h"
#include "rand.h"
#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
@@ -77,6 +82,7 @@ typedef struct _xmpp_connlist_t {
struct _xmpp_ctx_t {
const xmpp_mem_t *mem;
const xmpp_log_t *log;
int verbosity;
xmpp_rand_t *rand;
xmpp_loop_status_t loop_status;
@@ -87,33 +93,50 @@ struct _xmpp_ctx_t {
};
/* convenience functions for accessing the context */
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size);
void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size);
char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s);
void xmpp_log(const xmpp_ctx_t *const ctx,
const xmpp_log_level_t level,
const char *const area,
const char *const fmt,
va_list ap);
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size);
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 xmpp_error(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...);
void xmpp_warn(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...);
void xmpp_info(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...);
void xmpp_debug(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...);
void strophe_error(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...);
void strophe_warn(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...);
void strophe_info(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...);
void strophe_debug(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...);
void strophe_debug_verbose(
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
void strophe_log_internal(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
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 **/
@@ -124,13 +147,24 @@ typedef enum {
XMPP_STATE_CONNECTED
} xmpp_conn_state_t;
typedef enum {
XMPP_QUEUE_STROPHE = 0x1,
XMPP_QUEUE_USER = 0x2,
XMPP_QUEUE_SM = 0x800,
XMPP_QUEUE_SM_STROPHE = XMPP_QUEUE_SM | XMPP_QUEUE_STROPHE,
} xmpp_send_queue_owner_t;
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
struct _xmpp_send_queue_t {
char *data;
size_t len;
size_t written;
int wip;
xmpp_send_queue_owner_t owner;
void *userdata;
uint32_t sm_h;
xmpp_send_queue_t *next;
xmpp_send_queue_t *prev, *next;
};
#define UNUSED(x) ((void)(x))
@@ -143,9 +177,17 @@ struct _xmpp_send_queue_t {
#define SASL_MASK_SCRAMSHA1 (1 << 3)
#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,
@@ -153,9 +195,47 @@ enum {
XMPP_PORT_COMPONENT = 5347,
};
typedef void (*xmpp_open_handler)(xmpp_conn_t *const conn);
typedef void (*xmpp_open_handler)(xmpp_conn_t *conn);
typedef struct {
xmpp_send_queue_t *head, *tail;
} xmpp_queue_t;
struct _xmpp_sm_t {
xmpp_ctx_t *ctx;
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;
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;
@@ -166,26 +246,47 @@ struct _xmpp_conn_t {
int error;
xmpp_stream_error_t *stream_error;
xmpp_sock_t *xsock;
sock_t sock;
int ka_timeout; /* TCP keepalive timeout */
int ka_interval; /* TCP keepalive interval */
int ka_count; /* TCP keepalive count */
tls_t *tls;
char *tls_cafile;
int tls_support;
int tls_disabled;
int tls_mandatory;
int tls_legacy_ssl;
int tls_trust;
char *tls_cafile;
char *tls_capath;
char *tls_client_cert;
char *tls_client_key;
int tls_failed; /* set when tls fails, so we don't try again */
int sasl_support; /* if true, field is a bitfield of supported
mechanisms */
int auth_legacy_enabled;
int secured; /* set when stream is secured with TLS */
xmpp_certfail_handler certfail_handler;
xmpp_password_callback password_callback;
void *password_callback_userdata;
struct {
char pass[1024];
unsigned char fname_hash[XMPP_SHA1_DIGEST_SIZE];
size_t passlen, fnamelen;
} password_cache;
unsigned int password_retries;
/* if server returns <bind/> or <session/> we must do them */
int bind_required;
int session_required;
int sm_disable;
xmpp_sm_state_t *sm_state;
struct {
struct xmpp_compression *state;
int allowed, supported, dont_reset;
} compression;
char *lang;
char *domain;
@@ -198,6 +299,7 @@ struct _xmpp_conn_t {
int blocking_send;
int send_queue_max;
int send_queue_len;
int send_queue_user_len;
xmpp_send_queue_t *send_queue_head;
xmpp_send_queue_t *send_queue_tail;
@@ -213,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;
@@ -224,15 +327,18 @@ struct _xmpp_conn_t {
xmpp_handlist_t *timed_handlers;
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 *const conn);
void conn_disconnect_clean(xmpp_conn_t *const conn);
void conn_established(xmpp_conn_t *const conn);
void conn_open_stream(xmpp_conn_t *const conn);
int conn_tls_start(xmpp_conn_t *const conn);
void conn_prepare_reset(xmpp_conn_t *const conn, xmpp_open_handler handler);
void conn_parser_reset(xmpp_conn_t *const conn);
void conn_disconnect(xmpp_conn_t *conn);
void conn_disconnect_clean(xmpp_conn_t *conn);
void conn_established(xmpp_conn_t *conn);
void conn_open_stream(xmpp_conn_t *conn);
int conn_tls_start(xmpp_conn_t *conn);
void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler);
void conn_parser_reset(xmpp_conn_t *conn);
typedef enum {
XMPP_STANZA_UNKNOWN,
@@ -257,32 +363,54 @@ struct _xmpp_stanza_t {
};
/* handler management */
void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx);
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
uint64_t handler_fire_timed(xmpp_ctx_t *ctx);
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
void handler_add_timed(xmpp_conn_t *const conn,
void handler_add_timed(xmpp_conn_t *conn,
xmpp_timed_handler handler,
const unsigned long period,
void *const userdata);
void handler_add_id(xmpp_conn_t *const conn,
unsigned long period,
void *userdata);
void handler_add_id(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id,
void *const userdata);
void handler_add(xmpp_conn_t *const conn,
const char *id,
void *userdata);
void handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const ns,
const char *const name,
const char *const type,
void *const userdata);
const char *ns,
const char *name,
const char *type,
void *userdata);
void handler_system_delete_all(xmpp_conn_t *conn);
/* utility functions */
void disconnect_mem_error(xmpp_conn_t *const conn);
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 */
void auth_handle_open(xmpp_conn_t *const conn);
void auth_handle_component_open(xmpp_conn_t *const conn);
void auth_handle_open_raw(xmpp_conn_t *const conn);
void auth_handle_open_stub(xmpp_conn_t *const conn);
void auth_handle_open(xmpp_conn_t *conn);
void auth_handle_component_open(xmpp_conn_t *conn);
void auth_handle_open_raw(xmpp_conn_t *conn);
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);
/* send functions */
void send_raw(xmpp_conn_t *conn,
const char *data,
size_t len,
xmpp_send_queue_owner_t owner,
void *userdata);
/* this is a bit special as it will always mark the sent string as
* owned by libstrophe
*/
void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
void send_stanza(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
xmpp_send_queue_owner_t owner);
#endif /* __LIBSTROPHE_COMMON_H__ */

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;
}

1829
src/conn.c

File diff suppressed because it is too large Load Diff

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
@@ -22,11 +23,11 @@
#include <assert.h>
#include <string.h> /* memset, memcpy */
#include "common.h" /* xmpp_alloc */
#include "common.h" /* strophe_alloc */
#include "ostypes.h" /* uint8_t, size_t */
#include "sha1.h"
#include "snprintf.h" /* xmpp_snprintf */
#include "strophe.h" /* xmpp_ctx_t, xmpp_free */
#include "strophe.h" /* xmpp_ctx_t, strophe_free */
struct _xmpp_sha1_t {
xmpp_ctx_t *xmpp_ctx;
@@ -42,7 +43,7 @@ static char *digest_to_string(const uint8_t *digest, char *s, size_t len)
return NULL;
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
xmpp_snprintf(s + i * 2, 3, "%02x", digest[i]);
strophe_snprintf(s + i * 2, 3, "%02x", digest[i]);
return s;
}
@@ -53,7 +54,7 @@ static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
size_t slen;
slen = SHA1_DIGEST_SIZE * 2 + 1;
s = xmpp_alloc(ctx, slen);
s = strophe_alloc(ctx, slen);
if (s) {
s = digest_to_string(digest, s, slen);
assert(s != NULL);
@@ -122,7 +123,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
{
xmpp_sha1_t *sha1;
sha1 = xmpp_alloc(ctx, sizeof(*sha1));
sha1 = strophe_alloc(ctx, sizeof(*sha1));
if (sha1) {
memset(sha1, 0, sizeof(*sha1));
crypto_SHA1_Init(&sha1->ctx);
@@ -139,7 +140,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
*/
void xmpp_sha1_free(xmpp_sha1_t *sha1)
{
xmpp_free(sha1->xmpp_ctx, sha1);
strophe_free(sha1->xmpp_ctx, sha1);
}
/** Update SHA1 context with the next portion of data.
@@ -187,9 +188,9 @@ char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
}
/** Return message digest rendered as a string.
* Returns an allocated string. Free the string using the Strophe context
* which is passed to xmpp_sha1_new(). Call this function after
* xmpp_sha1_final().
* Returns an allocated string. Free the string by calling xmpp_free() using
* the Strophe context which is passed to xmpp_sha1_new(). Call this function
* after xmpp_sha1_final().
*
* @param sha1 a SHA1 object
*
@@ -244,15 +245,14 @@ static const char _base64_charmap[65] = {
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
static size_t base64_encoded_len(const size_t len)
static size_t base64_encoded_len(size_t len)
{
/* encoded steam is 4 bytes for every three, rounded up */
return ((len + 2) / 3) << 2;
}
static char *base64_encode(xmpp_ctx_t *ctx,
const unsigned char *const buffer,
const size_t len)
static char *
base64_encode(xmpp_ctx_t *ctx, const unsigned char *buffer, size_t len)
{
size_t clen;
char *cbuf, *c;
@@ -260,7 +260,7 @@ static char *base64_encode(xmpp_ctx_t *ctx,
size_t i;
clen = base64_encoded_len(len);
cbuf = xmpp_alloc(ctx, clen + 1);
cbuf = strophe_alloc(ctx, clen + 1);
if (cbuf != NULL) {
c = cbuf;
/* loop over data, turning every 3 bytes into 4 characters */
@@ -304,7 +304,7 @@ static char *base64_encode(xmpp_ctx_t *ctx,
return cbuf;
}
static size_t base64_decoded_len(const char *const buffer, const size_t len)
static size_t base64_decoded_len(const char *buffer, size_t len)
{
size_t nudge = 0;
unsigned char c;
@@ -331,8 +331,8 @@ static size_t base64_decoded_len(const char *const buffer, const size_t len)
}
static void base64_decode(xmpp_ctx_t *ctx,
const char *const buffer,
const size_t len,
const char *buffer,
size_t len,
unsigned char **out,
size_t *outlen)
{
@@ -349,7 +349,7 @@ static void base64_decode(xmpp_ctx_t *ctx,
if (dlen == 0)
goto _base64_error;
dbuf = xmpp_alloc(ctx, dlen + 1);
dbuf = strophe_alloc(ctx, dlen + 1);
if (dbuf != NULL) {
d = dbuf;
/* loop over each set of 4 characters, decoding 3 bytes */
@@ -428,7 +428,7 @@ static void base64_decode(xmpp_ctx_t *ctx,
_base64_decode_error:
/* invalid character; abort decoding! */
xmpp_free(ctx, dbuf);
strophe_free(ctx, dbuf);
_base64_error:
*out = NULL;
*outlen = 0;
@@ -470,7 +470,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
if (len == 0) {
/* handle empty string */
buf = xmpp_alloc(ctx, 1);
buf = strophe_alloc(ctx, 1);
if (buf)
buf[0] = '\0';
buflen = 0;
@@ -479,7 +479,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
}
if (buf) {
if (buflen != strlen((char *)buf)) {
xmpp_free(ctx, buf);
strophe_free(ctx, buf);
buf = NULL;
}
}

184
src/ctx.c
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
@@ -50,10 +51,22 @@
#include "resolver.h"
#include "util.h"
/* Workaround for systems without va_copy support. */
#if defined(_MSC_VER) && _MSC_VER < 1800 || \
!defined(_MSC_VER) && !defined(HAVE_DECL_VA_COPY)
#define va_copy(d, s) (memcpy(&d, &s, sizeof(va_list)))
#ifndef va_copy
#ifdef HAVE_VA_COPY
#define va_copy(dest, src) va_copy(dest, src)
#else
#ifdef HAVE___VA_COPY
#define va_copy(dest, src) __va_copy(dest, src)
#else
#ifndef VA_LIST_IS_ARRAY
#define va_copy(dest, src) (dest) = (src)
#else
#include <string.h>
#define va_copy(dest, src) \
memcpy((char *)(dest), (char *)(src), sizeof(va_list))
#endif
#endif
#endif
#endif
/** Initialize the Strophe library.
@@ -122,19 +135,19 @@ int xmpp_version_check(int major, int minor)
/* Wrap stdlib routines malloc, free, and realloc for default memory
* management.
*/
static void *_malloc(const size_t size, void *const userdata)
static void *_malloc(size_t size, void *userdata)
{
UNUSED(userdata);
return malloc(size);
}
static void _free(void *p, void *const userdata)
static void _free(void *p, void *userdata)
{
UNUSED(userdata);
free(p);
}
static void *_realloc(void *p, const size_t size, void *const userdata)
static void *_realloc(void *p, size_t size, void *userdata)
{
UNUSED(userdata);
return realloc(p, size);
@@ -146,8 +159,7 @@ static xmpp_mem_t xmpp_default_mem = {
_free, _realloc, NULL};
/* log levels and names */
static const char *const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN",
"ERROR"};
static const char *_xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
@@ -160,10 +172,10 @@ static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
* @param area the area the log message is for
* @param msg the log message
*/
static void xmpp_default_logger(void *const userdata,
const xmpp_log_level_t level,
const char *const area,
const char *const msg)
static void xmpp_default_logger(void *userdata,
xmpp_log_level_t level,
const char *area,
const char *msg)
{
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
if (level >= filter_level)
@@ -212,7 +224,7 @@ static xmpp_log_t xmpp_default_log = {NULL, NULL};
*
* @return a pointer to the allocated memory or NULL on an error
*/
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
{
return ctx->mem->alloc(size, ctx->mem->userdata);
}
@@ -223,11 +235,21 @@ void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
* @param ctx a Strophe context object
* @param p a pointer referencing memory to be freed
*/
void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
void strophe_free(const xmpp_ctx_t *ctx, void *p)
{
ctx->mem->free(p, ctx->mem->userdata);
}
/** Trampoline to \ref strophe_free
*
* @param ctx \ref strophe_free
* @param p \ref strophe_free
*/
void xmpp_free(const xmpp_ctx_t *ctx, void *p)
{
strophe_free(ctx, p);
}
/** Reallocate memory in a Strophe context.
* All Strophe functions will use this to reallocate memory.
*
@@ -237,7 +259,7 @@ void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
*
* @return a pointer to the reallocated memory or NULL on an error
*/
void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
{
return ctx->mem->realloc(p, size, ctx->mem->userdata);
}
@@ -246,8 +268,8 @@ void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
* Write a log message to the logger for the context for the specified
* level and area. This function takes a printf-style format string and a
* variable argument list (in va_list) format. This function is not meant
* to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info,
* and xmpp_debug.
* to be called directly, but is used via strophe_error, strophe_warn,
* strophe_info, and strophe_debug.
*
* @param ctx a Strophe context object
* @param level the level at which to log
@@ -255,32 +277,40 @@ void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
* @param fmt a printf-style format string for the message
* @param ap variable argument list supplied for the format string
*/
void xmpp_log(const xmpp_ctx_t *const ctx,
const xmpp_log_level_t level,
const char *const area,
const char *const fmt,
va_list ap)
static void _strophe_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
const char *fmt,
va_list ap)
{
int oldret, ret;
char smbuf[1024];
char *buf;
va_list copy;
if (!ctx->log->handler)
return;
if (ctx->log->handler == xmpp_default_logger &&
level < *(xmpp_log_level_t *)ctx->log->userdata)
return;
va_copy(copy, ap);
ret = xmpp_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
ret = strophe_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
if (ret >= (int)sizeof(smbuf)) {
buf = (char *)xmpp_alloc(ctx, ret + 1);
buf = (char *)strophe_alloc(ctx, ret + 1);
if (!buf) {
buf = NULL;
xmpp_error(ctx, "log", "Failed allocating memory for log message.");
strophe_error(ctx, "log",
"Failed allocating memory for log message.");
va_end(copy);
return;
}
oldret = ret;
ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
ret = strophe_vsnprintf(buf, ret + 1, fmt, copy);
if (ret > oldret) {
xmpp_error(ctx, "log", "Unexpected error");
xmpp_free(ctx, buf);
strophe_error(ctx, "log", "Unexpected error");
strophe_free(ctx, buf);
va_end(copy);
return;
}
@@ -289,11 +319,20 @@ void xmpp_log(const xmpp_ctx_t *const ctx,
}
va_end(copy);
if (ctx->log->handler)
ctx->log->handler(ctx->log->userdata, level, area, buf);
ctx->log->handler(ctx->log->userdata, level, area, buf);
if (buf != smbuf)
xmpp_free(ctx, buf);
strophe_free(ctx, buf);
}
/* Dummy trampoline, will be removed when deprecated.c is deleted */
void strophe_log_internal(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
const char *fmt,
va_list ap)
{
_strophe_log(ctx, level, area, fmt, ap);
}
/** Write to the log at the ERROR level.
@@ -306,15 +345,15 @@ void xmpp_log(const xmpp_ctx_t *const ctx,
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_error(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...)
void strophe_error(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
_strophe_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
va_end(ap);
}
@@ -328,15 +367,12 @@ void xmpp_error(const xmpp_ctx_t *const ctx,
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_warn(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...)
void strophe_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
_strophe_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
va_end(ap);
}
@@ -350,15 +386,12 @@ void xmpp_warn(const xmpp_ctx_t *const ctx,
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_info(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...)
void strophe_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
_strophe_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
va_end(ap);
}
@@ -372,15 +405,39 @@ void xmpp_info(const xmpp_ctx_t *const ctx,
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_debug(const xmpp_ctx_t *const ctx,
const char *const area,
const char *const fmt,
...)
void strophe_debug(const xmpp_ctx_t *ctx,
const char *area,
const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the DEBUG level if verbosity is enabled.
* This is a convenience function for writing to the log at the DEBUG level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param level the verbosity level
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void strophe_debug_verbose(
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
if (ctx->verbosity < level)
return;
va_start(ap, fmt);
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
@@ -398,8 +455,7 @@ void xmpp_debug(const xmpp_ctx_t *const ctx,
*
* @ingroup Context
*/
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
const xmpp_log_t *const log)
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
{
xmpp_ctx_t *ctx = NULL;
@@ -409,6 +465,8 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
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
@@ -419,13 +477,11 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
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;
if (ctx->rand == NULL) {
xmpp_free(ctx, ctx);
strophe_free(ctx, ctx);
ctx = NULL;
}
}
@@ -439,21 +495,21 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
*
* @ingroup Context
*/
void xmpp_ctx_free(xmpp_ctx_t *const ctx)
void xmpp_ctx_free(xmpp_ctx_t *ctx)
{
/* mem and log are owned by their suppliers */
xmpp_rand_free(ctx, ctx->rand);
xmpp_free(ctx, ctx); /* pull the hole in after us */
strophe_free(ctx, ctx); /* pull the hole in after us */
}
/** Set the timeout to use when calling xmpp_run().
/** Set the verbosity level of a Strophe context.
*
* @param ctx a Strophe context object
* @param timeout the time to wait for events in milliseconds
* @param level the verbosity level
*
* @ingroup Context
*/
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout)
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
{
ctx->timeout = timeout;
ctx->verbosity = level;
}

276
src/deprecated.c Normal file
View File

@@ -0,0 +1,276 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* deprecated.c
** strophe XMPP client library -- File with deprecated API functions.
**
** Copyright (C) 2022 Steffen Jaeckel
**
** 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
* File with deprecated API functions.
*/
/** @defgroup Deprecated All deprecated functions
* These functions will be removed in the next release.
*/
#include "common.h"
/** Allocate memory in a Strophe context.
* All Strophe functions will use this to allocate memory.
*
* @param ctx a Strophe context object
* @param size the number of bytes to allocate
*
* @return a pointer to the allocated memory or NULL on an error
*
* @ingroup Deprecated
*/
void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size)
{
return strophe_alloc(ctx, size);
}
/** Reallocate memory in a Strophe context.
* All Strophe functions will use this to reallocate memory.
*
* @param ctx a Strophe context object
* @param p a pointer to previously allocated memory
* @param size the new size in bytes to allocate
*
* @return a pointer to the reallocated memory or NULL on an error
*
* @ingroup Deprecated
*/
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
{
return strophe_realloc(ctx, p, size);
}
/** implement our own strdup that uses the ctx allocator */
/** Duplicate a string.
* This function replaces the standard strdup library call with a version
* that uses the Strophe context object's allocator.
*
* @param ctx a Strophe context object
* @param s a string
*
* @return a newly allocated string with the same data as s or NULL on error
*
* @ingroup Deprecated
*/
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s)
{
return strophe_strdup(ctx, s);
}
/** Duplicate a string with a maximum length.
* This function replaces the standard strndup library call with a version
* that uses the Strophe context object's allocator.
*
* @param ctx a Strophe context object
* @param s a string
* @param len the maximum length of the string to copy
*
* @return a newly allocated string that contains at most `len` symbols
* of the original string or NULL on error
*
* @ingroup Deprecated
*/
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
{
return strophe_strndup(ctx, s, len);
}
void xmpp_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
const char *fmt,
va_list ap)
{
strophe_log_internal(ctx, level, area, fmt, ap);
}
/** Write to the log at the ERROR level.
* This is a convenience function for writing to the log at the
* ERROR level. It takes a printf-style format string followed by a
* variable list of arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*
* @ingroup Deprecated
*/
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strophe_log_internal(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the WARN level.
* This is a convenience function for writing to the log at the WARN level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*
* @ingroup Deprecated
*/
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strophe_log_internal(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the INFO level.
* This is a convenience function for writing to the log at the INFO level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*
* @ingroup Deprecated
*/
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strophe_log_internal(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the DEBUG level.
* This is a convenience function for writing to the log at the DEBUG level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*
* @ingroup Deprecated
*/
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the DEBUG level if verbosity is enabled.
* This is a convenience function for writing to the log at the DEBUG level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param level the verbosity level
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*
* @ingroup Deprecated
*/
void xmpp_debug_verbose(
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
if (ctx->verbosity < level)
return;
va_start(ap, fmt);
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
/** strtok_r(3) implementation.
* This function has appeared in POSIX.1-2001, but not in C standard.
* For example, visual studio older than 2005 doesn't provide strtok_r()
* nor strtok_s().
*
* @ingroup Deprecated
*/
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
{
return strophe_strtok_r(s, delim, saveptr);
}
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = strophe_vsnprintf(str, count, fmt, ap);
va_end(ap);
return ret;
}
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg)
{
return strophe_vsnprintf(str, count, fmt, arg);
}
/** Set TCP keepalive parameters
* Turn on TCP keepalive and set timeout and interval. Zero timeout
* disables TCP keepalives. The parameters are applied immediately for
* a non disconnected object. Also, they are applied when the connection
* object connects successfully.
*
* @param conn a Strophe connection object
* @param timeout TCP keepalive timeout in seconds
* @param interval TCP keepalive interval in seconds
*
* @note this function is deprecated
* @see xmpp_conn_set_sockopt_callback()
*
* @ingroup Deprecated
*/
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
{
conn->ka_timeout = timeout;
conn->ka_interval = 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,22 @@
#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)
{
sock_close(conn->sock);
conn->sock = sock_connect(conn->xsock);
if (conn->sock == INVALID_SOCKET)
return -1;
conn->timeout_stamp = time_stamp();
return 0;
}
/** Run the event loop once.
* This function will run send any data that has been queued by
@@ -73,17 +88,18 @@
*
* @ingroup EventLoop
*/
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
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;
@@ -99,17 +115,21 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const 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 */
xmpp_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;
}
}
@@ -118,43 +138,55 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const 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;
if (ret != towrite)
break; /* partial write or an error */
/* all data for this queue item written, delete and move on */
xmpp_free(ctx, sq->data);
strophe_debug(conn->ctx, "conn", "SENT: %s", sq->data);
strophe_debug_verbose(1, ctx, "xmpp", "Q_SENT: %p", sq);
tsq = sq;
sq = sq->next;
conn->send_queue_len--;
xmpp_free(ctx, tsq);
if (tsq->owner & XMPP_QUEUE_USER)
conn->send_queue_user_len--;
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, h=%lu",
tsq, tsq->sm_h);
add_queue_back(&conn->sm_state->sm_queue, tsq);
tsq = NULL;
}
if (tsq) {
strophe_debug_verbose(2, ctx, "xmpp", "Q_FREE: %p", tsq);
strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s",
tsq->data);
strophe_free(ctx, tsq->data);
strophe_free(ctx, tsq);
}
/* pop the top item */
conn->send_queue_head = sq;
/* 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) {
/* FIXME: need to tear down send queues and random other things
* maybe this should be abstracted */
xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
strophe_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
}
next_item:
connitem = connitem->next;
}
@@ -179,6 +211,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
@@ -190,9 +223,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
conn->connect_timeout)
FD_SET(conn->sock, &wfds);
else {
conn->error = ETIMEDOUT;
xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
conn_disconnect(conn);
strophe_info(ctx, "xmpp", "Connection attempt timed out.");
ret = _connect_next(conn);
if (ret != 0) {
conn->error = ETIMEDOUT;
conn_disconnect(conn);
} else {
FD_SET(conn->sock, &wfds);
}
}
break;
case XMPP_STATE_CONNECTED:
@@ -208,7 +246,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* 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;
@@ -227,9 +265,9 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* select errored */
if (ret < 0) {
if (!sock_is_recoverable(sock_error()))
xmpp_error(ctx, "xmpp", "event watcher internal error %d",
sock_error());
if (!sock_is_recoverable(NULL, sock_error(NULL)))
strophe_error(ctx, "xmpp", "event watcher internal error %d",
sock_error(NULL));
return;
}
@@ -241,6 +279,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
@@ -251,47 +290,45 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
ret = sock_connect_error(conn->sock);
if (ret != 0) {
/* connection failed */
xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret);
conn_disconnect(conn);
strophe_debug(ctx, "xmpp", "connection failed, error %d",
ret);
ret = _connect_next(conn);
if (ret != 0) {
conn->error = ret;
conn_disconnect(conn);
}
break;
}
conn->state = XMPP_STATE_CONNECTED;
xmpp_debug(ctx, "xmpp", "connection successful");
strophe_debug(ctx, "xmpp", "connection successful");
conn_established(conn);
}
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);
if (!ret) {
xmpp_debug(ctx, "xmpp", "parse error [%s]", buf);
strophe_debug(ctx, "xmpp", "parse error [%s]", buf);
xmpp_send_error(conn, XMPP_SE_INVALID_XML,
"parse error");
}
} else {
if (conn->tls) {
if (!tls_is_recoverable(tls_error(conn->tls))) {
xmpp_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 */
xmpp_debug(ctx, "xmpp",
"Socket closed by remote host.");
strophe_debug(ctx, "xmpp",
"Socket closed by remote host.");
conn->error = ECONNRESET;
conn_disconnect(conn);
}
@@ -333,7 +370,7 @@ void xmpp_run(xmpp_ctx_t *ctx)
/* make it possible to start event loop again */
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
xmpp_debug(ctx, "event", "Event loop completed.");
strophe_debug(ctx, "event", "Event loop completed.");
}
/** Stop the event loop.
@@ -346,8 +383,20 @@ void xmpp_run(xmpp_ctx_t *ctx)
*/
void xmpp_stop(xmpp_ctx_t *ctx)
{
xmpp_debug(ctx, "event", "Stopping event loop.");
strophe_debug(ctx, "event", "Stopping event loop.");
if (ctx->loop_status == XMPP_LOOP_RUNNING)
ctx->loop_status = XMPP_LOOP_QUIT;
}
/** Set the timeout to use when calling xmpp_run().
*
* @param ctx a Strophe context object
* @param timeout the time to wait for events in milliseconds
*
* @ingroup EventLoop
*/
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout)
{
ctx->timeout = timeout;
}

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()
@@ -46,6 +45,17 @@ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
}
}
static void _free_handlist_item(xmpp_ctx_t *ctx, xmpp_handlist_t *item)
{
if (item->u.ns)
strophe_free(ctx, item->u.ns);
if (item->u.name)
strophe_free(ctx, item->u.name);
if (item->u.type)
strophe_free(ctx, item->u.type);
strophe_free(ctx, item);
}
/** Fire off all stanza handlers that match.
* This function is called internally by the event loop whenever stanzas
* are received from the XMPP server.
@@ -53,7 +63,7 @@ static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
* @param conn a Strophe connection object
* @param stanza a Strophe stanza object
*/
void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
{
xmpp_handlist_t *item, *next, *head, *head_old;
const char *id, *ns, *name, *type;
@@ -69,9 +79,9 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const 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;
@@ -87,8 +97,8 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
/* replace old value */
hash_add(conn->id_handlers, id, head);
}
xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
}
item = next;
}
@@ -105,9 +115,10 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const 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;
}
@@ -124,13 +135,7 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
if (!ret) {
/* handler is one-shot, so delete it */
_handler_item_remove(&conn->handlers, item);
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
if (item->u.type)
xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item);
_free_handlist_item(conn->ctx, item);
}
}
item = next;
@@ -144,7 +149,7 @@ void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
*
* @return the time in milliseconds until the next handler will be ready
*/
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
uint64_t handler_fire_timed(xmpp_ctx_t *ctx)
{
xmpp_connlist_t *connitem;
xmpp_handlist_t *item, *next;
@@ -169,9 +174,9 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const 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;
@@ -189,7 +194,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
if (!ret) {
/* delete handler if it returned false */
_handler_item_remove(&conn->timed_handlers, item);
xmpp_free(ctx, item);
strophe_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
@@ -220,7 +225,7 @@ uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
if (!ret) {
/* delete handler if it returned false */
_handler_item_remove(&ctx->timed_handlers, item);
xmpp_free(ctx, item);
strophe_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
@@ -253,16 +258,16 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
static void _timed_handler_add(xmpp_ctx_t *ctx,
xmpp_handlist_t **handlers_list,
xmpp_void_handler handler,
const unsigned long period,
void *const userdata,
const int user_handler)
unsigned long period,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item;
/* check if handler is already in the list */
for (item = *handlers_list; item; item = item->next) {
if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(ctx, "xmpp", "Timed handler already exists.");
strophe_warn(ctx, "xmpp", "Timed handler already exists.");
break;
}
}
@@ -270,7 +275,7 @@ static void _timed_handler_add(xmpp_ctx_t *ctx,
return;
/* build new item */
item = xmpp_alloc(ctx, sizeof(xmpp_handlist_t));
item = strophe_alloc(ctx, sizeof(xmpp_handlist_t));
if (!item)
return;
@@ -297,7 +302,7 @@ static void _timed_handler_delete(xmpp_ctx_t *ctx,
item = *handlers_list;
if (item->handler == handler) {
*handlers_list = item->next;
xmpp_free(ctx, item);
strophe_free(ctx, item);
} else {
handlers_list = &item->next;
}
@@ -311,16 +316,15 @@ static void _timed_handler_delete(xmpp_ctx_t *ctx,
*
* @ingroup Handlers
*/
void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
xmpp_timed_handler handler)
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler)
{
_timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
}
static void _id_handler_add(xmpp_conn_t *const conn,
static void _id_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id,
void *const userdata,
const char *id,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item, *tail;
@@ -329,7 +333,7 @@ static void _id_handler_add(xmpp_conn_t *const conn,
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
while (item) {
if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(conn->ctx, "xmpp", "Id handler already exists.");
strophe_warn(conn->ctx, "xmpp", "Id handler already exists.");
break;
}
item = item->next;
@@ -338,7 +342,7 @@ static void _id_handler_add(xmpp_conn_t *const conn,
return;
/* build new item */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
item = strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item)
return;
@@ -348,9 +352,9 @@ static void _id_handler_add(xmpp_conn_t *const conn,
item->enabled = 0;
item->next = NULL;
item->u.id = xmpp_strdup(conn->ctx, id);
item->u.id = strophe_strdup(conn->ctx, id);
if (!item->u.id) {
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item);
return;
}
@@ -373,9 +377,9 @@ static void _id_handler_add(xmpp_conn_t *const conn,
*
* @ingroup Handlers
*/
void xmpp_id_handler_delete(xmpp_conn_t *const conn,
void xmpp_id_handler_delete(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id)
const char *id)
{
xmpp_handlist_t *item, *prev, *next;
@@ -395,8 +399,8 @@ void xmpp_id_handler_delete(xmpp_conn_t *const conn,
hash_add(conn->id_handlers, id, next);
}
xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
item = next;
} else {
prev = item;
@@ -405,13 +409,23 @@ void xmpp_id_handler_delete(xmpp_conn_t *const conn,
}
}
static int _dup_string(xmpp_ctx_t *ctx, const char *src, char **dest)
{
if (src) {
*dest = strophe_strdup(ctx, src);
if (!(*dest))
return 1;
}
return 0;
}
/* add a stanza handler */
static void _handler_add(xmpp_conn_t *const conn,
static void _handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const ns,
const char *const name,
const char *const type,
void *const userdata,
const char *ns,
const char *name,
const char *type,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item, *tail;
@@ -421,7 +435,7 @@ static void _handler_add(xmpp_conn_t *const conn,
/* same handler function can process different stanzas and
distinguish them according to userdata. */
if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
strophe_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
break;
}
}
@@ -429,47 +443,21 @@ static void _handler_add(xmpp_conn_t *const conn,
return;
/* build new item */
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
item = (xmpp_handlist_t *)strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item)
return;
memset(item, 0, sizeof(*item));
item->user_handler = user_handler;
item->handler = handler;
item->userdata = userdata;
item->enabled = 0;
item->next = NULL;
if (ns) {
item->u.ns = xmpp_strdup(conn->ctx, ns);
if (!item->u.ns) {
xmpp_free(conn->ctx, item);
return;
}
} else
item->u.ns = NULL;
if (name) {
item->u.name = xmpp_strdup(conn->ctx, name);
if (!item->u.name) {
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
xmpp_free(conn->ctx, item);
return;
}
} else
item->u.name = NULL;
if (type) {
item->u.type = xmpp_strdup(conn->ctx, type);
if (!item->u.type) {
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
xmpp_free(conn->ctx, item);
}
} else
item->u.type = NULL;
if (_dup_string(conn->ctx, ns, &item->u.ns))
goto error_out;
if (_dup_string(conn->ctx, name, &item->u.name))
goto error_out;
if (_dup_string(conn->ctx, type, &item->u.type))
goto error_out;
/* append to list */
if (!conn->handlers)
@@ -480,6 +468,11 @@ static void _handler_add(xmpp_conn_t *const conn,
tail = tail->next;
tail->next = item;
}
return;
error_out:
_free_handlist_item(conn->ctx, item);
}
/** Delete a stanza handler.
@@ -489,7 +482,7 @@ static void _handler_add(xmpp_conn_t *const conn,
*
* @ingroup Handlers
*/
void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler)
{
xmpp_handlist_t *prev, *item;
@@ -505,13 +498,7 @@ void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
else
conn->handlers = item->next;
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
if (item->u.type)
xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item);
_free_handlist_item(conn->ctx, item);
item = prev ? prev->next : conn->handlers;
} else {
prev = item;
@@ -536,10 +523,10 @@ void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
*
* @ingroup Handlers
*/
void xmpp_timed_handler_add(xmpp_conn_t *const conn,
void xmpp_timed_handler_add(xmpp_conn_t *conn,
xmpp_timed_handler handler,
const unsigned long period,
void *const userdata)
unsigned long period,
void *userdata)
{
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
userdata, 1);
@@ -554,10 +541,10 @@ void xmpp_timed_handler_add(xmpp_conn_t *const conn,
* @param period the time in milliseconds between firings
* @param userdata an opaque data pointer that will be passed to the handler
*/
void handler_add_timed(xmpp_conn_t *const conn,
void handler_add_timed(xmpp_conn_t *conn,
xmpp_timed_handler handler,
const unsigned long period,
void *const userdata)
unsigned long period,
void *userdata)
{
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
userdata, 0);
@@ -579,10 +566,10 @@ void handler_add_timed(xmpp_conn_t *const conn,
*
* @ingroup Handlers
*/
void xmpp_id_handler_add(xmpp_conn_t *const conn,
void xmpp_id_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id,
void *const userdata)
const char *id,
void *userdata)
{
_id_handler_add(conn, handler, id, userdata, 1);
}
@@ -596,10 +583,10 @@ void xmpp_id_handler_add(xmpp_conn_t *const conn,
* @param id a string with the id
* @param userdata an opaque data pointer that will be passed to the handler
*/
void handler_add_id(xmpp_conn_t *const conn,
void handler_add_id(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id,
void *const userdata)
const char *id,
void *userdata)
{
_id_handler_add(conn, handler, id, userdata, 0);
}
@@ -626,12 +613,12 @@ void handler_add_id(xmpp_conn_t *const conn,
*
* @ingroup Handlers
*/
void xmpp_handler_add(xmpp_conn_t *const conn,
void xmpp_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const ns,
const char *const name,
const char *const type,
void *const userdata)
const char *ns,
const char *name,
const char *type,
void *userdata)
{
_handler_add(conn, handler, ns, name, type, userdata, 1);
}
@@ -647,12 +634,12 @@ void xmpp_handler_add(xmpp_conn_t *const conn,
* @param type a string with the 'type' attribute value to match
* @param userdata an opaque data pointer that will be passed to the handler
*/
void handler_add(xmpp_conn_t *const conn,
void handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const ns,
const char *const name,
const char *const type,
void *const userdata)
const char *ns,
const char *name,
const char *type,
void *userdata)
{
_handler_add(conn, handler, ns, name, type, userdata, 0);
}
@@ -666,8 +653,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
{
xmpp_handlist_t *item, *next, *head, *head_old;
hash_iterator_t *iter;
const char *key;
char *key2 = NULL;
const char *key, *key2;
/* TODO unify all kinds of handlers and avoid copy-paste below */
@@ -676,13 +662,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&conn->handlers, item);
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
if (item->u.type)
xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item);
_free_handlist_item(conn->ctx, item);
item = next;
} else
item = item->next;
@@ -693,7 +673,7 @@ void handler_system_delete_all(xmpp_conn_t *conn)
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&conn->timed_handlers, item);
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item);
item = next;
} else
item = item->next;
@@ -708,26 +688,24 @@ void handler_system_delete_all(xmpp_conn_t *conn)
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&head, item);
xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
item = next;
} else
item = item->next;
}
if (head != head_old)
key2 = xmpp_strdup(conn->ctx, key);
/* Hash table implementation is not perfect, so we need to find next
key before dropping current one. Otherwise, we will get access to
freed memory. */
key = hash_iter_next(iter);
key2 = hash_iter_next(iter);
if (head != head_old) {
/* hash_add() replaces value if the key exists */
if (head != NULL)
hash_add(conn->id_handlers, key2, head);
hash_add(conn->id_handlers, key, head);
else
hash_drop(conn->id_handlers, key2);
xmpp_free(conn->ctx, key2);
hash_drop(conn->id_handlers, key);
}
key = key2;
}
if (iter)
hash_iter_release(iter);
@@ -762,10 +740,10 @@ void handler_system_delete_all(xmpp_conn_t *conn)
*
* @ingroup Handlers
*/
void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler,
const unsigned long period,
void *const userdata)
unsigned long period,
void *userdata)
{
_timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1);
}
@@ -777,7 +755,7 @@ void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
*
* @ingroup Handlers
*/
void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx,
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler)
{
_timed_handler_delete(ctx, &ctx->timed_handlers, handler);

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
@@ -46,16 +47,15 @@ struct _hash_iterator_t {
};
/** allocate and initialize a new hash table */
hash_t *
hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func)
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func)
{
hash_t *result = NULL;
result = xmpp_alloc(ctx, sizeof(hash_t));
result = strophe_alloc(ctx, sizeof(hash_t));
if (result != NULL) {
result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
result->entries = strophe_alloc(ctx, size * sizeof(hashentry_t *));
if (result->entries == NULL) {
xmpp_free(ctx, result);
strophe_free(ctx, result);
return NULL;
}
memset(result->entries, 0, size * sizeof(hashentry_t *));
@@ -72,14 +72,14 @@ hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func)
}
/** obtain a new reference to an existing hash table */
hash_t *hash_clone(hash_t *const table)
hash_t *hash_clone(hash_t *table)
{
table->ref++;
return table;
}
/** release a hash table that is no longer needed */
void hash_release(hash_t *const table)
void hash_release(hash_t *table)
{
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry, *next;
@@ -92,15 +92,15 @@ void hash_release(hash_t *const table)
entry = table->entries[i];
while (entry != NULL) {
next = entry->next;
xmpp_free(ctx, entry->key);
strophe_free(ctx, entry->key);
if (table->free)
table->free(ctx, entry->value);
xmpp_free(ctx, entry);
strophe_free(ctx, entry);
entry = next;
}
}
xmpp_free(ctx, table->entries);
xmpp_free(ctx, table);
strophe_free(ctx, table->entries);
strophe_free(ctx, table);
}
}
@@ -143,7 +143,7 @@ hashentry_t *_hash_entry_find(hash_t *table, const char *key)
* each key can appear only once; the value of any
* identical key will be replaced
*/
int hash_add(hash_t *table, const char *const key, void *data)
int hash_add(hash_t *table, const char *key, void *data)
{
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry = NULL;
@@ -154,12 +154,12 @@ int hash_add(hash_t *table, const char *const key, void *data)
if (entry == NULL) {
/* allocate and fill a new entry */
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
entry = strophe_alloc(ctx, sizeof(hashentry_t));
if (!entry)
return -1;
entry->key = xmpp_strdup(ctx, key);
entry->key = strophe_strdup(ctx, key);
if (!entry->key) {
xmpp_free(ctx, entry);
strophe_free(ctx, entry);
return -1;
}
/* insert ourselves in the linked list */
@@ -199,7 +199,7 @@ int hash_drop(hash_t *table, const char *key)
/* traverse the linked list looking for the key */
if (!strcmp(key, entry->key)) {
/* match, remove the entry */
xmpp_free(ctx, entry->key);
strophe_free(ctx, entry->key);
if (table->free)
table->free(ctx, entry->value);
if (prev == NULL) {
@@ -207,7 +207,7 @@ int hash_drop(hash_t *table, const char *key)
} else {
prev->next = entry->next;
}
xmpp_free(ctx, entry);
strophe_free(ctx, entry);
table->num_keys--;
return 0;
}
@@ -229,7 +229,7 @@ hash_iterator_t *hash_iter_new(hash_t *table)
xmpp_ctx_t *ctx = table->ctx;
hash_iterator_t *iter;
iter = xmpp_alloc(ctx, sizeof(*iter));
iter = strophe_alloc(ctx, sizeof(*iter));
if (iter != NULL) {
iter->ref = 1;
iter->table = hash_clone(table);
@@ -249,7 +249,7 @@ void hash_iter_release(hash_iterator_t *iter)
if (iter->ref == 0) { // ref is unsigned!!!
hash_release(iter->table);
xmpp_free(ctx, iter);
strophe_free(ctx, iter);
}
}

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
@@ -18,23 +19,22 @@
typedef struct _hash_t hash_t;
typedef void (*hash_free_func)(const xmpp_ctx_t *const ctx, void *p);
typedef void (*hash_free_func)(const xmpp_ctx_t *ctx, void *p);
/** allocate and initialize a new hash table */
hash_t *
hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func);
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func);
/** allocate a new reference to an existing hash table */
hash_t *hash_clone(hash_t *const table);
hash_t *hash_clone(hash_t *table);
/** release a hash table when no longer needed */
void hash_release(hash_t *const table);
void hash_release(hash_t *table);
/** add a key, value pair to a hash table.
* each key can appear only once; the value of any
* identical key will be replaced
*/
int hash_add(hash_t *table, const char *const key, void *data);
int hash_add(hash_t *table, const char *key, void *data);
/** look up a key in a hash table */
void *hash_get(hash_t *table, const char *key);

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

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
@@ -20,12 +21,9 @@
typedef struct _parser_t parser_t;
typedef void (*parser_start_callback)(char *name,
char **attrs,
void *const userdata);
typedef void (*parser_end_callback)(char *name, void *const userdata);
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
void *const userdata);
typedef void (*parser_start_callback)(char *name, char **attrs, void *userdata);
typedef void (*parser_end_callback)(char *name, void *userdata);
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza, void *userdata);
parser_t *parser_new(xmpp_ctx_t *ctx,
parser_start_callback startcb,

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
@@ -60,7 +61,7 @@ static xmpp_ctx_t *mem_ctx = NULL;
static void *parser_mem_malloc(size_t size)
{
if (mem_ctx != NULL)
return xmpp_alloc(mem_ctx, size);
return strophe_alloc(mem_ctx, size);
else
return NULL;
}
@@ -68,7 +69,7 @@ static void *parser_mem_malloc(size_t size)
static void *parser_mem_realloc(void *ptr, size_t size)
{
if (mem_ctx != NULL)
return xmpp_realloc(mem_ctx, ptr, size);
return strophe_realloc(mem_ctx, ptr, size);
else
return NULL;
}
@@ -76,7 +77,7 @@ static void *parser_mem_realloc(void *ptr, size_t size)
static void parser_mem_free(void *ptr)
{
if (mem_ctx != NULL)
xmpp_free(mem_ctx, ptr);
strophe_free(mem_ctx, ptr);
}
static const XML_Memory_Handling_Suite parser_mem_suite = {
@@ -95,11 +96,11 @@ static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
c = strchr(nsname, namespace_sep);
if (c == NULL)
return xmpp_strdup(ctx, nsname);
return strophe_strdup(ctx, nsname);
c++;
len = strlen(c);
result = xmpp_alloc(ctx, len + 1);
result = strophe_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, c, len);
result[len] = '\0';
@@ -116,7 +117,7 @@ static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
c = strchr(nsname, namespace_sep);
if (c != NULL) {
result = xmpp_alloc(ctx, (c - nsname) + 1);
result = strophe_alloc(ctx, (c - nsname) + 1);
if (result != NULL) {
memcpy(result, nsname, (c - nsname));
result[c - nsname] = '\0';
@@ -138,7 +139,7 @@ static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
/* namespaced attributes aren't used in xmpp, discard namespace */
attr = _xml_name(stanza->ctx, attrs[i]);
xmpp_stanza_set_attribute(stanza, attr, attrs[i + 1]);
xmpp_free(stanza->ctx, attr);
strophe_free(stanza->ctx, attr);
}
}
@@ -152,10 +153,9 @@ static void complete_inner_text(parser_t *parser)
/* FIXME: disconnect on allocation error */
if (stanza) {
xmpp_stanza_set_text(stanza, parser->inner_text);
xmpp_stanza_add_child(parser->stanza, stanza);
xmpp_stanza_release(stanza);
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
}
xmpp_free(parser->ctx, parser->inner_text);
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
parser->inner_text_size = 0;
parser->inner_text_used = 0;
@@ -181,7 +181,8 @@ _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
if (!parser->stanza && parser->depth != 1) {
/* something terrible happened */
/* FIXME: shutdown disconnect */
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
strophe_error(parser->ctx, "parser",
"oops, where did our stanza go?");
} else {
child = xmpp_stanza_new(parser->ctx);
if (!child) {
@@ -194,17 +195,16 @@ _start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
if (parser->stanza != NULL) {
complete_inner_text(parser);
xmpp_stanza_add_child(parser->stanza, child);
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
}
parser->stanza = child;
}
}
if (ns)
xmpp_free(parser->ctx, ns);
strophe_free(parser->ctx, ns);
if (name)
xmpp_free(parser->ctx, name);
strophe_free(parser->ctx, name);
parser->depth++;
}
@@ -246,10 +246,10 @@ static void _characters(void *userdata, const XML_Char *s, int len)
if (parser->inner_text_used + len >= parser->inner_text_size) {
parser->inner_text_size =
parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
p = xmpp_realloc(parser->ctx, parser->inner_text,
parser->inner_text_size);
p = strophe_realloc(parser->ctx, parser->inner_text,
parser->inner_text_size);
if (p == NULL) {
xmpp_free(parser->ctx, parser->inner_text);
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
parser->inner_text_used = 0;
parser->inner_text_size = 0;
@@ -270,7 +270,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
{
parser_t *parser;
parser = xmpp_alloc(ctx, sizeof(parser_t));
parser = strophe_alloc(ctx, sizeof(parser_t));
if (parser != NULL) {
parser->ctx = ctx;
parser->expat = NULL;
@@ -295,18 +295,32 @@ char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
return _xml_name(ctx, nsname);
}
static void _free_parent_stanza(xmpp_stanza_t *stanza)
{
xmpp_stanza_t *parent;
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
;
xmpp_stanza_release(parent);
}
/* free a parser */
void parser_free(parser_t *parser)
{
if (parser->expat)
XML_ParserFree(parser->expat);
if (parser->stanza) {
_free_parent_stanza(parser->stanza);
parser->stanza = NULL;
}
if (parser->inner_text) {
xmpp_free(parser->ctx, parser->inner_text);
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}
xmpp_free(parser->ctx, parser);
strophe_free(parser->ctx, parser);
}
/* shuts down and restarts XML parser. true on success */
@@ -330,12 +344,12 @@ int parser_reset(parser_t *parser)
}
if (parser->stanza) {
xmpp_stanza_release(parser->stanza);
_free_parent_stanza(parser->stanza);
parser->stanza = NULL;
}
if (parser->inner_text) {
xmpp_free(parser->ctx, parser->inner_text);
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}

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
@@ -48,12 +49,12 @@ _set_attributes(xmpp_stanza_t *stanza, int nattrs, const xmlChar **attrs)
/* SAX2 uses array of localname/prefix/uri/value_begin/value_end */
for (i = 0; i < nattrs * 5; i += 5) {
len = attrs[i + 4] - attrs[i + 3];
value = xmpp_alloc(stanza->ctx, len + 1);
value = strophe_alloc(stanza->ctx, len + 1);
if (value) {
memcpy(value, attrs[i + 3], len);
value[len] = '\0';
xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], value);
xmpp_free(stanza->ctx, value);
strophe_free(stanza->ctx, value);
}
}
}
@@ -70,7 +71,7 @@ _convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
if (!attrs)
return NULL;
ret = xmpp_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
ret = strophe_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
if (!ret)
return NULL;
memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *));
@@ -80,11 +81,11 @@ _convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
o = c * 2;
len = attrs[i + 4] - attrs[i + 3];
value = xmpp_alloc(parser->ctx, len + 1);
value = strophe_alloc(parser->ctx, len + 1);
if (value) {
memcpy(value, attrs[i + 3], len);
value[len] = '\0';
ret[o] = xmpp_strdup(parser->ctx, (char *)attrs[i]);
ret[o] = strophe_strdup(parser->ctx, (char *)attrs[i]);
ret[o + 1] = value;
}
}
@@ -101,12 +102,12 @@ static void _free_cbattrs(parser_t *parser, char **attrs)
for (i = 0; attrs[i]; i += 2) {
if (attrs[i])
xmpp_free(parser->ctx, attrs[i]);
strophe_free(parser->ctx, attrs[i]);
if (attrs[i + 1])
xmpp_free(parser->ctx, attrs[i + 1]);
strophe_free(parser->ctx, attrs[i + 1]);
}
xmpp_free(parser->ctx, attrs);
strophe_free(parser->ctx, attrs);
}
static void _start_element(void *userdata,
@@ -140,7 +141,8 @@ static void _start_element(void *userdata,
if (!parser->stanza && parser->depth != 1) {
/* something terrible happened */
/* FIXME: we should probably trigger a disconnect */
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
strophe_error(parser->ctx, "parser",
"oops, where did our stanza go?");
} else if (!parser->stanza) {
/* starting a new toplevel stanza */
parser->stanza = xmpp_stanza_new(parser->ctx);
@@ -163,10 +165,7 @@ static void _start_element(void *userdata,
xmpp_stanza_set_ns(child, (char *)uri);
/* add child to parent */
xmpp_stanza_add_child(parser->stanza, child);
/* the child is owned by the toplevel stanza now */
xmpp_stanza_release(child);
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
/* make child the current stanza */
parser->stanza = child;
@@ -222,8 +221,7 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
}
xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
xmpp_stanza_add_child(parser->stanza, stanza);
xmpp_stanza_release(stanza);
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
}
/* create a new parser */
@@ -235,7 +233,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
{
parser_t *parser;
parser = xmpp_alloc(ctx, sizeof(parser_t));
parser = strophe_alloc(ctx, sizeof(parser_t));
if (parser != NULL) {
parser->ctx = ctx;
parser->xmlctx = NULL;
@@ -259,7 +257,16 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
{
return xmpp_strdup(ctx, nsname);
return strophe_strdup(ctx, nsname);
}
static void _free_parent_stanza(xmpp_stanza_t *stanza)
{
xmpp_stanza_t *parent;
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
;
xmpp_stanza_release(parent);
}
/* free a parser */
@@ -268,8 +275,8 @@ void parser_free(parser_t *parser)
if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx);
if (parser->stanza)
xmpp_stanza_release(parser->stanza);
xmpp_free(parser->ctx, parser);
_free_parent_stanza(parser->stanza);
strophe_free(parser->ctx, parser);
}
/* shuts down and restarts XML parser. true on success */
@@ -278,7 +285,7 @@ int parser_reset(parser_t *parser)
if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx);
if (parser->stanza)
xmpp_stanza_release(parser->stanza);
_free_parent_stanza(parser->stanza);
parser->stanza = NULL;
parser->depth = 0;

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
@@ -23,12 +24,24 @@
#include <string.h> /* memeset */
#include <time.h> /* clock, time */
#include "common.h" /* xmpp_alloc, xmpp_free */
#include "ostypes.h" /* uint8_t, uint32_t, size_t */
#include "sha1.h"
#include "snprintf.h" /* xmpp_snprintf */
#if !defined(_WIN32)
#include <unistd.h>
#endif
#include "rand.h" /* xmpp_rand_t */
#if !defined(DONT_USE_GETRANDOM) && defined(__linux__) && \
defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ(2, 25)
#define USE_GETRANDOM
#include <sys/random.h>
#include <errno.h>
#endif
#endif
#include "common.h" /* strophe_alloc, strophe_free */
#include "ostypes.h" /* uint8_t, uint32_t, size_t */
#ifndef USE_GETRANDOM
#include "sha1.h"
#define outlen SHA1_DIGEST_SIZE
#define seedlen (440 / 8)
@@ -55,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.
@@ -261,7 +274,7 @@ static void xmpp_rand_reseed(xmpp_rand_t *rand)
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
{
xmpp_rand_t *out = xmpp_alloc(ctx, sizeof(*out));
xmpp_rand_t *out = strophe_alloc(ctx, sizeof(*out));
if (out != NULL) {
memset(out, 0, sizeof(*out));
}
@@ -270,21 +283,72 @@ xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
{
xmpp_free(ctx, rand);
strophe_free(ctx, rand);
}
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
{
int rc;
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
if (rc == RESEED_NEEDED) {
xmpp_rand_reseed(rand);
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
assert(rc == 0);
size_t gen, tot = 0;
while (tot < len) {
gen = len - tot;
if (gen > GENERATE_MAX)
gen = GENERATE_MAX;
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
if (rc == RESEED_NEEDED) {
xmpp_rand_reseed(rand);
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
assert(rc == 0);
}
tot += gen;
}
}
#else
static int _read_getrandom(void *p, size_t n)
{
unsigned char *q = (unsigned char *)p;
while (n > 0u) {
ssize_t ret = getrandom(q, n, 0);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
return 1;
}
q += ret;
n -= (size_t)ret;
}
return 0;
}
struct _xmpp_rand_t {
char nothing;
};
static xmpp_rand_t _xmpp_rand;
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
{
UNUSED(ctx);
return &_xmpp_rand;
}
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
{
UNUSED(ctx);
assert(rand == &_xmpp_rand);
}
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
{
assert(rand == &_xmpp_rand);
assert(_read_getrandom(output, len) == 0);
}
#endif
int xmpp_rand(xmpp_rand_t *rand)
{
int result;

View File

@@ -1,65 +0,0 @@
/* rand.h
* strophe XMPP client library -- pseudo-random number generator
*
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
*
* This software is provided AS-IS with no warranty, either express
* or implied.
*
* This program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @file
* Pseudo-random number generator.
*/
#ifndef __LIBSTROPHE_RAND_H__
#define __LIBSTROPHE_RAND_H__
#include <stddef.h> /* size_t */
#include "strophe.h" /* xmpp_ctx_t */
typedef struct _xmpp_rand_t xmpp_rand_t;
/** Create new xmpp_rand_t object.
*
* @param ctx A Strophe context object
*
* @ingroup Random
*/
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
/** Destroy an xmpp_rand_t object.
*
* @param ctx A Strophe context object
*
* @ingroup Random
*/
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
/** Generate random integer.
* Analogue of rand(3).
*
* @ingroup Random
*/
int xmpp_rand(xmpp_rand_t *rand);
/** Generate random bytes.
* Generates len bytes and stores them to the output buffer.
*
* @ingroup Random
*/
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
/** Generate a nonce that is printable randomized string.
* This function doesn't allocate memory and doesn't fail.
*
* @param output A buffer where a NULL-terminated string will be placed.
* The string will contain len-1 printable symbols.
* @param len Number of bytes reserved for the output string, including
* end of line '\0'.
*
* @ingroup Random
*/
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
#endif /* __LIBSTROPHE_RAND_H__ */

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
@@ -91,6 +92,25 @@ void resolver_shutdown(void)
#endif
}
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
const char *host,
unsigned short port,
unsigned short prio,
unsigned short weight)
{
resolver_srv_rr_t *rr = strophe_alloc(ctx, sizeof(*rr));
if (rr) {
memset(rr, 0, sizeof(*rr));
rr->port = port;
rr->priority = prio;
rr->weight = weight;
if (host) {
snprintf(rr->target, sizeof(rr->target), "%s", host);
}
}
return rr;
}
static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
{
resolver_srv_rr_t *rr_head;
@@ -158,6 +178,10 @@ int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
#else
set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list);
if (set != XMPP_DOMAIN_FOUND && *srv_rr_list != NULL) {
resolver_srv_free(ctx, *srv_rr_list);
*srv_rr_list = NULL;
}
#endif
resolver_srv_list_sort(srv_rr_list);
@@ -179,8 +203,8 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
(void)buf;
(void)len;
xmpp_snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service, proto,
domain);
strophe_snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service,
proto, domain);
*srv_rr_list = NULL;
@@ -196,7 +220,7 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
return set;
#endif /* _WIN32 */
buf = xmpp_alloc(ctx, RESOLVER_BUF_MAX);
buf = strophe_alloc(ctx, RESOLVER_BUF_MAX);
if (buf == NULL)
return XMPP_DOMAIN_NOT_FOUND;
@@ -210,7 +234,7 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx,
if (len > 0)
set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
xmpp_free(ctx, buf);
strophe_free(ctx, buf);
#endif /* HAVE_CARES */
@@ -223,7 +247,7 @@ void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list)
while (srv_rr_list != NULL) {
rr = srv_rr_list->next;
xmpp_free(ctx, srv_rr_list);
strophe_free(ctx, srv_rr_list);
srv_rr_list = rr;
}
}
@@ -321,6 +345,9 @@ static unsigned message_name_get(const unsigned char *buf,
if (i >= buf_len)
return 0;
pointer = (label_len & 0x3f) << 8 | buf[i++];
/* Prevent infinite looping */
if (pointer >= buf_offset)
return 0;
if (name != NULL && name_len >= name_max && name_max > 0) {
/* We have filled the name buffer. Don't pass it recursively. */
name[name_max - 1] = '\0';
@@ -431,17 +458,19 @@ static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
j += 10;
if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
rr = xmpp_alloc(ctx, sizeof(*rr));
rr->next = *srv_rr_list;
rr->priority = xmpp_ntohs_ptr(&buf[j]);
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
name_len = message_name_get(buf, len, j + 6, rr->target,
sizeof(rr->target));
if (name_len > 0)
*srv_rr_list = rr;
else
xmpp_free(ctx, rr); /* skip broken record */
rr = resolver_srv_rr_new(ctx, NULL, 0, 0, 0);
if (rr) {
rr->next = *srv_rr_list;
rr->priority = xmpp_ntohs_ptr(&buf[j]);
rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
name_len = message_name_get(buf, len, j + 6, rr->target,
sizeof(rr->target));
if (name_len > 0)
*srv_rr_list = rr;
else
strophe_free(ctx, rr); /* skip broken record */
}
}
j += rdlength;
}
@@ -480,7 +509,7 @@ static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
item = srv;
while (item != NULL) {
rr = xmpp_alloc(ctx, sizeof(*rr));
rr = strophe_alloc(ctx, sizeof(*rr));
if (rr == NULL)
break;
rr->next = *srv_rr_list;
@@ -601,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;
}
@@ -705,15 +734,15 @@ static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx,
while (current) {
if (current->wType == DNS_TYPE_SRV) {
rr = xmpp_alloc(ctx, sizeof(*rr));
rr = strophe_alloc(ctx, sizeof(*rr));
if (rr == NULL)
break;
rr->next = *srv_rr_list;
rr->port = current->Data.Srv.wPort;
rr->priority = current->Data.Srv.wPriority;
rr->weight = current->Data.Srv.wWeight;
xmpp_snprintf(rr->target, sizeof(rr->target), "%s",
current->Data.Srv.pNameTarget);
strophe_snprintf(rr->target, sizeof(rr->target), "%s",
current->Data.Srv.pNameTarget);
*srv_rr_list = rr;
}
current = current->pNext;

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

View File

@@ -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
@@ -22,7 +23,6 @@
#include "sasl.h"
#include "md5.h"
#include "scram.h"
#include "rand.h"
#include "util.h"
/* strtok_s() has appeared in visual studio 2005.
@@ -49,14 +49,14 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
idlen = strlen(authid);
passlen = strlen(password);
msglen = 2 + idlen + passlen;
msg = xmpp_alloc(ctx, msglen);
msg = strophe_alloc(ctx, msglen);
if (msg != NULL) {
msg[0] = '\0';
memcpy(msg + 1, authid, idlen);
msg[1 + idlen] = '\0';
memcpy(msg + 1 + idlen + 1, password, passlen);
result = xmpp_base64_encode(ctx, (unsigned char *)msg, msglen);
xmpp_free(ctx, msg);
strophe_free(ctx, msg);
}
return result;
@@ -65,11 +65,11 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
/** helpers for digest auth */
/* create a new, null-terminated string from a substring */
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
static char *_make_string(xmpp_ctx_t *ctx, const char *s, unsigned len)
{
char *result;
result = xmpp_alloc(ctx, len + 1);
result = strophe_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, s, len);
result[len] = '\0';
@@ -83,7 +83,7 @@ static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
char *result;
size_t len = strlen(s);
result = xmpp_alloc(ctx, len + 3);
result = strophe_alloc(ctx, len + 3);
if (result != NULL) {
result[0] = '"';
memcpy(result + 1, s, len);
@@ -103,11 +103,11 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
if (text == NULL) {
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
strophe_error(ctx, "SASL", "couldn't Base64 decode challenge!");
return NULL;
}
result = hash_new(ctx, 10, xmpp_free);
result = hash_new(ctx, 10, strophe_free);
if (result != NULL) {
s = text;
while (*s != '\0') {
@@ -145,16 +145,16 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
s = t;
}
if (value == NULL) {
xmpp_free(ctx, key);
strophe_free(ctx, key);
break;
}
/* TODO: check for collisions per spec */
hash_add(result, key, value);
/* hash table now owns the value, free the key */
xmpp_free(ctx, key);
strophe_free(ctx, key);
}
}
xmpp_free(ctx, text);
strophe_free(ctx, text);
return result;
}
@@ -182,7 +182,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
/* allocate a zero-length string if necessary */
if (buf == NULL) {
buf = xmpp_alloc(ctx, 1);
buf = strophe_alloc(ctx, 1);
buf[0] = '\0';
}
if (buf == NULL)
@@ -192,7 +192,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
olen = strlen(buf);
value = hash_get(table, key);
if (value == NULL) {
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
strophe_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
value = "";
}
if (quote) {
@@ -205,7 +205,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
keylen = strlen(key);
valuelen = strlen(qvalue);
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
buf = xmpp_realloc(ctx, buf, olen + nlen);
buf = strophe_realloc(ctx, buf, olen + nlen);
if (buf != NULL) {
c = buf + olen;
@@ -220,7 +220,7 @@ _add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
}
if (quote)
xmpp_free(ctx, (char *)qvalue);
strophe_free(ctx, (char *)qvalue);
return buf;
}
@@ -255,7 +255,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
/* parse the challenge */
table = _parse_digest_challenge(ctx, challenge);
if (table == NULL) {
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
strophe_error(ctx, "SASL", "couldn't parse digest challenge");
return NULL;
}
@@ -266,18 +266,18 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
server */
realm = hash_get(table, "realm");
if (realm == NULL || strlen(realm) == 0) {
hash_add(table, "realm", xmpp_strdup(ctx, domain));
hash_add(table, "realm", strophe_strdup(ctx, domain));
realm = hash_get(table, "realm");
}
/* add our response fields */
hash_add(table, "username", xmpp_strdup(ctx, node));
hash_add(table, "username", strophe_strdup(ctx, node));
xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce));
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
hash_add(table, "cnonce", strophe_strdup(ctx, cnonce));
hash_add(table, "nc", strophe_strdup(ctx, "00000001"));
if (hash_get(table, "qop") == NULL)
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
hash_add(table, "qop", strophe_strdup(ctx, "auth"));
value = strophe_alloc(ctx, 5 + strlen(domain) + 1);
memcpy(value, "xmpp/", 5);
memcpy(value + 5, domain, strlen(domain));
value[5 + strlen(domain)] = '\0';
@@ -343,7 +343,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
MD5Update(&MD5, (unsigned char *)hex, 32);
MD5Final(digest, &MD5);
response = xmpp_alloc(ctx, 32 + 1);
response = strophe_alloc(ctx, 32 + 1);
_digest_to_hex((char *)digest, hex);
memcpy(response, hex, 32);
response[32] = '\0';
@@ -361,13 +361,13 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx,
result = _add_key(ctx, table, "response", result, 0);
result = _add_key(ctx, table, "charset", result, 0);
xmpp_free(ctx, node);
xmpp_free(ctx, domain);
strophe_free(ctx, node);
strophe_free(ctx, domain);
hash_release(table); /* also frees value strings */
/* reuse response for the base64 encode of our result */
response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result));
xmpp_free(ctx, result);
strophe_free(ctx, result);
return response;
}
@@ -375,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,
@@ -398,10 +399,11 @@ char *sasl_scram(xmpp_ctx_t *ctx,
char *result = NULL;
size_t response_len;
size_t auth_len;
int l;
UNUSED(jid);
tmp = xmpp_strdup(ctx, challenge);
tmp = strophe_strdup(ctx, challenge);
if (!tmp) {
return NULL;
}
@@ -428,56 +430,63 @@ char *sasl_scram(xmpp_ctx_t *ctx,
}
ival = strtol(i, &saveptr, 10);
auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
auth = xmpp_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 = xmpp_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;
}
xmpp_snprintf(response, response_len, "c=biws,%s", r);
xmpp_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) {
xmpp_free(ctx, sign_b64);
goto out_response;
strophe_free(ctx, sign_b64);
goto out_auth;
}
strcat(response, ",p=");
strcat(response, sign_b64);
xmpp_free(ctx, sign_b64);
strophe_free(ctx, sign_b64);
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:
xmpp_free(ctx, response);
out_auth:
xmpp_free(ctx, auth);
strophe_free(ctx, auth);
out_response:
strophe_free(ctx, response);
out_sval:
xmpp_free(ctx, sval);
strophe_free(ctx, sval);
out:
xmpp_free(ctx, tmp);
strophe_free(ctx, tmp);
return result;
}

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

@@ -206,9 +206,7 @@ void crypto_SHA1_Init(SHA1_CTX *context)
}
/* Run your data through this. */
void crypto_SHA1_Update(SHA1_CTX *context,
const uint8_t *data,
const size_t len)
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len)
{
size_t i, j;

View File

@@ -24,9 +24,7 @@ typedef struct {
#define SHA1_DIGEST_SIZE 20
void crypto_SHA1_Init(SHA1_CTX *context);
void crypto_SHA1_Update(SHA1_CTX *context,
const uint8_t *data,
const size_t len);
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len);
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest);
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest);

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

@@ -61,7 +61,7 @@
/* JAM: we don't need this - #include "config.h" */
/* JAM: changed declarations to xmpp_snprintf and xmpp_vsnprintf to
/* JAM: changed declarations to strophe_snprintf and strophe_vsnprintf to
avoid namespace collision. */
#include "snprintf.h"
@@ -706,7 +706,7 @@ static int dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
return 1;
}
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
if (str != NULL && count > 0)
str[0] = 0;
@@ -716,13 +716,13 @@ int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
#ifndef HAVE_SNPRINTF
/* VARARGS3 */
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
int strophe_snprintf(char *str, size_t count, const char *fmt, ...)
{
VA_LOCAL_DECL;
int total;
VA_START(fmt);
total = xmpp_vsnprintf(str, count, fmt, ap);
total = strophe_vsnprintf(str, count, fmt, ap);
VA_END;
return total;
}

View File

@@ -20,15 +20,15 @@
#endif
#ifdef HAVE_SNPRINTF
#define xmpp_snprintf snprintf
#define strophe_snprintf snprintf
#else
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
int strophe_snprintf(char *str, size_t count, const char *fmt, ...);
#endif
#ifdef HAVE_VSNPRINTF
#define xmpp_vsnprintf vsnprintf
#define strophe_vsnprintf vsnprintf
#else
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
#endif
#endif /* __LIBSTROPHE_SNPRINTF_H__ */

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
@@ -24,6 +25,7 @@
#include <iphlpapi.h>
#include <mstcpip.h> /* tcp_keepalive */
#else
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
@@ -33,8 +35,31 @@
#include <fcntl.h>
#endif
#include "sock.h"
#include "snprintf.h"
#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;
struct addrinfo *ainfo_list;
struct addrinfo *ainfo_cur;
resolver_srv_rr_t *srv_rr_list;
resolver_srv_rr_t *srv_rr_cur;
const char *host;
unsigned short port;
};
void sock_initialize(void)
{
@@ -51,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
@@ -69,52 +95,181 @@ static int _in_progress(int error)
#endif
}
sock_t sock_connect(const char *const host, const unsigned short port)
static void sock_getaddrinfo(xmpp_sock_t *xsock)
{
sock_t sock;
char service[6];
struct addrinfo *res, *ainfo, hints;
int err;
struct addrinfo hints;
int rc;
xmpp_snprintf(service, 6, "%u", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(host, service, &hints, &res);
if (err != 0)
return -1;
for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock < 0)
continue;
err = sock_set_nonblocking(sock);
if (err == 0) {
err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
if (err == 0 || _in_progress(sock_error()))
break;
}
sock_close(sock);
if (xsock->ainfo_list) {
freeaddrinfo(xsock->ainfo_list);
xsock->ainfo_list = NULL;
}
freeaddrinfo(res);
sock = ainfo == NULL ? -1 : sock;
if (xsock->srv_rr_cur) {
/* Cache host and port for debug logs. */
xsock->host = xsock->srv_rr_cur->target;
xsock->port = xsock->srv_rr_cur->port;
strophe_snprintf(service, 6, "%u", xsock->srv_rr_cur->port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
rc = getaddrinfo(xsock->srv_rr_cur->target, service, &hints,
&xsock->ainfo_list);
if (rc != 0) {
strophe_debug(xsock->ctx, "sock",
"getaddrinfo() failed with %s (%d)", gai_strerror(rc),
rc);
xsock->ainfo_list = NULL;
}
}
xsock->ainfo_cur = xsock->ainfo_list;
}
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port)
{
xmpp_ctx_t *ctx = conn->ctx;
xmpp_sock_t *xsock;
int found = XMPP_DOMAIN_NOT_FOUND;
xsock = strophe_alloc(ctx, sizeof(*xsock));
if (!xsock) {
return NULL;
}
xsock->ctx = ctx;
xsock->conn = conn;
xsock->host = NULL;
xsock->port = 0;
if (!host) {
found = resolver_srv_lookup(ctx, "xmpp-client", "tcp", domain,
&xsock->srv_rr_list);
if (XMPP_DOMAIN_NOT_FOUND == found)
strophe_debug(ctx, "sock",
"SRV lookup failed, connecting via domain.");
}
if (XMPP_DOMAIN_NOT_FOUND == found) {
/* Resolution failed or the host is provided explicitly. */
xsock->srv_rr_list =
resolver_srv_rr_new(ctx, host ? host : domain, port, 0, 0);
}
xsock->srv_rr_cur = xsock->srv_rr_list;
xsock->ainfo_list = NULL;
sock_getaddrinfo(xsock);
if (xsock->srv_rr_cur)
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
return xsock;
}
void sock_free(xmpp_sock_t *xsock)
{
if (!xsock)
return;
if (xsock->ainfo_list)
freeaddrinfo(xsock->ainfo_list);
if (xsock->srv_rr_list)
resolver_srv_free(xsock->ctx, xsock->srv_rr_list);
strophe_free(xsock->ctx, xsock);
}
static const char *_sockaddr2str(struct sockaddr *sa, char *buf, size_t buflen)
{
buf[0] = '\0';
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, buflen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf,
buflen);
break;
default:
strophe_snprintf(buf, buflen, "<Unknown>");
}
return buf;
}
sock_t sock_connect(xmpp_sock_t *xsock)
{
struct addrinfo *ainfo;
sock_t sock;
int rc;
char buf[64];
do {
if (!xsock->ainfo_cur) {
sock_getaddrinfo(xsock);
if (xsock->srv_rr_cur)
xsock->srv_rr_cur = xsock->srv_rr_cur->next;
}
if (!xsock->ainfo_cur) {
/* We tried all available addresses. */
return INVALID_SOCKET;
}
ainfo = xsock->ainfo_cur;
strophe_debug(xsock->ctx, "sock", "Connecting to %s:%u via %s",
xsock->host, xsock->port,
_sockaddr2str(ainfo->ai_addr, buf, sizeof(buf)));
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock != INVALID_SOCKET) {
rc = 0;
if (xsock->conn->sockopt_cb) {
/* Don't allow user to overwrite sockfd value. */
sock_t sock_copy = sock;
rc = xsock->conn->sockopt_cb(xsock->conn, &sock_copy);
if (rc != 0) {
strophe_debug(xsock->ctx, "sock",
"User's setsockopt callback"
" failed with %d (errno=%d)",
rc, errno);
}
}
if (rc == 0)
rc = sock_set_nonblocking(sock);
if (rc == 0)
rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
/* Assume only connect() can cause "in progress" error. */
if (rc != 0 && !_in_progress(sock_error(NULL))) {
sock_close(sock);
sock = INVALID_SOCKET;
}
}
strophe_debug(xsock->ctx, "sock", "sock_connect() result %d", sock);
xsock->ainfo_cur = xsock->ainfo_cur->ai_next;
} while (sock == INVALID_SOCKET);
return sock;
}
int sock_set_keepalive(const sock_t sock, int timeout, int interval)
int sock_set_keepalive(sock_t sock,
int timeout,
int interval,
int count,
unsigned int user_timeout)
{
int ret;
int optval = (timeout && interval) ? 1 : 0;
/* This function doesn't change maximum number of keepalive probes */
UNUSED(count);
UNUSED(user_timeout);
#ifdef _WIN32
struct tcp_keepalive ka;
@@ -149,12 +304,58 @@ int sock_set_keepalive(const sock_t sock, int timeout, int interval)
return ret;
#endif /* TCP_KEEPINTVL */
}
if (count) {
#ifdef TCP_KEEPCNT
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
if (ret < 0)
return ret;
#endif /* TCP_KEEPCNT */
}
if (user_timeout) {
#ifdef TCP_USER_TIMEOUT
ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
sizeof(user_timeout));
if (ret < 0)
return ret;
#elif defined(TCP_RXT_CONNDROPTIME)
int rxt = user_timeout / 1000;
ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
sizeof(rxt));
if (ret < 0)
return ret;
#endif /* TCP_USER_TIMEOUT */
}
#endif /* _WIN32 */
return ret;
}
int sock_close(const sock_t sock)
/** Example sockopt callback function
* An example function that can be used to set reasonable default keepalive
* options on sockets when registered for a connection with
* xmpp_conn_set_sockopt_callback()
*
* @param conn a Strophe connection object
* @param socket pointer to a socket descriptor
*
* @see xmpp_sockopt_callback for details on the `socket` parameter
* @ingroup Connections
*/
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *socket)
{
sock_t sock = *((sock_t *)socket);
return sock_set_keepalive(
sock, conn->ka_timeout, conn->ka_interval, conn->ka_count,
conn->ka_count
? (conn->ka_timeout + conn->ka_interval * conn->ka_count) * 1000
: 0);
}
int sock_close(sock_t sock)
{
#ifdef _WIN32
return closesocket(sock);
@@ -180,28 +381,29 @@ static int _sock_set_blocking_mode(sock_t sock, int blocking)
#endif
}
int sock_set_blocking(const sock_t sock)
int sock_set_blocking(sock_t sock)
{
return _sock_set_blocking_mode(sock, 1);
}
int sock_set_nonblocking(const sock_t sock)
int sock_set_nonblocking(sock_t sock)
{
return _sock_set_blocking_mode(sock, 0);
}
int sock_read(const sock_t sock, void *const buff, const 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(const sock_t sock, const void *const buff, const 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(const int error)
int sock_is_recoverable(struct conn_interface *intf, int error)
{
UNUSED(intf);
#ifdef _WIN32
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
error == WSAEINPROGRESS);
@@ -210,7 +412,7 @@ int sock_is_recoverable(const int error)
#endif
}
int sock_connect_error(const sock_t sock)
int sock_connect_error(sock_t sock)
{
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
@@ -230,15 +432,15 @@ int sock_connect_error(const 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
@@ -20,26 +21,43 @@
#ifndef _WIN32
typedef int sock_t;
#define INVALID_SOCKET (-1)
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <mstcpip.h> /* tcp_keepalive */
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);
sock_t sock_connect(const char *const host, const unsigned short port);
int sock_close(const sock_t sock);
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain,
const char *host,
unsigned short port);
void sock_free(xmpp_sock_t *xsock);
sock_t sock_connect(xmpp_sock_t *xsock);
int sock_close(sock_t sock);
int sock_set_blocking(const sock_t sock);
int sock_set_nonblocking(const sock_t sock);
int sock_read(const sock_t sock, void *const buff, const size_t len);
int sock_write(const sock_t sock, const void *const buff, const size_t len);
int sock_is_recoverable(const int error);
int sock_set_blocking(sock_t sock);
int sock_set_nonblocking(sock_t sock);
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(const sock_t sock);
int sock_set_keepalive(const sock_t sock, int timeout, int interval);
int sock_connect_error(sock_t sock);
int sock_set_keepalive(sock_t sock,
int timeout,
int interval,
int count,
unsigned int user_timeout);
#endif /* __LIBSTROPHE_SOCK_H__ */

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
@@ -39,17 +40,12 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
{
xmpp_stanza_t *stanza;
stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t));
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;
@@ -64,7 +60,7 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza)
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza)
{
stanza->ref++;
@@ -74,8 +70,7 @@ xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza)
/*
* Copy the attributes of stanza src into stanza dst. Return -1 on error.
*/
static int _stanza_copy_attributes(xmpp_stanza_t *dst,
const xmpp_stanza_t *const src)
static int _stanza_copy_attributes(xmpp_stanza_t *dst, const xmpp_stanza_t *src)
{
hash_iterator_t *iter;
const char *key;
@@ -114,7 +109,7 @@ static int _stanza_copy_attributes(xmpp_stanza_t *dst,
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza)
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza)
{
xmpp_stanza_t *copy, *child, *copychild, *tail;
@@ -125,7 +120,7 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza)
copy->type = stanza->type;
if (stanza->data) {
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
copy->data = strophe_strdup(stanza->ctx, stanza->data);
if (!copy->data)
goto copy_error;
}
@@ -169,7 +164,7 @@ copy_error:
*
* @ingroup Stanza
*/
int xmpp_stanza_release(xmpp_stanza_t *const stanza)
int xmpp_stanza_release(xmpp_stanza_t *stanza)
{
int released = 0;
xmpp_stanza_t *child, *tchild;
@@ -183,14 +178,15 @@ int xmpp_stanza_release(xmpp_stanza_t *const stanza)
while (child) {
tchild = child;
child = child->next;
tchild->next = NULL;
xmpp_stanza_release(tchild);
}
if (stanza->attributes)
hash_release(stanza->attributes);
if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
xmpp_free(stanza->ctx, stanza);
strophe_free(stanza->ctx, stanza->data);
strophe_free(stanza->ctx, stanza);
released = 1;
}
@@ -205,7 +201,7 @@ int xmpp_stanza_release(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza)
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza)
{
return stanza->ctx;
}
@@ -218,7 +214,7 @@ xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
int xmpp_stanza_is_text(xmpp_stanza_t *const stanza)
int xmpp_stanza_is_text(xmpp_stanza_t *stanza)
{
return (stanza && stanza->type == XMPP_STANZA_TEXT);
}
@@ -231,7 +227,7 @@ int xmpp_stanza_is_text(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza)
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza)
{
return (stanza && stanza->type == XMPP_STANZA_TAG);
}
@@ -242,7 +238,7 @@ int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza)
* On failure, returns NULL.
*/
static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
static char *_escape_xml(xmpp_ctx_t *ctx, char *text)
{
size_t len = 0;
char *src;
@@ -264,7 +260,7 @@ static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
len++;
}
}
if ((buf = xmpp_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
if ((buf = strophe_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
return NULL; /* Error */
dst = buf;
for (src = text; *src != '\0'; src++) {
@@ -295,11 +291,8 @@ static char *_escape_xml(xmpp_ctx_t *const ctx, char *text)
}
/* small helper function */
static void _render_update(int *written,
const int length,
const int lastwrite,
size_t *left,
char **ptr)
static void _render_update(
int *written, int length, int lastwrite, size_t *left, char **ptr)
{
*written += lastwrite;
@@ -317,9 +310,8 @@ static void _render_update(int *written,
* return values < 0 indicate some error occurred,
* and return values > buflen indicate buffer was not large enough
*/
static int _render_stanza_recursive(xmpp_stanza_t *stanza,
char *const buf,
size_t const buflen)
static int
_render_stanza_recursive(xmpp_stanza_t *stanza, char *buf, size_t buflen)
{
char *ptr = buf;
size_t left = buflen;
@@ -341,8 +333,8 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
tmp = _escape_xml(stanza->ctx, stanza->data);
if (tmp == NULL)
return XMPP_EMEM;
ret = xmpp_snprintf(ptr, left, "%s", tmp);
xmpp_free(stanza->ctx, tmp);
ret = strophe_snprintf(ptr, left, "%s", tmp);
strophe_free(stanza->ctx, tmp);
if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr);
@@ -351,7 +343,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
return XMPP_EINVOP;
/* write beginning of tag and attributes */
ret = xmpp_snprintf(ptr, left, "<%s", stanza->data);
ret = strophe_snprintf(ptr, left, "<%s", stanza->data);
if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr);
@@ -379,8 +371,8 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
hash_iter_release(iter);
return XMPP_EMEM;
}
ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
xmpp_free(stanza->ctx, tmp);
ret = strophe_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
strophe_free(stanza->ctx, tmp);
if (ret < 0) {
hash_iter_release(iter);
return XMPP_EMEM;
@@ -392,7 +384,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
if (!stanza->children) {
/* write end if singleton tag */
ret = xmpp_snprintf(ptr, left, "/>");
ret = strophe_snprintf(ptr, left, "/>");
if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr);
@@ -400,7 +392,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
/* this stanza has child stanzas */
/* write end of start tag */
ret = xmpp_snprintf(ptr, left, ">");
ret = strophe_snprintf(ptr, left, ">");
if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr);
@@ -418,7 +410,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
}
/* write end tag */
ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data);
ret = strophe_snprintf(ptr, left, "</%s>", stanza->data);
if (ret < 0)
return XMPP_EMEM;
@@ -444,9 +436,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
*
* @ingroup Stanza
*/
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
char **const buf,
size_t *const buflen)
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen)
{
char *buffer, *tmp;
size_t length;
@@ -454,7 +444,7 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
/* allocate a default sized buffer and attempt to render */
length = 1024;
buffer = xmpp_alloc(stanza->ctx, length);
buffer = strophe_alloc(stanza->ctx, length);
if (!buffer) {
*buf = NULL;
*buflen = 0;
@@ -463,16 +453,16 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
ret = _render_stanza_recursive(stanza, buffer, length);
if (ret < 0) {
xmpp_free(stanza->ctx, buffer);
strophe_free(stanza->ctx, buffer);
*buf = NULL;
*buflen = 0;
return ret;
}
if ((size_t)ret > length - 1) {
tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1);
tmp = strophe_realloc(stanza->ctx, buffer, ret + 1);
if (!tmp) {
xmpp_free(stanza->ctx, buffer);
strophe_free(stanza->ctx, buffer);
*buf = NULL;
*buflen = 0;
return XMPP_EMEM;
@@ -482,7 +472,7 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
ret = _render_stanza_recursive(stanza, buffer, length);
if ((size_t)ret > length - 1) {
xmpp_free(stanza->ctx, buffer);
strophe_free(stanza->ctx, buffer);
*buf = NULL;
*buflen = 0;
return XMPP_EMEM;
@@ -507,16 +497,16 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
*
* @ingroup Stanza
*/
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name)
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name)
{
if (stanza->type == XMPP_STANZA_TEXT)
return XMPP_EINVOP;
if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
strophe_free(stanza->ctx, stanza->data);
stanza->type = XMPP_STANZA_TAG;
stanza->data = xmpp_strdup(stanza->ctx, name);
stanza->data = strophe_strdup(stanza->ctx, name);
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
}
@@ -531,7 +521,7 @@ int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza)
{
if (stanza->type == XMPP_STANZA_TEXT)
return NULL;
@@ -546,7 +536,7 @@ const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza)
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza)
{
if (stanza->attributes == NULL) {
return 0;
@@ -569,7 +559,7 @@ int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
const char **attr,
int attrlen)
{
@@ -611,9 +601,9 @@ int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
const char *const key,
const char *const value)
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
const char *key,
const char *value)
{
char *val;
int rc;
@@ -622,19 +612,19 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
return XMPP_EINVOP;
if (!stanza->attributes) {
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
stanza->attributes = hash_new(stanza->ctx, 8, strophe_free);
if (!stanza->attributes)
return XMPP_EMEM;
}
val = xmpp_strdup(stanza->ctx, value);
val = strophe_strdup(stanza->ctx, value);
if (!val) {
return XMPP_EMEM;
}
rc = hash_add(stanza->attributes, key, val);
if (rc < 0) {
xmpp_free(stanza->ctx, val);
strophe_free(stanza->ctx, val);
return XMPP_EMEM;
}
@@ -652,7 +642,7 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
int xmpp_stanza_set_ns(xmpp_stanza_t *const stanza, const char *const ns)
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns)
{
return xmpp_stanza_set_attribute(stanza, "xmlns", ns);
}
@@ -727,7 +717,7 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
*
* @ingroup Stanza
*/
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text)
{
if (stanza->type == XMPP_STANZA_TAG)
return XMPP_EINVOP;
@@ -735,8 +725,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
stanza->type = XMPP_STANZA_TEXT;
if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
stanza->data = xmpp_strdup(stanza->ctx, text);
strophe_free(stanza->ctx, stanza->data);
stanza->data = strophe_strdup(stanza->ctx, text);
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
}
@@ -756,8 +746,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
* @ingroup Stanza
*/
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
const char *const text,
const size_t size)
const char *text,
size_t size)
{
if (stanza->type == XMPP_STANZA_TAG)
return XMPP_EINVOP;
@@ -765,8 +755,8 @@ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
stanza->type = XMPP_STANZA_TEXT;
if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
stanza->data = xmpp_alloc(stanza->ctx, size + 1);
strophe_free(stanza->ctx, stanza->data);
stanza->data = strophe_alloc(stanza->ctx, size + 1);
if (!stanza->data)
return XMPP_EMEM;
@@ -786,7 +776,7 @@ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza)
{
return xmpp_stanza_get_attribute(stanza, "id");
}
@@ -801,7 +791,7 @@ const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza)
{
return xmpp_stanza_get_attribute(stanza, "xmlns");
}
@@ -816,7 +806,7 @@ const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza)
{
return xmpp_stanza_get_attribute(stanza, "type");
}
@@ -831,7 +821,7 @@ const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza)
{
return xmpp_stanza_get_attribute(stanza, "to");
}
@@ -846,11 +836,87 @@ const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza)
{
return xmpp_stanza_get_attribute(stanza, "from");
}
/** Get the first child of stanza following a path-like list of names.
* This function searches the children and their children that match
* the given path.
*
* * "name" - Search 'name'
*
* * "name[@ns='foo']" - Search 'name' which is in the namespace 'foo'
*
* The Syntax to pass namespaces is inspired by the XPATH way of passing
* attributes.
*
* The namespace syntax only supports single quotes `'`.
*
* The \ref XMPP_STANZA_NAME_IN_NS macro is provided as a helper for names
* in namespaces.
*
* @param stanza a Strophe stanza object
* @param ... a var-args list that must be terminated by a NULL entry
*
* @return the matching child stanza object or NULL if no match was found
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...)
{
xmpp_stanza_t *child = NULL;
char *p, *tok, *attr, *saveattr, *ns = NULL;
const char *xmlns;
va_list ap;
va_start(ap, stanza);
while ((p = va_arg(ap, char *)) != NULL) {
tok = strophe_strdup(stanza->ctx, p);
if (!tok) {
child = NULL;
break;
}
saveattr = ns = NULL;
attr = strophe_strtok_r(tok, "[", &saveattr);
if (attr) {
attr = strophe_strtok_r(NULL, "]", &saveattr);
if (attr) {
if (!strncmp(attr, "@ns='", 5)) {
ns = attr + 5;
strophe_strtok_r(ns, "'", &saveattr);
}
}
}
if (!child) {
if (strcmp(xmpp_stanza_get_name(stanza), tok))
goto error_out;
if (ns) {
xmlns = xmpp_stanza_get_ns(stanza);
if (!xmlns || strcmp(xmlns, ns))
goto error_out;
}
child = stanza;
} else {
if (!ns)
child = xmpp_stanza_get_child_by_name(child, tok);
else
child = xmpp_stanza_get_child_by_name_and_ns(child, tok, ns);
}
error_out:
strophe_free(stanza->ctx, tok);
if (!child)
break;
}
va_end(ap);
return p == NULL ? child : NULL;
}
/** Get the first child of stanza with name.
* This function searches all the immediate children of stanza for a child
* stanza that matches the name. The first matching child is returned.
@@ -862,8 +928,8 @@ const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
const char *const name)
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
const char *name)
{
xmpp_stanza_t *child;
@@ -888,8 +954,8 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
const char *const ns)
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
const char *ns)
{
xmpp_stanza_t *child;
const char *child_ns;
@@ -916,9 +982,9 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
const char *const name,
const char *const ns)
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
const char *name,
const char *ns)
{
xmpp_stanza_t *child;
const char *child_ns;
@@ -947,7 +1013,7 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza)
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza)
{
return stanza->children;
}
@@ -960,7 +1026,7 @@ xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza)
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza)
{
return stanza->next;
}
@@ -976,7 +1042,7 @@ xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza)
{
size_t len, clen;
xmpp_stanza_t *child;
@@ -984,7 +1050,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
if (stanza->type == XMPP_STANZA_TEXT) {
if (stanza->data)
return xmpp_strdup(stanza->ctx, stanza->data);
return strophe_strdup(stanza->ctx, stanza->data);
else
return NULL;
}
@@ -997,7 +1063,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
if (len == 0)
return NULL;
text = (char *)xmpp_alloc(stanza->ctx, len + 1);
text = (char *)strophe_alloc(stanza->ctx, len + 1);
if (!text)
return NULL;
@@ -1027,7 +1093,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza)
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza)
{
if (stanza->type == XMPP_STANZA_TEXT)
return stanza->data;
@@ -1046,7 +1112,7 @@ const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza)
*
* @ingroup Stanza
*/
int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id)
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id)
{
return xmpp_stanza_set_attribute(stanza, "id", id);
}
@@ -1062,7 +1128,7 @@ int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id)
*
* @ingroup Stanza
*/
int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type)
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type)
{
return xmpp_stanza_set_attribute(stanza, "type", type);
}
@@ -1079,7 +1145,7 @@ int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type)
*
* @ingroup Stanza
*/
int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to)
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to)
{
return xmpp_stanza_set_attribute(stanza, "to", to);
}
@@ -1096,7 +1162,7 @@ int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to)
*
* @ingroup Stanza
*/
int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from)
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from)
{
return xmpp_stanza_set_attribute(stanza, "from", from);
}
@@ -1112,8 +1178,7 @@ int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from)
*
* @ingroup Stanza
*/
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
const char *const name)
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name)
{
if (stanza->type != XMPP_STANZA_TAG)
return NULL;
@@ -1133,8 +1198,7 @@ const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
const char *const name)
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name)
{
if (stanza->type != XMPP_STANZA_TAG)
return -1;
@@ -1146,8 +1210,8 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
}
/** Create a stanza object in reply to another.
* This function makes a copy of a stanza object with the attribute to set
* its original from.
* This function makes a copy of a stanza object with the attribute "to" set
* its original "from".
* The stanza will have a reference count of one, so the caller does not
* need to clone it.
*
@@ -1157,7 +1221,7 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza)
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza)
{
xmpp_stanza_t *copy = NULL;
const char *from;
@@ -1174,7 +1238,7 @@ xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza)
copy->type = stanza->type;
if (stanza->data) {
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
copy->data = strophe_strdup(stanza->ctx, stanza->data);
if (!copy->data)
goto copy_error;
}
@@ -1204,7 +1268,7 @@ copy_error:
* Check https://tools.ietf.org/html/rfc6120#section-8.3 for details.
*
* @param stanza a Strophe stanza object
* @param error_type type attribute in the <error/> child element
* @param error_type type attribute in the `<error/>` child element
* @param condition the defined-condition (e.g. "item-not-found")
* @param text optional description, may be NULL
*
@@ -1212,16 +1276,16 @@ copy_error:
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
const char *const error_type,
const char *const condition,
const char *const text)
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
const char *error_type,
const char *condition,
const char *text)
{
xmpp_ctx_t *ctx = stanza->ctx;
xmpp_stanza_t *reply = NULL;
xmpp_stanza_t *error;
xmpp_stanza_t *item;
xmpp_stanza_t *text_stanza;
xmpp_stanza_t *error = NULL;
xmpp_stanza_t *item = NULL;
xmpp_stanza_t *text_stanza = NULL;
const char *to;
if (!error_type || !condition)
@@ -1230,41 +1294,53 @@ xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
reply = xmpp_stanza_reply(stanza);
if (!reply)
goto quit_err;
xmpp_stanza_set_type(reply, "error");
if (xmpp_stanza_set_type(reply, "error") != XMPP_EOK)
goto quit_err;
to = xmpp_stanza_get_to(stanza);
if (to)
xmpp_stanza_set_from(reply, to);
if (xmpp_stanza_set_from(reply, to) != XMPP_EOK)
goto quit_err;
error = xmpp_stanza_new(ctx);
if (!error)
goto quit_err;
xmpp_stanza_set_name(error, "error");
xmpp_stanza_set_type(error, error_type);
xmpp_stanza_add_child(reply, error);
if (xmpp_stanza_set_name(error, "error") != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_set_type(error, error_type) != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_add_child(reply, error) != XMPP_EOK)
goto quit_err;
xmpp_stanza_release(error);
item = xmpp_stanza_new(ctx);
if (!item)
goto quit_err;
xmpp_stanza_set_name(item, condition);
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
xmpp_stanza_add_child(error, item);
if (xmpp_stanza_set_name(item, condition) != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
goto quit_err;
xmpp_stanza_release(item);
if (text) {
item = xmpp_stanza_new(ctx);
if (!item)
goto quit_err;
xmpp_stanza_set_name(item, "text");
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
xmpp_stanza_add_child(error, item);
if (xmpp_stanza_set_name(item, "text") != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
goto quit_err;
xmpp_stanza_release(item);
text_stanza = xmpp_stanza_new(ctx);
if (!text_stanza)
goto quit_err;
xmpp_stanza_set_text(text_stanza, text);
xmpp_stanza_add_child(item, text_stanza);
if (xmpp_stanza_set_text(text_stanza, text) != XMPP_EOK)
goto quit_err;
if (xmpp_stanza_add_child(item, text_stanza) != XMPP_EOK)
goto quit_err;
xmpp_stanza_release(text_stanza);
}
@@ -1273,14 +1349,20 @@ xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
quit_err:
if (reply)
xmpp_stanza_release(reply);
if (error)
xmpp_stanza_release(error);
if (item)
xmpp_stanza_release(item);
if (text_stanza)
xmpp_stanza_release(text_stanza);
return NULL;
}
static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
const char *const name,
const char *const type,
const char *const id,
const char *const to)
const char *name,
const char *type,
const char *id,
const char *to)
{
xmpp_stanza_t *stanza = xmpp_stanza_new(ctx);
int ret;
@@ -1301,7 +1383,7 @@ static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
return stanza;
}
/** Create a <message/> stanza object with given attributes.
/** Create a `<message/>` stanza object with given attributes.
* Attributes are optional and may be NULL.
*
* @param ctx a Strophe context object
@@ -1314,20 +1396,20 @@ static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
const char *const type,
const char *const to,
const char *const id)
const char *type,
const char *to,
const char *id)
{
return _stanza_new_with_attrs(ctx, "message", type, id, to);
}
/** Get text from <body/> child element.
/** Get text from `<body/>` child element.
* This function returns new allocated string. The caller is responsible
* for freeing this string with xmpp_free().
*
* @param msg well formed <message/> stanza
* @param msg well formed `<message/>` stanza
*
* @return allocated string or NULL on failure (no <body/> element or
* @return allocated string or NULL on failure (no `<body/>` element or
* memory allocation error)
*
* @ingroup Stanza
@@ -1346,16 +1428,17 @@ char *xmpp_message_get_body(xmpp_stanza_t *msg)
return text;
}
/** Add <body/> child element to a <message/> stanza with the given text.
/** Add `<body/>` child element to a `<message/>` stanza with the given text.
*
* @param msg a <message> stanza object without <body/> child element.
* @param msg a `<message>` stanza object without `<body/>` child element.
* @param text The text that shall be placed in the body.
*
* @return 0 on success (XMPP_EOK), and a number less than 0 on failure
* (XMPP_EMEM, XMPP_EINVOP)
*
* @ingroup Stanza
*/
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text)
{
xmpp_ctx_t *ctx = msg->ctx;
xmpp_stanza_t *body;
@@ -1363,7 +1446,7 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
const char *name;
int ret;
/* check that msg is a <message/> stanza and doesn't contain <body/> */
/* check that msg is a `<message/>` stanza and doesn't contain `<body/>` */
name = xmpp_stanza_get_name(msg);
body = xmpp_stanza_get_child_by_name(msg, "body");
if (!name || strcmp(name, "message") != 0 || body)
@@ -1390,7 +1473,7 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
return ret;
}
/** Create an <iq/> stanza object with given attributes.
/** Create an `<iq/>` stanza object with given attributes.
* Attributes are optional and may be NULL.
*
* @param ctx a Strophe context object
@@ -1401,13 +1484,12 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text)
*
* @ingroup Stanza
*/
xmpp_stanza_t *
xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id)
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id)
{
return _stanza_new_with_attrs(ctx, "iq", type, id, NULL);
}
/** Create a <presence/> stanza object.
/** Create a `<presence/>` stanza object.
*
* @param ctx a Strophe context object
*
@@ -1424,7 +1506,7 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
* The error text is optional and may be NULL.
*
* @param ctx a Strophe context object
* @param type enum of xmpp_error_type_t
* @param type enum of strophe_error_type_t
* @param text content of a 'text'
*
* @return a new Strophe stanza object
@@ -1433,9 +1515,8 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
xmpp_error_type_t const type,
const char *const text)
xmpp_stanza_t *
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
{
xmpp_stanza_t *error =
_stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL);
@@ -1520,8 +1601,7 @@ xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
}
xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
xmpp_stanza_add_child(error, error_type);
xmpp_stanza_release(error_type);
xmpp_stanza_add_child_ex(error, error_type, 0);
if (text) {
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
@@ -1531,11 +1611,9 @@ xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
xmpp_stanza_set_text(content, text);
xmpp_stanza_add_child(error_text, content);
xmpp_stanza_release(content);
xmpp_stanza_add_child_ex(error_text, content, 0);
xmpp_stanza_add_child(error, error_text);
xmpp_stanza_release(error_text);
xmpp_stanza_add_child_ex(error, error_text, 0);
}
return error;
@@ -1556,8 +1634,11 @@ static void _stub_stream_end(char *name, void *userdata)
static void _stream_stanza(xmpp_stanza_t *stanza, void *userdata)
{
stanza = xmpp_stanza_clone(stanza);
*(xmpp_stanza_t **)userdata = stanza;
xmpp_stanza_t **dest = userdata;
if (*dest == NULL) {
stanza = xmpp_stanza_clone(stanza);
*dest = stanza;
}
}
/** Create a stanza object from the string.

283
src/tls.c Normal file
View File

@@ -0,0 +1,283 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.c
** strophe XMPP client library -- generic TLS functions
**
** Copyright (C) 2021 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
* Generic TLS functionality.
*/
/** @defgroup TLS SSL/TLS specific functionality
* These functions provide SSL/TLS specific functionality.
*/
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#if !defined(_WIN32)
#include <unistd.h>
#endif
#include "strophe.h"
#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;
};
const size_t tlscert_dnsnames_increment = 4;
/** Get the Strophe context which is assigned to this certificate.
*
* @param cert a Strophe TLS certificate object
*
* @return the Strophe context object where this certificate originates from
*
* @ingroup TLS
*/
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
{
return cert->ctx;
}
/** Get the Strophe connection which is assigned to this certificate.
*
* @param cert a Strophe TLS certificate object
*
* @return the Strophe connection object where this certificate originates from
*
* @ingroup TLS
*/
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
{
return cert->conn;
}
/** Get the userdata of a Strophe connection which is assigned to this
* certificate.
*
* @param cert a Strophe TLS certificate object
*
* @return the userdata of a Strophe connection object where this certificate
* originates from
*
* @ingroup TLS
*/
void *xmpp_tlscert_get_userdata(const xmpp_tlscert_t *cert)
{
if (cert->conn == NULL)
return NULL;
return cert->conn->userdata;
}
/** Get the complete PEM of this certificate.
*
* @param cert a Strophe TLS certificate object
*
* @return a string containing the PEM of this certificate
*
* @ingroup TLS
*/
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
{
return cert->pem;
}
/** Get the dnsName entries out of the SubjectAlternativeNames.
*
* Note: Max. `MAX_NUM_DNSNAMES` are supported.
*
* @param cert a Strophe TLS certificate object
* @param n which dnsName entry
*
* @return a string with the n'th dnsName
*
* @ingroup TLS
*/
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
{
if (n >= cert->dnsnames->cur)
return NULL;
return cert->dnsnames->data[n];
}
/** Get various parts of the certificate as String.
*
* c.f. \ref xmpp_cert_element_t for details.
*
* @param cert a Strophe TLS certificate object
* @param elmnt which part of the certificate
*
* @return a string with the part of the certificate
*
* @ingroup TLS
*/
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
xmpp_cert_element_t elmnt)
{
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
return NULL;
return cert->elements[elmnt];
}
/** Get a descriptive string for each xmpp_cert_element_t.
*
* c.f. \ref xmpp_cert_element_t for details.
*
* @param elmnt which element
*
* @return a string with the description
*
* @ingroup TLS
*/
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
{
static const char *descriptions[] = {
"X.509 Version",
"SerialNumber",
"Subject",
"Issuer",
"Issued On",
"Expires On",
"Public Key Algorithm",
"Certificate Signature Algorithm",
"Fingerprint SHA-1",
"Fingerprint SHA-256",
};
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
return NULL;
return descriptions[elmnt];
}
/** Allocate and initialize a Strophe TLS certificate object.
*
* @param ctx a Strophe context object
*
* @return a certificate object or NULL
*/
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
{
xmpp_tlscert_t *tlscert = strophe_alloc(ctx, sizeof(*tlscert));
if (!tlscert)
return NULL;
memset(tlscert, 0, sizeof(*tlscert));
tlscert->dnsnames = strophe_alloc(ctx, sizeof(*tlscert->dnsnames));
if (!tlscert->dnsnames) {
strophe_free(ctx, tlscert);
return NULL;
}
memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
tlscert->ctx = ctx;
return tlscert;
}
/** Free a certificate object.
*
* @param cert a Strophe TLS certificate object
*
* @ingroup TLS
*/
void xmpp_tlscert_free(xmpp_tlscert_t *cert)
{
size_t n;
for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
if (cert->elements[n])
strophe_free(cert->ctx, cert->elements[n]);
}
if (cert->dnsnames->data) {
for (n = 0; n < cert->dnsnames->cur; ++n) {
if (cert->dnsnames->data[n])
strophe_free(cert->ctx, cert->dnsnames->data[n]);
}
}
strophe_free(cert->ctx, cert->dnsnames->data);
strophe_free(cert->ctx, cert->dnsnames);
if (cert->pem)
strophe_free(cert->ctx, cert->pem);
strophe_free(cert->ctx, cert);
}
/** Add a dnsName to the Strophe TLS certificate object.
*
* @param cert a Strophe TLS certificate object
* @param dnsname dnsName that shall be stored
*
* @return classic Unix style - 0=success, 1=error
*/
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
{
if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
char **dnsnames =
strophe_realloc(cert->ctx, cert->dnsnames->data,
(cert->dnsnames->max + tlscert_dnsnames_increment) *
sizeof(char **));
if (!dnsnames)
return 1;
cert->dnsnames->data = dnsnames;
cert->dnsnames->max += tlscert_dnsnames_increment;
}
cert->dnsnames->data[cert->dnsnames->cur++] =
strophe_strdup(cert->ctx, dnsname);
return 0;
}
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn)
{
int ret;
unsigned char hash[XMPP_SHA1_DIGEST_SIZE];
const char *fname = conn->tls_client_cert;
size_t fname_len = strlen(fname);
xmpp_sha1_digest((void *)fname, fname_len, hash);
if (fname_len && fname_len == conn->password_cache.fnamelen &&
memcmp(hash, conn->password_cache.fname_hash, sizeof(hash)) == 0) {
if (conn->password_cache.passlen) {
memcpy(pw, conn->password_cache.pass,
conn->password_cache.passlen + 1);
return conn->password_cache.passlen;
}
}
size_t max_len = pw_max == 256 ? pw_max : sizeof(conn->password_cache.pass);
ret = conn->password_callback(conn->password_cache.pass, max_len, conn,
conn->password_callback_userdata);
if (ret < 0 || ret >= (ssize_t)max_len) {
memset(conn->password_cache.pass, 0, sizeof(conn->password_cache.pass));
return -1;
}
conn->password_cache.pass[ret] = '\0';
memcpy(pw, conn->password_cache.pass, ret + 1);
conn->password_cache.passlen = ret;
conn->password_cache.fnamelen = fname_len;
memcpy(conn->password_cache.fname_hash, hash, sizeof(hash));
return conn->password_cache.passlen;
}
void tls_clear_password_cache(xmpp_conn_t *conn)
{
memset(&conn->password_cache, 0, sizeof(conn->password_cache));
}

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
@@ -21,24 +22,52 @@
typedef struct _tls tls_t;
typedef struct _dnsname_t dnsname_t;
struct _xmpp_tlscert_t {
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
char *pem;
char *elements[XMPP_CERT_ELEMENT_MAX];
dnsname_t *dnsnames;
};
/* provided by the real TLS implementation */
void tls_initialize(void);
void tls_shutdown(void);
tls_t *tls_new(xmpp_conn_t *conn);
void tls_free(tls_t *tls);
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n);
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 *const buff, const size_t len);
int tls_write(tls_t *tls, const void *const buff, const size_t len);
int tls_error(struct conn_interface *intf);
int tls_is_recoverable(struct conn_interface *intf, int error);
int tls_clear_pending_write(tls_t *tls);
int tls_is_recoverable(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);
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn);
void tls_clear_password_cache(xmpp_conn_t *conn);
#endif /* __LIBSTROPHE_TLS_H__ */

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
@@ -33,6 +34,21 @@ void tls_shutdown(void)
return;
}
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
{
UNUSED(conn);
UNUSED(n);
/* always fail */
return NULL;
}
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
{
UNUSED(conn);
/* always fail */
return 0;
}
tls_t *tls_new(xmpp_conn_t *conn)
{
UNUSED(conn);
@@ -46,6 +62,12 @@ void tls_free(tls_t *tls)
return;
}
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
{
UNUSED(conn);
return NULL;
}
int tls_set_credentials(tls_t *tls, const char *cafilename)
{
UNUSED(tls);
@@ -53,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);
@@ -65,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 *const buff, const 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 *const buff, const 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,27 +7,44 @@
** 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
* TLS implementation with GNUTLS
*/
#include <string.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <gnutls/x509-ext.h>
#include <gnutls/pkcs11.h>
#include <gnutls/pkcs12.h>
#include "common.h"
#include "tls.h"
#include "sock.h"
/* FIXME this shouldn't be a constant string */
#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
#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? */
sock_t sock;
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;
};
@@ -46,41 +64,504 @@ void tls_shutdown(void)
gnutls_global_deinit();
}
static int _tls_password_callback(void *userdata,
int attempt,
const char *token_url,
const char *token_label,
unsigned int flags,
char *pin,
size_t pin_max)
{
xmpp_conn_t *conn = userdata;
UNUSED(attempt);
UNUSED(token_url);
UNUSED(token_label);
UNUSED(flags);
int ret = tls_caching_password_callback(pin, pin_max, conn);
return ret > 0 ? 0 : GNUTLS_E_PKCS11_PIN_ERROR;
}
static gnutls_x509_crt_t _tls_load_cert_x509(xmpp_conn_t *conn)
{
gnutls_x509_crt_t cert;
gnutls_datum_t data;
int res;
if (gnutls_x509_crt_init(&cert) < 0)
return NULL;
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
goto error_out;
res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM);
gnutls_free(data.data);
if (res < 0)
goto error_out;
return cert;
error_out:
gnutls_x509_crt_deinit(cert);
return NULL;
}
static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn)
{
gnutls_pkcs12_t p12;
gnutls_x509_crt_t *cert = NULL;
gnutls_datum_t data;
gnutls_x509_privkey_t key;
unsigned int cert_num = 0, retries = 0;
int err = -1;
if (gnutls_pkcs12_init(&p12) < 0)
return NULL;
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
goto error_out;
if (gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0) < 0)
goto error_out2;
/* First try to open file with no pass */
if ((err = gnutls_pkcs12_simple_parse(p12, NULL, &key, &cert, &cert_num,
NULL, NULL, NULL, 0)) == 0) {
goto done;
}
/* Now let's try to open file with an empty pass */
if ((err = gnutls_pkcs12_simple_parse(p12, "", &key, &cert, &cert_num, NULL,
NULL, NULL, 0)) == 0) {
goto done;
}
if (!conn->password_callback) {
strophe_error(conn->ctx, "tls", "No password callback set");
goto error_out2;
}
/* ... and only now ask the user for a password */
while (retries++ < conn->password_retries) {
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
int passlen =
_tls_password_callback(conn, 0, NULL, NULL, 0, pass, sizeof(pass));
if (passlen < 0)
continue;
err = gnutls_pkcs12_simple_parse(p12, pass, &key, &cert, &cert_num,
NULL, NULL, NULL, 0);
memset(pass, 0, sizeof(pass));
if (err == 0)
break;
tls_clear_password_cache(conn);
if (err != GNUTLS_E_DECRYPTION_FAILED &&
err != GNUTLS_E_MAC_VERIFY_FAILED) {
strophe_error(conn->ctx, "tls", "could not read P12 file");
break;
}
strophe_debug(conn->ctx, "tls", "wrong password?");
}
done:
gnutls_pkcs12_deinit(p12);
gnutls_free(data.data);
if (err < 0)
goto error_out;
gnutls_x509_privkey_deinit(key);
if (cert_num > 1) {
strophe_error(conn->ctx, "tls", "Can't handle stack of %u certs",
cert_num);
goto error_out;
}
gnutls_x509_crt_t ret = *cert;
gnutls_free(cert);
return ret;
error_out2:
gnutls_free(data.data);
error_out:
tls_clear_password_cache(conn);
if (cert) {
for (unsigned int n = 0; n < cert_num; ++n) {
gnutls_x509_crt_deinit(cert[n]);
}
gnutls_free(cert);
}
return NULL;
}
static gnutls_x509_crt_t _tls_load_cert(xmpp_conn_t *conn)
{
if (conn->tls && conn->tls->client_cert)
return conn->tls->client_cert;
if (conn->tls_client_cert && !conn->tls_client_key) {
return _tls_load_cert_p12(conn);
}
return _tls_load_cert_x509(conn);
}
static void _tls_free_cert(xmpp_conn_t *conn, gnutls_x509_crt_t cert)
{
if (conn->tls && conn->tls->client_cert == cert)
return;
gnutls_x509_crt_deinit(cert);
}
static int _tls_get_id_on_xmppaddr(xmpp_conn_t *conn,
gnutls_x509_crt_t cert,
unsigned int n,
char **ret)
{
gnutls_datum_t san;
size_t name_len, oid_len;
char oid[128], name[128];
name_len = oid_len = 128;
int res =
gnutls_x509_crt_get_subject_alt_name(cert, n, name, &name_len, NULL);
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
/* This is the happy flow path with fixed GnuTLS.
* While implementing this I stumbled over an issue in GnuTLS
* which lead to
* https://gitlab.com/gnutls/gnutls/-/merge_requests/1397
*/
if (ret) {
*ret = strophe_strdup(conn->ctx, name);
}
return GNUTLS_SAN_OTHERNAME_XMPP;
}
if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
if (res != GNUTLS_SAN_OTHERNAME)
return GNUTLS_E_X509_UNKNOWN_SAN;
res = gnutls_x509_crt_get_subject_alt_othername_oid(cert, n, oid, &oid_len);
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
gnutls_datum_t xmpp_addr;
san.data = (unsigned char *)name;
san.size = name_len;
res = gnutls_x509_othername_to_virtual(oid, &san, NULL, &xmpp_addr);
if (res < 0) {
gnutls_free(xmpp_addr.data);
return GNUTLS_E_MEMORY_ERROR;
}
if (ret) {
*ret = strophe_strdup(conn->ctx, (char *)xmpp_addr.data);
}
gnutls_free(xmpp_addr.data);
return GNUTLS_SAN_OTHERNAME_XMPP;
}
return GNUTLS_E_X509_UNKNOWN_SAN;
}
static int _tls_id_on_xmppaddr(xmpp_conn_t *conn,
gnutls_x509_crt_t cert,
unsigned int n,
char **ret)
{
int res = GNUTLS_E_SUCCESS;
unsigned int i, j;
for (i = j = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++i) {
res = _tls_get_id_on_xmppaddr(conn, cert, i, NULL);
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
if (j == n) {
res = _tls_get_id_on_xmppaddr(conn, cert, i, ret);
break;
}
j++;
}
}
return res;
}
/** Search through the SubjectAlternativeNames and return the next
* id-on-xmppAddr element starting from `n`.
*/
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
{
char *ret = NULL;
gnutls_x509_crt_t cert = _tls_load_cert(conn);
if (cert == NULL)
return NULL;
_tls_id_on_xmppaddr(conn, cert, n, &ret);
_tls_free_cert(conn, cert);
return ret;
}
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
{
unsigned int ret = 0, n;
int res = GNUTLS_E_SUCCESS;
gnutls_x509_crt_t cert = _tls_load_cert(conn);
if (cert == NULL)
return 0;
for (n = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++n) {
res = _tls_id_on_xmppaddr(conn, cert, n, NULL);
if (res == GNUTLS_SAN_OTHERNAME_XMPP)
ret++;
}
_tls_free_cert(conn, cert);
return ret;
}
static xmpp_tlscert_t *_x509_to_tlscert(xmpp_ctx_t *ctx, gnutls_x509_crt_t cert)
{
int res;
char buf[512], smallbuf[64];
size_t size, m;
unsigned int algo, n;
gnutls_datum_t data;
time_t time_val;
xmpp_tlscert_t *tlscert = tlscert_new(ctx);
gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &data);
tlscert->pem = strophe_alloc(ctx, data.size + 1);
memcpy(tlscert->pem, data.data, data.size);
tlscert->pem[data.size] = '\0';
gnutls_free(data.data);
size = sizeof(buf);
gnutls_x509_crt_get_dn(cert, buf, &size);
tlscert->elements[XMPP_CERT_SUBJECT] = strophe_strdup(ctx, buf);
size = sizeof(buf);
gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
tlscert->elements[XMPP_CERT_ISSUER] = strophe_strdup(ctx, buf);
time_val = gnutls_x509_crt_get_activation_time(cert);
tlscert->elements[XMPP_CERT_NOTBEFORE] =
strophe_strdup(ctx, ctime(&time_val));
tlscert->elements[XMPP_CERT_NOTBEFORE]
[strlen(tlscert->elements[XMPP_CERT_NOTBEFORE]) - 1] =
'\0';
time_val = gnutls_x509_crt_get_expiration_time(cert);
tlscert->elements[XMPP_CERT_NOTAFTER] =
strophe_strdup(ctx, ctime(&time_val));
tlscert->elements[XMPP_CERT_NOTAFTER]
[strlen(tlscert->elements[XMPP_CERT_NOTAFTER]) - 1] = '\0';
size = sizeof(smallbuf);
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, smallbuf, &size);
hex_encode(buf, smallbuf, size);
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA1] = strophe_strdup(ctx, buf);
size = sizeof(smallbuf);
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, smallbuf, &size);
hex_encode(buf, smallbuf, size);
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = strophe_strdup(ctx, buf);
strophe_snprintf(buf, sizeof(buf), "%d", gnutls_x509_crt_get_version(cert));
tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf);
algo = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
tlscert->elements[XMPP_CERT_KEYALG] =
strophe_strdup(ctx, gnutls_pk_algorithm_get_name(algo));
algo = gnutls_x509_crt_get_signature_algorithm(cert);
tlscert->elements[XMPP_CERT_SIGALG] =
strophe_strdup(ctx, gnutls_sign_get_name(algo));
size = sizeof(smallbuf);
gnutls_x509_crt_get_serial(cert, smallbuf, &size);
hex_encode(buf, smallbuf, size);
tlscert->elements[XMPP_CERT_SERIALNUMBER] = strophe_strdup(ctx, buf);
for (n = 0, m = 0, res = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
++n) {
size = sizeof(buf);
res = gnutls_x509_crt_get_subject_alt_name(cert, n, buf, &size, NULL);
if (res == GNUTLS_SAN_DNSNAME) {
if (tlscert_add_dnsname(tlscert, buf))
strophe_debug(ctx, "tls", "Can't store dnsName(%zu): %s", m,
buf);
m++;
}
}
return tlscert;
}
static int _tls_verify(gnutls_session_t session)
{
tls_t *tls = gnutls_session_get_ptr(session);
const gnutls_datum_t *cert_list;
gnutls_certificate_type_t type;
gnutls_datum_t out;
unsigned int cert_list_size = 0, status;
gnutls_x509_crt_t cert;
if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
return -1;
if (gnutls_certificate_verify_peers2(session, &status) < 0) {
strophe_error(tls->ctx, "tls", "Verify peers failed");
return -1;
}
type = gnutls_certificate_type_get(session);
if (gnutls_certificate_verification_status_print(status, type, &out, 0) <
0) {
strophe_error(tls->ctx, "tls", "Status print failed");
return -1;
}
/* Return early if the Certificate is trusted
* OR if we trust all Certificates */
if (status == 0 || tls->conn->tls_trust) {
gnutls_free(out.data);
return 0;
}
if (!tls->conn->certfail_handler) {
strophe_error(tls->ctx, "tls",
"No certfail handler set, canceling connection attempt");
gnutls_free(out.data);
return -1;
}
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
/* OpenSSL displays the certificate chain in reverse order than GnuTLS.
* To show consistent behavior to the user, traverse the list from the
* end.
*/
while (cert_list_size--) {
gnutls_x509_crt_init(&cert);
gnutls_x509_crt_import(cert, &cert_list[cert_list_size],
GNUTLS_X509_FMT_DER);
xmpp_tlscert_t *tlscert = _x509_to_tlscert(tls->ctx, cert);
if (!tlscert) {
gnutls_x509_crt_deinit(cert);
gnutls_free(out.data);
return -1;
}
tlscert->conn = tls->conn;
if (tls->conn->certfail_handler(tlscert, (char *)out.data) == 0) {
xmpp_tlscert_free(tlscert);
gnutls_x509_crt_deinit(cert);
gnutls_free(out.data);
return -1;
}
xmpp_tlscert_free(tlscert);
gnutls_x509_crt_deinit(cert);
}
gnutls_free(out.data);
return 0;
}
tls_t *tls_new(xmpp_conn_t *conn)
{
tls_t *tls = xmpp_alloc(conn->ctx, sizeof(tls_t));
tls_t *tls = strophe_alloc(conn->ctx, sizeof(tls_t));
if (tls) {
memset(tls, 0, sizeof(*tls));
tls->ctx = conn->ctx;
tls->sock = conn->sock;
tls->conn = conn;
gnutls_init(&tls->session, GNUTLS_CLIENT);
gnutls_certificate_allocate_credentials(&tls->cred);
tls_set_credentials(tls, CAFILE);
tls_set_credentials(tls, NULL);
if (conn->password_callback)
gnutls_certificate_set_pin_function(tls->cred,
_tls_password_callback, conn);
if (conn->tls_client_cert && conn->tls_client_key) {
unsigned int retries = 0;
tls->client_cert = _tls_load_cert(conn);
if (!tls->client_cert) {
strophe_error(tls->ctx, "tls",
"could not read client certificate");
goto error_out;
}
while (retries++ < conn->password_retries) {
int err = gnutls_certificate_set_x509_key_file(
tls->cred, conn->tls_client_cert, conn->tls_client_key,
GNUTLS_X509_FMT_PEM);
if (err == 0)
break;
tls_clear_password_cache(conn);
if (err != GNUTLS_E_DECRYPTION_FAILED) {
strophe_error(tls->ctx, "tls",
"could not read private key");
goto error_out;
}
strophe_debug(tls->ctx, "tls", "wrong password?");
}
} else if (conn->tls_client_cert) {
unsigned int retries = 0;
while (retries++ < conn->password_retries) {
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
pass[0] = '\0';
int passlen = _tls_password_callback(conn, 0, NULL, NULL, 0,
pass, sizeof(pass));
if (passlen < 0)
continue;
int err = gnutls_certificate_set_x509_simple_pkcs12_file(
tls->cred, conn->tls_client_cert, GNUTLS_X509_FMT_DER,
pass);
memset(pass, 0, sizeof(pass));
if (err == 0)
break;
tls_clear_password_cache(conn);
if (err != GNUTLS_E_DECRYPTION_FAILED &&
err != GNUTLS_E_MAC_VERIFY_FAILED) {
strophe_error(tls->ctx, "tls", "could not read P12 file");
goto error_out;
}
strophe_debug(tls->ctx, "tls", "wrong password?");
}
}
gnutls_certificate_set_verify_function(tls->cred, _tls_verify);
gnutls_set_default_priority(tls->session);
gnutls_session_set_ptr(tls->session, tls);
/* fixme: this may require setting a callback on win32? */
gnutls_transport_set_int(tls->session, conn->sock);
}
return tls;
error_out:
if (tls->client_cert)
gnutls_x509_crt_deinit(tls->client_cert);
gnutls_certificate_free_credentials(tls->cred);
gnutls_deinit(tls->session);
strophe_free(tls->ctx, tls);
return NULL;
}
void tls_free(tls_t *tls)
{
gnutls_free(tls->channel_binding.data);
gnutls_x509_crt_deinit(tls->client_cert);
gnutls_deinit(tls->session);
gnutls_certificate_free_credentials(tls->cred);
xmpp_free(tls->ctx, tls);
strophe_free(tls->ctx, tls);
}
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
{
xmpp_tlscert_t *tlscert = NULL;
if (conn && conn->tls && conn->tls->session) {
unsigned int list_size = 0;
const gnutls_datum_t *der_cert =
gnutls_certificate_get_peers(conn->tls->session, &list_size);
if (der_cert && list_size) {
gnutls_x509_crt_t cert;
if (gnutls_x509_crt_init(&cert) < 0)
return NULL;
if (gnutls_x509_crt_import(cert, der_cert, GNUTLS_X509_FMT_DER) ==
0)
tlscert = _x509_to_tlscert(conn->ctx, cert);
gnutls_x509_crt_deinit(cert);
}
}
return tlscert;
}
int tls_set_credentials(tls_t *tls, const char *cafilename)
{
int err;
UNUSED(cafilename);
/* set trusted credentials -- takes a .pem filename */
err = gnutls_certificate_set_x509_trust_file(tls->cred, cafilename,
GNUTLS_X509_FMT_PEM);
int err = gnutls_certificate_set_x509_system_trust(tls->cred);
if (err >= 0 && tls->conn->tls_cafile)
err = gnutls_certificate_set_x509_trust_file(
tls->cred, tls->conn->tls_cafile, GNUTLS_X509_FMT_PEM);
if (err >= 0 && tls->conn->tls_capath)
err = gnutls_certificate_set_x509_trust_dir(
tls->cred, tls->conn->tls_capath, GNUTLS_X509_FMT_PEM);
if (err >= 0) {
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
tls->cred);
@@ -90,11 +571,60 @@ 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->sock);
sock_set_blocking(tls->conn->sock);
tls->lasterror = gnutls_handshake(tls->session);
sock_set_nonblocking(tls->sock);
sock_set_nonblocking(tls->conn->sock);
return tls->lasterror == GNUTLS_E_SUCCESS;
}
@@ -105,24 +635,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 *const buff, const 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;
@@ -130,9 +662,10 @@ int tls_read(tls_t *tls, void *const buff, const size_t len)
return ret;
}
int tls_write(tls_t *tls, const void *const buff, const 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;
@@ -140,8 +673,8 @@ int tls_write(tls_t *tls, const void *const buff, const 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;
}

File diff suppressed because it is too large Load Diff

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
@@ -61,6 +62,21 @@ void tls_shutdown(void)
return;
}
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
{
UNUSED(n);
/* always fail */
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
return NULL;
}
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
{
/* always fail */
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
return 0;
}
tls_t *tls_new(xmpp_conn_t *conn)
{
xmpp_ctx_t *ctx = conn->ctx;
@@ -87,7 +103,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
return NULL;
}
tls = xmpp_alloc(ctx, sizeof(*tls));
tls = strophe_alloc(ctx, sizeof(*tls));
if (!tls) {
return NULL;
@@ -123,7 +139,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
return NULL;
}
xmpp_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
strophe_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
memset(&scred, 0, sizeof(scred));
scred.dwVersion = SCHANNEL_CRED_VERSION;
@@ -148,7 +164,7 @@ tls_t *tls_new(xmpp_conn_t *conn)
return NULL;
}
xmpp_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
strophe_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
tls->init = 1;
@@ -181,15 +197,15 @@ tls_t *tls_new(xmpp_conn_t *conn)
void tls_free(tls_t *tls)
{
if (tls->recvbuffer) {
xmpp_free(tls->ctx, tls->recvbuffer);
strophe_free(tls->ctx, tls->recvbuffer);
}
if (tls->readybuffer) {
xmpp_free(tls->ctx, tls->readybuffer);
strophe_free(tls->ctx, tls->readybuffer);
}
if (tls->sendbuffer) {
xmpp_free(tls->ctx, tls->sendbuffer);
strophe_free(tls->ctx, tls->sendbuffer);
}
if (tls->init) {
@@ -203,10 +219,17 @@ void tls_free(tls_t *tls)
tls->hsec32 = NULL;
}
xmpp_free(tls->ctx, tls);
strophe_free(tls->ctx, tls);
return;
}
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
{
/* always fail */
strophe_error(conn->ctx, "tls", "tls_peer_cert() not implemented");
return NULL;
}
int tls_set_credentials(tls_t *tls, const char *cafilename)
{
UNUSED(tls);
@@ -214,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;
@@ -222,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 |
@@ -241,7 +283,7 @@ int tls_start(tls_t *tls)
memset(&(sbin[0]), 0, sizeof(sbin[0]));
sbin[0].BufferType = SECBUFFER_TOKEN;
sbin[0].pvBuffer = xmpp_alloc(tls->ctx, tls->spi->cbMaxToken);
sbin[0].pvBuffer = strophe_alloc(tls->ctx, tls->spi->cbMaxToken);
sbin[0].cbBuffer = tls->spi->cbMaxToken;
memset(&(sbin[1]), 0, sizeof(sbin[1]));
@@ -273,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;
@@ -313,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);
}
}
@@ -334,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;
@@ -347,12 +389,12 @@ int tls_start(tls_t *tls)
}
}
xmpp_free(tls->ctx, sbin[0].pvBuffer);
strophe_free(tls->ctx, sbin[0].pvBuffer);
if (ret != SEC_E_OK) {
tls->lasterror = ret;
xmpp_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
(unsigned long)ret);
strophe_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
(unsigned long)ret);
return 0;
}
@@ -361,16 +403,16 @@ int tls_start(tls_t *tls)
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
tls->spcss.cbTrailer;
tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen);
tls->recvbuffer = strophe_alloc(tls->ctx, tls->recvbuffermaxlen);
tls->recvbufferpos = 0;
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
tls->spcss.cbTrailer;
tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen);
tls->sendbuffer = strophe_alloc(tls->ctx, tls->sendbuffermaxlen);
tls->sendbufferpos = 0;
tls->sendbufferlen = 0;
tls->readybuffer = xmpp_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
tls->readybuffer = strophe_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
tls->readybufferpos = 0;
tls->readybufferlen = 0;
@@ -383,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
@@ -412,9 +456,10 @@ int tls_pending(tls_t *tls)
return 0;
}
int tls_read(tls_t *tls, void *const buff, const 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) {
@@ -437,7 +482,7 @@ int tls_read(tls_t *tls, void *const buff, const 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;
}
@@ -449,7 +494,7 @@ int tls_read(tls_t *tls, void *const buff, const 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) {
@@ -458,8 +503,8 @@ int tls_read(tls_t *tls, void *const buff, const 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;
}
}
@@ -534,16 +579,17 @@ int tls_read(tls_t *tls, void *const buff, const 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;
@@ -557,12 +603,13 @@ int tls_clear_pending_write(tls_t *tls)
return 1;
}
int tls_write(tls_t *tls, const void *const buff, const 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) {
@@ -622,7 +669,7 @@ int tls_write(tls_t *tls, const void *const buff, const 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;
}
@@ -634,7 +681,8 @@ int tls_write(tls_t *tls, const void *const buff, const 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
@@ -15,6 +16,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
@@ -36,21 +38,41 @@
* @param ctx a Strophe context object
* @param s a string
*
* @return a new allocates string with the same data as s or NULL on error
* @return a newly allocated string with the same data as s or NULL on error
*/
char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s)
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s)
{
size_t len;
char *copy;
return strophe_strndup(ctx, s, SIZE_MAX);
}
len = strlen(s);
copy = xmpp_alloc(ctx, len + 1);
/** Duplicate a string with a maximum length.
* This function replaces the standard strndup library call with a version
* that uses the Strophe context object's allocator.
*
* @param ctx a Strophe context object
* @param s a string
* @param len the maximum length of the string to copy
*
* @return a newly allocated string that contains at most `len` symbols
* of the original string or NULL on error
*/
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
{
char *copy;
size_t l;
l = strlen(s);
if (l > len)
l = len;
copy = strophe_alloc(ctx, l + 1);
if (!copy) {
xmpp_error(ctx, "xmpp", "failed to allocate required memory");
strophe_error(ctx, "xmpp", "failed to allocate required memory");
return NULL;
}
memcpy(copy, s, len + 1);
memcpy(copy, s, l);
copy[l] = '\0';
return copy;
}
@@ -60,7 +82,7 @@ char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s)
* For example, visual studio older than 2005 doesn't provide strtok_r()
* nor strtok_s().
*/
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
char *strophe_strtok_r(char *s, const char *delim, char **saveptr)
{
size_t len;
@@ -135,8 +157,24 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
*
* @param conn a Strophe connection object
*/
void disconnect_mem_error(xmpp_conn_t *const conn)
void disconnect_mem_error(xmpp_conn_t *conn)
{
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
strophe_error(conn->ctx, "xmpp", "Memory allocation error");
xmpp_disconnect(conn);
}
int string_to_ul(const char *s, unsigned long *ul)
{
char *endptr;
*ul = strtoul(s, &endptr, 10);
return *endptr != '\0';
}
void hex_encode(char *writebuf, void *readbuf, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
sprintf(writebuf, "%02x", ((unsigned char *)readbuf)[i]);
writebuf += 2;
}
}

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
@@ -26,10 +27,14 @@
#define xmpp_min(x, y) ((x) < (y) ? (x) : (y))
/* string functions */
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);
char *strophe_strtok_r(char *s, const char *delim, char **saveptr);
/* timing functions */
uint64_t time_stamp(void);
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
/* misc functions */
int string_to_ul(const char *s, unsigned long *ul);
void hex_encode(char *writebuf, void *readbuf, size_t len);
#endif /* __LIBSTROPHE_UTIL_H__ */

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
@@ -15,7 +16,6 @@
#include "strophe.h"
#include "common.h"
#include "rand.h"
/** @def XMPP_UUID_LEN
* UUID length in string representation excluding '\0'.
@@ -65,7 +65,7 @@ char *xmpp_uuid_gen(xmpp_ctx_t *ctx)
{
char *uuid;
uuid = xmpp_alloc(ctx, XMPP_UUID_LEN + 1);
uuid = strophe_alloc(ctx, XMPP_UUID_LEN + 1);
if (uuid != NULL) {
crypto_uuid_gen(ctx, uuid);
}

608
strophe.h
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" {
@@ -79,6 +81,18 @@ extern "C" {
* Namespace definition for 'jabber:iq:register'.
*/
#define XMPP_NS_REGISTER "jabber:iq:register"
/** @def XMPP_NS_SM
* 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
@@ -122,18 +136,22 @@ typedef struct _xmpp_log_t xmpp_log_t;
/* opaque run time context containing the above hooks */
typedef struct _xmpp_ctx_t xmpp_ctx_t;
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *const mem,
const xmpp_log_t *const log);
void xmpp_ctx_free(xmpp_ctx_t *const ctx);
typedef struct _xmpp_tlscert_t xmpp_tlscert_t;
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log);
void xmpp_ctx_free(xmpp_ctx_t *ctx);
/* set the verbosity level of the ctx */
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level);
/* free some blocks returned by other APIs, for example the
buffer you get from xmpp_stanza_to_text */
void xmpp_free(const xmpp_ctx_t *const ctx, void *p);
void xmpp_free(const xmpp_ctx_t *ctx, void *p);
struct _xmpp_mem_t {
void *(*alloc)(const size_t size, void *const userdata);
void (*free)(void *p, void *const userdata);
void *(*realloc)(void *p, const size_t size, void *const userdata);
void *(*alloc)(size_t size, void *userdata);
void (*free)(void *p, void *userdata);
void *(*realloc)(void *p, size_t size, void *userdata);
void *userdata;
};
@@ -146,10 +164,10 @@ typedef enum {
typedef enum { XMPP_UNKNOWN, XMPP_CLIENT, XMPP_COMPONENT } xmpp_conn_type_t;
typedef void (*xmpp_log_handler)(void *const userdata,
const xmpp_log_level_t level,
const char *const area,
const char *const msg);
typedef void (*xmpp_log_handler)(void *userdata,
xmpp_log_level_t level,
const char *area,
const char *msg);
struct _xmpp_log_t {
xmpp_log_handler handler;
@@ -164,6 +182,7 @@ xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level);
/* opaque connection object */
typedef struct _xmpp_conn_t xmpp_conn_t;
typedef struct _xmpp_stanza_t xmpp_stanza_t;
typedef struct _xmpp_sm_t xmpp_sm_state_t;
/* connection flags */
#define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0)
@@ -177,6 +196,19 @@ typedef struct _xmpp_stanza_t xmpp_stanza_t;
* Enable legacy authentication support.
*/
#define XMPP_CONN_FLAG_LEGACY_AUTH (1UL << 4)
/** @def XMPP_CONN_FLAG_DISABLE_SM
* 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 {
@@ -213,119 +245,282 @@ typedef enum {
XMPP_SE_XML_NOT_WELL_FORMED
} xmpp_error_type_t;
/** Certificate Elements
*
* @ingroup TLS
*/
typedef enum {
XMPP_CERT_VERSION, /**< X.509 Version */
XMPP_CERT_SERIALNUMBER, /**< SerialNumber */
XMPP_CERT_SUBJECT, /**< Subject */
XMPP_CERT_ISSUER, /**< Issuer */
XMPP_CERT_NOTBEFORE, /**< Issued on */
XMPP_CERT_NOTAFTER, /**< Expires on */
XMPP_CERT_KEYALG, /**< Public Key Algorithm */
XMPP_CERT_SIGALG, /**< Certificate Signature Algorithm */
XMPP_CERT_FINGERPRINT_SHA1, /**< Fingerprint SHA-1 */
XMPP_CERT_FINGERPRINT_SHA256, /**< Fingerprint SHA-256 */
XMPP_CERT_ELEMENT_MAX /**< Last element of the enum */
} xmpp_cert_element_t;
typedef struct {
xmpp_error_type_t type;
char *text;
xmpp_stanza_t *stanza;
} xmpp_stream_error_t;
typedef void (*xmpp_conn_handler)(xmpp_conn_t *const conn,
const xmpp_conn_event_t event,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata);
typedef void (*xmpp_conn_handler)(xmpp_conn_t *conn,
xmpp_conn_event_t event,
int error,
xmpp_stream_error_t *stream_error,
void *userdata);
void xmpp_send_error(xmpp_conn_t *const conn,
xmpp_error_type_t const type,
char *const text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *const ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *const conn);
int xmpp_conn_release(xmpp_conn_t *const conn);
/** The Handler function which will be called when the TLS stack can't
* verify the authenticity of a Certificate that gets presented by
* the server we're trying to connect to.
*
* When this function is called and details of the `cert` have to be
* kept, please copy them yourself. The `cert` object will be free'd
* automatically when this function returns.
*
* NB: `errormsg` is specific per certificate on OpenSSL and the same
* for all certificates on GnuTLS.
*
* @param cert a Strophe certificate object
* @param errormsg The error that caused this.
*
* @return 0 if the connection attempt should be terminated,
* 1 if the connection should be established.
*
* @ingroup TLS
*/
typedef int (*xmpp_certfail_handler)(const xmpp_tlscert_t *cert,
const char *const errormsg);
long xmpp_conn_get_flags(const xmpp_conn_t *const conn);
int xmpp_conn_set_flags(xmpp_conn_t *const conn, long flags);
const char *xmpp_conn_get_jid(const xmpp_conn_t *const conn);
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *const conn);
void xmpp_conn_set_jid(xmpp_conn_t *const conn, const char *const jid);
const char *xmpp_conn_get_pass(const xmpp_conn_t *const conn);
void xmpp_conn_set_pass(xmpp_conn_t *const conn, const char *const pass);
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *const conn);
void xmpp_conn_disable_tls(xmpp_conn_t *const conn);
int xmpp_conn_is_secured(xmpp_conn_t *const conn);
void xmpp_conn_set_keepalive(xmpp_conn_t *const conn,
int timeout,
int interval);
int xmpp_conn_is_connecting(xmpp_conn_t *const conn);
int xmpp_conn_is_connected(xmpp_conn_t *const conn);
int xmpp_conn_is_disconnected(xmpp_conn_t *const conn);
/** The Handler function which will be called when the TLS stack can't
* decrypt a password protected key file.
*
* When this callback is called it shall write a NULL-terminated
* string of maximum length `pw_max - 1` to `pw`.
*
* This is currently only supported for GnuTLS and OpenSSL.
*
* On 2022-02-02 the following maximum lengths are valid:
* ```
* include/gnutls/pkcs11.h: #define GNUTLS_PKCS11_MAX_PIN_LEN 256
* include/openssl/pem.h: #define PEM_BUFSIZE 1024
* ```
*
* We expect the buffer to be NULL-terminated, therefore the usable lengths
* are:
*
* * 255 for GnuTLS
* * 1023 for OpenSSL
*
* Useful API's inside this callback are e.g.
*
* \ref xmpp_conn_get_keyfile
*
*
* @param pw The buffer where the password shall be stored.
* @param pw_max The maximum length of the password.
* @param conn The Strophe connection object this callback originates from.
* @param userdata The userdata pointer as supplied when setting this callback.
*
* @return -1 on error, else the number of bytes written to `pw` w/o
* terminating NUL byte
*
* @ingroup TLS
*/
typedef int (*xmpp_password_callback)(char *pw,
size_t pw_max,
xmpp_conn_t *conn,
void *userdata);
int xmpp_connect_client(xmpp_conn_t *const conn,
const char *const altdomain,
/** The function which will be called when Strophe creates a new socket.
*
* The `sock` argument is a pointer that is dependent on the architecture
* Strophe is compiled for.
*
* For POSIX compatible systems usage shall be:
* ```
* int soc = *((int*)sock);
* ```
*
* On Windows usage shall be:
* ```
* SOCKET soc = *((SOCKET*)sock);
* ```
*
* This function will be called for each socket that is created.
*
* `examples/bot.c` uses a libstrophe supplied callback function that sets
* basic keepalive parameters (`xmpp_sockopt_cb_keepalive()`).
*
* `examples/complex.c` implements a custom function that could be useful
* for an application.
*
* @param conn The Strophe connection object this callback originates from.
* @param sock A pointer to the underlying file descriptor.
*
* @return 0 on success, -1 on error
*
* @ingroup Connections
*/
typedef int (*xmpp_sockopt_callback)(xmpp_conn_t *conn, void *sock);
/* an example callback that sets basic keepalive parameters */
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn);
int xmpp_conn_release(xmpp_conn_t *conn);
long xmpp_conn_get_flags(const xmpp_conn_t *conn);
int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags);
const char *xmpp_conn_get_jid(const xmpp_conn_t *conn);
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn);
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
xmpp_certfail_handler hndl);
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn);
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
xmpp_password_callback cb,
void *userdata);
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn);
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
const char *cert,
const char *key);
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
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);
int xmpp_conn_is_secured(xmpp_conn_t *conn);
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
xmpp_sockopt_callback callback);
int xmpp_conn_is_connecting(xmpp_conn_t *conn);
int xmpp_conn_is_connected(xmpp_conn_t *conn);
int xmpp_conn_is_disconnected(xmpp_conn_t *conn);
int xmpp_conn_send_queue_len(const xmpp_conn_t *conn);
typedef enum {
XMPP_QUEUE_OLDEST = -1,
XMPP_QUEUE_YOUNGEST = -2,
} xmpp_queue_element_t;
char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
xmpp_queue_element_t which);
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);
int xmpp_connect_client(xmpp_conn_t *conn,
const char *altdomain,
unsigned short altport,
xmpp_conn_handler callback,
void *const userdata);
void *userdata);
int xmpp_connect_component(xmpp_conn_t *const conn,
const char *const server,
int xmpp_connect_component(xmpp_conn_t *conn,
const char *server,
unsigned short port,
xmpp_conn_handler callback,
void *const userdata);
void *userdata);
int xmpp_connect_raw(xmpp_conn_t *const conn,
const char *const altdomain,
int xmpp_connect_raw(xmpp_conn_t *conn,
const char *altdomain,
unsigned short altport,
xmpp_conn_handler callback,
void *const userdata);
int xmpp_conn_open_stream_default(xmpp_conn_t *const conn);
int xmpp_conn_open_stream(xmpp_conn_t *const conn,
void *userdata);
int xmpp_conn_open_stream_default(xmpp_conn_t *conn);
int xmpp_conn_open_stream(xmpp_conn_t *conn,
char **attributes,
size_t attributes_len);
int xmpp_conn_tls_start(xmpp_conn_t *const conn);
int xmpp_conn_tls_cafile(xmpp_conn_t *const conn, const char *cafile);
int xmpp_conn_tls_start(xmpp_conn_t *conn);
void xmpp_disconnect(xmpp_conn_t *const conn);
void xmpp_disconnect(xmpp_conn_t *conn);
void xmpp_send(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
void xmpp_send_raw_string(xmpp_conn_t *const conn, const char *const fmt, ...);
void xmpp_send_raw(xmpp_conn_t *const conn,
const char *const data,
const size_t len);
void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len);
/* handlers */
/* if the handler returns false it is removed */
typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn,
void *const userdata);
typedef int (*xmpp_timed_handler)(xmpp_conn_t *conn, void *userdata);
void xmpp_timed_handler_add(xmpp_conn_t *const conn,
void xmpp_timed_handler_add(xmpp_conn_t *conn,
xmpp_timed_handler handler,
const unsigned long period,
void *const userdata);
void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
xmpp_timed_handler handler);
unsigned long period,
void *userdata);
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler);
/* if the handler returns false it is removed */
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *const ctx,
void *const userdata);
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *ctx, void *userdata);
void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler,
const unsigned long period,
void *const userdata);
void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx,
unsigned long period,
void *userdata);
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler);
/* if the handler returns false it is removed */
typedef int (*xmpp_handler)(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const userdata);
typedef int (*xmpp_handler)(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata);
void xmpp_handler_add(xmpp_conn_t *const conn,
void xmpp_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const ns,
const char *const name,
const char *const type,
void *const userdata);
void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler);
const char *ns,
const char *name,
const char *type,
void *userdata);
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler);
void xmpp_id_handler_add(xmpp_conn_t *const conn,
void xmpp_id_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id,
void *const userdata);
void xmpp_id_handler_delete(xmpp_conn_t *const conn,
const char *id,
void *userdata);
void xmpp_id_handler_delete(xmpp_conn_t *conn,
xmpp_handler handler,
const char *const id);
const char *id);
/*
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
@@ -338,94 +533,91 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str);
/* clone a stanza */
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza);
/* copies a stanza and all children */
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza);
/* free a stanza object and it's contents */
int xmpp_stanza_release(xmpp_stanza_t *const stanza);
int xmpp_stanza_release(xmpp_stanza_t *stanza);
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza);
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza);
int xmpp_stanza_is_text(xmpp_stanza_t *const stanza);
int xmpp_stanza_is_tag(xmpp_stanza_t *const stanza);
int xmpp_stanza_is_text(xmpp_stanza_t *stanza);
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza);
/* marshall a stanza into text for transmission or display */
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
char **const buf,
size_t *const buflen);
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen);
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
const char *const name);
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
const char *const ns);
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
const char *const name,
const char *const ns);
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza);
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
const char *name);
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
const char *ns);
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
const char *name,
const char *ns);
/* helper macro for names with a namespace */
#define XMPP_STANZA_NAME_IN_NS(name, ns) name "[@ns='" ns "']"
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...);
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza);
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
xmpp_stanza_t *child,
int do_clone);
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
const char *const name);
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza);
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name);
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza);
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
const char **attr,
int attrlen);
/* concatenate all child text nodes. this function
* returns a string that must be freed by the caller */
char *xmpp_stanza_get_text(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza);
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza);
/* set_attribute adds/replaces attributes */
int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
const char *const key,
const char *const value);
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name);
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text);
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
const char *key,
const char *value);
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name);
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text);
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
const char *const text,
const size_t size);
int xmpp_stanza_del_attribute(xmpp_stanza_t *const stanza,
const char *const name);
const char *text,
size_t size);
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name);
/* common stanza helpers */
const char *xmpp_stanza_get_ns(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_type(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_id(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_to(xmpp_stanza_t *const stanza);
const char *xmpp_stanza_get_from(xmpp_stanza_t *const stanza);
int xmpp_stanza_set_ns(xmpp_stanza_t *const stanza, const char *const ns);
int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id);
int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type);
int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to);
int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from);
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza);
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza);
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns);
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id);
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type);
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to);
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from);
/* allocate and initialize a stanza in reply to another */
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
const char *const error_type,
const char *const condition,
const char *const text);
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza);
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
const char *error_type,
const char *condition,
const char *text);
/* stanza subclasses */
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
const char *const type,
const char *const to,
const char *const id);
const char *type,
const char *to,
const char *id);
char *xmpp_message_get_body(xmpp_stanza_t *msg);
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text);
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text);
xmpp_stanza_t *
xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id);
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id);
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx);
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
xmpp_error_type_t const type,
const char *const text);
xmpp_stanza_t *
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text);
/* jid */
@@ -441,10 +633,22 @@ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
/* event loop */
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout);
void xmpp_run(xmpp_ctx_t *ctx);
void xmpp_stop(xmpp_ctx_t *ctx);
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout);
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout);
/* TLS certificates */
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert);
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert);
void *xmpp_tlscert_get_userdata(const xmpp_tlscert_t *cert);
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert);
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n);
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
xmpp_cert_element_t elmnt);
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt);
void xmpp_tlscert_free(xmpp_tlscert_t *cert);
/* UUID */
@@ -483,6 +687,120 @@ void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
unsigned char **out,
size_t *outlen);
/* RNG */
typedef struct _xmpp_rand_t xmpp_rand_t;
/** Create new xmpp_rand_t object.
*
* @param ctx A Strophe context object
*
* @ingroup Random
*/
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
/** Destroy an xmpp_rand_t object.
*
* @param ctx A Strophe context object
* @param rand A xmpp_rand_t object
*
* @ingroup Random
*/
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
/** Generate random integer.
* Analogue of rand(3).
*
* @ingroup Random
*/
int xmpp_rand(xmpp_rand_t *rand);
/** Generate random bytes.
* Generates len bytes and stores them to the output buffer.
*
* @param rand A xmpp_rand_t object
* @param output A buffer where a len random bytes will be placed.
* @param len Number of bytes reserved for the output..
*
* @ingroup Random
*/
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
/** Generate a nonce that is printable randomized string.
* This function doesn't allocate memory and doesn't fail.
*
* @param rand A xmpp_rand_t object
* @param output A buffer where a NULL-terminated string will be placed.
* The string will contain len-1 printable symbols.
* @param len Number of bytes reserved for the output string, including
* end of line '\0'.
*
* @ingroup Random
*/
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)))
#elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 300)
#define XMPP_DEPRECATED(x) __attribute__((deprecated))
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1500
#define XMPP_DEPRECATED(x) __declspec(deprecated("replaced by " #x))
#else
#define XMPP_DEPRECATED(x)
#endif
XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size);
XMPP_DEPRECATED(internal)
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
XMPP_DEPRECATED(internal)
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s);
XMPP_DEPRECATED(internal)
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
XMPP_DEPRECATED(internal)
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);
XMPP_DEPRECATED(internal)
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
XMPP_DEPRECATED(internal)
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
XMPP_DEPRECATED(internal)
void xmpp_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
const char *fmt,
va_list ap);
XMPP_DEPRECATED(internal)
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
XMPP_DEPRECATED(internal)
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
XMPP_DEPRECATED(internal)
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
XMPP_DEPRECATED(internal)
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
XMPP_DEPRECATED(internal)
void xmpp_debug_verbose(
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
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
}
#endif

14
testbuild.sh Executable file
View File

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

BIN
tests/cert.emptypass.pfx Normal file

Binary file not shown.

BIN
tests/cert.nopass.pfx Normal file

Binary file not shown.

19
tests/cert.pem Normal file
View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAqagAwIBAgIIddFxfQ2VcRIwCgYIKoZIzj0EAwQwSzFJMEcGA1UEAwxA
dmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxlbmd0aC5pcy5hLnZhbGlk
LmFzY2lpLmNoYXJhY3RlcjAgFw0yMzA4MDkxMzI3MDBaGA8yMTI0MDgwOTEzMjcw
MFowSzFJMEcGA1UEAwxAdmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxl
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-----

BIN
tests/cert.pfx Normal file

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>

6
tests/key.pem Normal file
View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBZ90AXrC5n1ZxFGq4WpqMgvH7qw2YddaUzzPFtf9qDRDrRe3xW6nTr
Iywpxdh26GWgBwYFK4EEACKhZANiAAR3zfWucQBD2Fs/tDuiX3veVhlHrquhHGua
aOFBnCYLO/C+neCk/hUf2sg0ovwZktGQJLF27IZEo0AOl5gMFpDSJyRf27gMIf4R
VQDl8Md3irYLj7JMYNcWI9Fmd1q2PaA=
-----END EC PRIVATE KEY-----

9
tests/key_encrypted.pem Normal file
View File

@@ -0,0 +1,9 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,D1537075EE9323A68A08B79257D87D53
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>
@@ -16,9 +17,9 @@
static uint8_t char_to_bin(char c)
{
return c <= '9'
? (uint8_t)(c - '0')
: c <= 'Z' ? (uint8_t)(c - 'A' + 10) : (uint8_t)(c - 'a' + 10);
return c <= '9' ? (uint8_t)(c - '0')
: c <= 'Z' ? (uint8_t)(c - 'A' + 10)
: (uint8_t)(c - 'a' + 10);
}
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.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__
@@ -14,6 +15,7 @@
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ostypes.h"
@@ -33,31 +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 (strcmp(__v1, __v2) != 0) { \
printf("%s differs!\n" \
"expected: %s\n" \
"got: %s\n", \
#v1, __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("%s differs!\n", #v1); \
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(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>
@@ -22,7 +23,7 @@ static int mem_alloc_called = 0;
static int mem_free_called = 0;
static int mem_realloc_called = 0;
void *my_alloc(const size_t size, void *const userdata)
void *my_alloc(size_t size, void *userdata)
{
(void)userdata;
@@ -30,7 +31,7 @@ void *my_alloc(const size_t size, void *const userdata)
return malloc(size);
}
void my_free(void *p, void *const userdata)
void my_free(void *p, void *userdata)
{
(void)userdata;
@@ -38,7 +39,7 @@ void my_free(void *p, void *const userdata)
return free(p);
}
void *my_realloc(void *p, const size_t size, void *const userdata)
void *my_realloc(void *p, size_t size, void *userdata)
{
(void)userdata;
@@ -46,10 +47,10 @@ void *my_realloc(void *p, const size_t size, void *const userdata)
return realloc(p, size);
}
void my_logger(void *const userdata,
const xmpp_log_level_t level,
const char *const area,
const char *const msg)
void my_logger(void *userdata,
xmpp_log_level_t level,
const char *area,
const char *msg)
{
if (strcmp((char *)userdata, "asdf") == 0 && level == XMPP_LEVEL_DEBUG &&
strcmp(area, "test") == 0 && strcmp(msg, "hello") == 0)
@@ -81,22 +82,22 @@ int main()
mylog.userdata = my_str;
ctx = xmpp_ctx_new(&mymem, &mylog);
xmpp_debug(ctx, "test", "hello");
strophe_debug(ctx, "test", "hello");
testptr1 = xmpp_alloc(ctx, 1024);
testptr1 = strophe_alloc(ctx, 1024);
if (testptr1 == NULL) {
xmpp_ctx_free(ctx);
return 1;
}
testptr2 = xmpp_realloc(ctx, testptr1, 2048);
testptr2 = strophe_realloc(ctx, testptr1, 2048);
if (testptr2 == NULL) {
xmpp_free(ctx, testptr1);
strophe_free(ctx, testptr1);
xmpp_ctx_free(ctx);
return 1;
}
xmpp_free(ctx, testptr2);
strophe_free(ctx, testptr2);
xmpp_ctx_free(ctx);

49
tests/test_fuzz_parser.c Normal file
View File

@@ -0,0 +1,49 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "strophe.h"
#include "parser.h"
void xmpp_initialize(void);
void cbtest_handle_start(char *name, char **attrs, void *userdata)
{
(void)name;
(void)attrs;
(void)userdata;
}
void cbtest_handle_end(char *name, void *userdata)
{
(void)name;
(void)userdata;
}
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
{
(void)stanza;
(void)userdata;
}
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
xmpp_ctx_t *ctx;
parser_t *parser;
char *dup = malloc(Size);
memcpy(dup, Data, Size);
ctx = xmpp_ctx_new(NULL, NULL);
parser = parser_new(ctx, cbtest_handle_start, cbtest_handle_end,
cbtest_handle_stanza, NULL);
parser_feed(parser, dup, Size);
free(dup);
parser_free(parser);
xmpp_ctx_free(ctx);
return 0;
}

View File

@@ -0,0 +1,63 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "strophe.h"
#include "resolver.h"
void xmpp_initialize(void);
void cbtest_handle_start(char *name, char **attrs, void *userdata)
{
(void)name;
(void)attrs;
(void)userdata;
}
void cbtest_handle_end(char *name, void *userdata)
{
(void)name;
(void)userdata;
}
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
{
(void)stanza;
(void)userdata;
}
/* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) */
static const unsigned char data2[] = {
0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65,
0x72, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c,
0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1a, 0x00, 0x1e,
0x00, 0x1e, 0x14, 0x66, 0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32,
0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, 0x67, 0x00,
0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1c,
0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0x09, 0x68, 0x65, 0x72, 0x6d, 0x65,
0x73, 0x32, 0x76, 0x36, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03,
0x6f, 0x72, 0x67, 0x00,
};
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
xmpp_ctx_t *ctx;
resolver_srv_rr_t *srv_rr_list;
unsigned char *dup = malloc(Size + 64);
memcpy(dup, data2, 64);
memcpy(&dup[64], Data, Size);
ctx = xmpp_ctx_new(NULL, NULL);
resolver_srv_lookup_buf(ctx, dup, 64 + Size, &srv_rr_list);
if (srv_rr_list != NULL)
resolver_srv_free(ctx, srv_rr_list);
free(dup);
xmpp_ctx_free(ctx);
return 0;
}

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>
@@ -55,7 +56,7 @@ int main(int argc, char **argv)
}
/* allocate a hash table */
table = hash_new(ctx, TABLESIZE, xmpp_free);
table = hash_new(ctx, TABLESIZE, strophe_free);
if (table == NULL) {
/* table allocation failed! */
return 1;
@@ -63,7 +64,7 @@ int main(int argc, char **argv)
/* test insertion */
for (i = 0; i < nkeys; i++) {
err = hash_add(table, keys[i], xmpp_strdup(ctx, values[i]));
err = hash_add(table, keys[i], strophe_strdup(ctx, values[i]));
if (err)
return err;
}
@@ -76,7 +77,7 @@ int main(int argc, char **argv)
/* test replacing old values */
for (i = 0; i < nkeys; i++) {
err = hash_add(table, keys[0], xmpp_strdup(ctx, values[i]));
err = hash_add(table, keys[0], strophe_strdup(ctx, values[i]));
if (err)
return err;
if (hash_num_keys(table) != nkeys)
@@ -88,7 +89,7 @@ int main(int argc, char **argv)
return 1;
}
/* restore value for the 1st key */
hash_add(table, keys[0], xmpp_strdup(ctx, values[0]));
hash_add(table, keys[0], strophe_strdup(ctx, values[0]));
/* test cloning */
clone = hash_clone(table);

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

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 */
@@ -21,32 +22,36 @@
#include "rand.c"
/* stubs to build test without whole libstrophe */
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
{
(void)ctx;
(void)size;
return NULL;
}
void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
void strophe_free(const xmpp_ctx_t *ctx, void *p)
{
(void)ctx;
(void)p;
}
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
#ifndef HAVE_SNPRINTF
int strophe_snprintf(char *str, size_t count, const char *fmt, ...)
{
(void)str;
(void)count;
(void)fmt;
return 0;
}
#endif /* HAVE_SNPRINTF */
uint64_t time_stamp(void)
{
return 0;
}
#ifndef USE_GETRANDOM
static struct {
const char *entropy_input;
const char *nonce;
@@ -158,3 +163,19 @@ int main()
return 0;
}
#else
int main()
{
uint8_t output[1024];
xmpp_rand_t *rand = xmpp_rand_new(NULL);
assert(rand != NULL);
/* this would assert if it failed */
xmpp_rand_bytes(rand, output, sizeof(output));
return 0;
}
#endif

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,14 +7,13 @@
* 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>
#include <stdio.h>
#include "strophe.h"
#include "rand.h"
#include "resolver.h"
#include "test.h"
@@ -175,10 +175,10 @@ int main()
xmpp_ctx_t *ctx;
xmpp_rand_t *rand;
resolver_srv_rr_t *srv_rr_list;
char *domain;
char *domain, *rnd_b64;
unsigned char *buf;
unsigned short port;
size_t i;
size_t i, slen;
int ret;
ctx = xmpp_ctx_new(NULL, NULL);
@@ -217,17 +217,25 @@ int main()
rand = xmpp_rand_new(ctx);
assert(rand != NULL);
assert(sizeof(data2) > 64);
buf = xmpp_alloc(ctx, sizeof(data2));
buf = strophe_alloc(ctx, sizeof(data2));
assert(buf != NULL);
memcpy(buf, data2, 64);
xmpp_rand_bytes(rand, &buf[64], sizeof(data2) - 64);
ret = resolver_srv_lookup_buf(ctx, buf, sizeof(data2), &srv_rr_list);
if (ret == XMPP_DOMAIN_FOUND && srv_rr_list != NULL)
resolver_srv_free(ctx, srv_rr_list);
xmpp_free(ctx, buf);
xmpp_rand_free(ctx, rand);
printf("ok\n");
printf("Broken message was:\n");
rnd_b64 = xmpp_base64_encode(ctx, buf, sizeof(data2));
slen = strlen(rnd_b64);
for (i = 0; i < slen; i += 64) {
printf("%.64s\n", &rnd_b64[i]);
}
strophe_free(ctx, buf);
strophe_free(ctx, rnd_b64);
xmpp_ctx_free(ctx);
return 0;

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>
@@ -29,23 +30,6 @@ static const char response_md5[] =
"MDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL3NvbWVyZWFsbSIscmVz"
"cG9uc2U9NGVhNmU4N2JjMDkzMzUwNzQzZGIyOGQ3MDIwOGNhZmIsY2hhcnNl"
"dD11dGYtOA==";
static const char response_md5_rfc[] =
"dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i"
"T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw"
"MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i"
"LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo"
"YXJzZXQ9dXRmLTgK";
static const char challenge_md5_2[] =
"cmVhbG09ImVsd29vZC5pbm5vc29mdC5jb20iLG5vbmNlPSJPQTZNRzl0"
"RVFHbTJoaCIscW9wPSJhdXRoIixhbGdvcml0aG09bWQ1LXNlc3MsY2hh"
"cnNldD11dGYtOA==";
static const char response_md5_2[] =
"Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iY2hyaXMiLHJlYWxtPSJlbHdvb2"
"QuaW5ub3NvZnQuY29tIixub25jZT0iT0E2TUc5dEVRR20yaGgiLG5jPTAw"
"MDAwMDAxLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLGRpZ2VzdC11cmk9Im"
"ltYXAvZWx3b29kLmlubm9zb2Z0LmNvbSIscmVzcG9uc2U9ZDM4OGRhZDkw"
"ZDRiYmQ3NjBhMTUyMzIxZjIxNDNhZjcscW9wPWF1dGg=";
int test_plain(xmpp_ctx_t *ctx)
{
@@ -60,7 +44,7 @@ int test_plain(xmpp_ctx_t *ctx)
/* SASL PLAIN returned incorrect string! */
return 2;
}
xmpp_free(ctx, result);
strophe_free(ctx, result);
return 0;
}

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 */

93
tests/test_send_queue.c Normal file
View File

@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_send_queue.c
** libstrophe XMPP client library -- test routines for the send queue
**
** Copyright (C) 2021 Steffen Jaeckel
**
** 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"
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;
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;
xmpp_conn_set_sm_state(conn, sm_state);
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(1, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "bar", 3);
ENSURE_EQ(2, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "baz", 3);
ENSURE_EQ(3, xmpp_conn_send_queue_len(conn));
xmpp_send_raw(conn, "baan", 4);
ENSURE_EQ(4, xmpp_conn_send_queue_len(conn));
conn->send_queue_head->wip = 1;
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(2, xmpp_conn_send_queue_len(conn));
conn->send_queue_head->wip = 0;
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(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(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(0, xmpp_conn_send_queue_len(conn));
conn->state = state;
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

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;
@@ -37,7 +45,7 @@ int main(void)
for (x = 0; fp_fmt[x] != NULL; x++)
for (y = 0; fp_nums[y] != 0; y++) {
xmpp_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
strophe_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
sprintf(buf2, fp_fmt[x], fp_nums[y]);
if (strcmp(buf1, buf2)) {
printf("xmpp_snprintf doesn't match Format: "
@@ -50,7 +58,7 @@ int main(void)
for (x = 0; int_fmt[x] != NULL; x++)
for (y = 0; int_nums[y] != 0; y++) {
xmpp_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
strophe_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
sprintf(buf2, int_fmt[x], int_nums[y]);
if (strcmp(buf1, buf2)) {
printf("xmpp_snprintf doesn't match Format: "
@@ -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 */
@@ -17,6 +18,8 @@
#include <stdlib.h>
#include <string.h>
#include "test.h"
#define MAGICPTR ((void *)0xfeedbeef)
static unsigned long used_blocks = 0;
@@ -97,7 +100,7 @@ static void test_stanza_from_string(xmpp_ctx_t *ctx)
size_t buflen;
int ret;
static const char *str =
const char *str =
"<signcrypt xmlns=\"urn:xmpp:openpgp:0\"><to "
"jid=\"user@domain.com\"/><time "
"stamp=\"2020-06-03T21:26:24+0200\"/><rpad/><payload><body "
@@ -107,9 +110,137 @@ static void test_stanza_from_string(xmpp_ctx_t *ctx)
assert(stanza != NULL);
ret = xmpp_stanza_to_text(stanza, &buf, &buflen);
assert(ret == XMPP_EOK);
assert(strcmp(buf, str) == 0);
COMPARE(str, buf);
xmpp_free(ctx, buf);
xmpp_stanza_release(stanza);
/* create a string with two stanzas to make sure we don't
* leak any memory when we convert them to a xmpp_stanza_t
*/
buf = malloc(strlen(str) * 2 + 1);
assert(buf != NULL);
memcpy(buf, str, strlen(str) + 1);
memcpy(&buf[strlen(str)], str, strlen(str) + 1);
stanza = xmpp_stanza_new_from_string(ctx, buf);
assert(stanza != NULL);
free(buf);
ret = xmpp_stanza_to_text(stanza, &buf, &buflen);
assert(ret == XMPP_EOK);
COMPARE(str, buf);
xmpp_free(ctx, buf);
xmpp_stanza_release(stanza);
/* Error path. */
stanza = xmpp_stanza_new_from_string(ctx, "<uu><uu>tt");
assert(stanza == NULL);
}
static void test_stanza_error(xmpp_ctx_t *ctx)
{
xmpp_stanza_t *stanza;
xmpp_stanza_t *error;
xmpp_stanza_t *item;
xmpp_stanza_t *mood;
char *buf;
size_t buflen;
const char *attr[10];
int attrlen = ARRAY_SIZE(attr);
int ret;
static const char *str =
"<iq from='romeo@montague.lit/home' to='juliet@capulet.lit/chamber' "
"type='get' id='e2e1'><ping xmlns='urn:xmpp:ping'/></iq>";
static const char *str_error =
"<error type=\"cancel\"><service-unavailable "
"xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/></error>";
// clang-format off
static const char *str_mood =
"<iq from='juliet@capulet.lit/balcony' id='publish1' type='set'>"
"<pubsub xmlns='http://jabber.org/protocol/pubsub'>"
"<publish node='http://jabber.org/protocol/mood'>"
"<item>"
"<mood xmlns='http://jabber.org/protocol/mood'>"
"<annoyed/>"
"<text>curse my nurse!</text>"
"</mood>"
"</item>"
"</publish>"
"</pubsub>"
"</iq>";
// clang-format on
stanza = xmpp_stanza_new_from_string(ctx, str);
assert(stanza != NULL);
error =
xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL);
assert(error != NULL);
mood = xmpp_stanza_new_from_string(ctx, str_mood);
assert(mood != NULL);
assert(xmpp_stanza_get_to(error) != NULL);
COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error));
assert(xmpp_stanza_get_from(error) != NULL);
COMPARE("juliet@capulet.lit/chamber", xmpp_stanza_get_from(error));
assert(xmpp_stanza_get_id(error) != NULL);
COMPARE("e2e1", xmpp_stanza_get_id(error));
assert(xmpp_stanza_get_type(error) != NULL);
COMPARE("error", xmpp_stanza_get_type(error));
/* FAIL - no list given */
item = xmpp_stanza_get_child_by_path(mood, NULL);
assert(item == NULL);
/* FAIL - first entry doesn't match */
item = xmpp_stanza_get_child_by_path(mood, "foo", NULL);
assert(item == NULL);
/* FAIL - 'iq' has no namespace */
item = xmpp_stanza_get_child_by_path(
mood, XMPP_STANZA_NAME_IN_NS("iq", "foobar"),
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/pubsub"),
"publish", "item", "mood", NULL);
assert(item == NULL);
/* FAIL - 'pubsub' is in another namespace */
item = xmpp_stanza_get_child_by_path(
mood, "iq",
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/foobar"),
"publish", "item", "mood", NULL);
assert(item == NULL);
item = xmpp_stanza_get_child_by_path(mood, "iq", "pubsub", "publish",
"item", "mood", NULL);
assert(item != NULL);
assert(xmpp_stanza_get_children(item) != NULL);
assert(xmpp_stanza_get_name(xmpp_stanza_get_children(item)) != NULL);
COMPARE("annoyed", xmpp_stanza_get_name(xmpp_stanza_get_children(item)));
item = xmpp_stanza_get_child_by_path(
mood, "iq",
XMPP_STANZA_NAME_IN_NS("pubsub", "http://jabber.org/protocol/pubsub"),
"publish", "item",
XMPP_STANZA_NAME_IN_NS("mood", "http://jabber.org/protocol/mood"),
NULL);
assert(item != NULL);
assert(xmpp_stanza_get_children(item) != NULL);
assert(xmpp_stanza_get_name(xmpp_stanza_get_children(item)) != NULL);
COMPARE("annoyed", xmpp_stanza_get_name(xmpp_stanza_get_children(item)));
ret = xmpp_stanza_get_attributes(error, attr, attrlen);
/* attr contains both attribute name and value. */
assert(ret == 8);
item = xmpp_stanza_get_child_by_name(error, "error");
assert(item != NULL);
ret = xmpp_stanza_to_text(item, &buf, &buflen);
assert(ret == XMPP_EOK);
COMPARE(str_error, buf);
xmpp_free(ctx, buf);
xmpp_stanza_release(mood);
xmpp_stanza_release(stanza);
xmpp_stanza_release(error);
}
int main()
@@ -122,6 +253,7 @@ int main()
test_stanza_add_child(ctx);
test_stanza_from_string(ctx);
test_stanza_error(ctx);
xmpp_ctx_free(ctx);
xmpp_shutdown();

Some files were not shown because too many files have changed in this diff Show More