577 Commits

Author SHA1 Message Date
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
Dmitry Podgorny
c07ac0a68d conn: don't crash when user sets password to NULL
Make it possible to reset password to NULL. It is not required for
ANONYMOUS authentication. Also, report an error and disconnect if
password is not set and libstrophe should try authentication mechanisms
other than ANONYMOUS.
2020-09-30 20:56:52 +03:00
Dmitry Podgorny
60ce94c267 auth: add missed space in log message 2020-09-30 20:43:32 +03:00
Dmitry Podgorny
99f2d4fe54 travis-ci: build libstrophe against LibreSSL 2020-09-24 15:07:15 +03:00
Dmitry Podgorny
acced31192 tls/openssl: Fix undefined error codes for LibreSSL
LibreSSL doesn't define all error codes which OpenSSL defines. Wrap them
with #ifndef.

Reference: https://bugs.gentoo.org/744127
2020-09-24 13:34:49 +03:00
Dmitry Podgorny
9ee08f8f2a Release libstrophe-0.10.0 2020-09-16 00:27:34 +03:00
Dmitry Podgorny
7c1061ccbb autotools: remove rpm files 2020-09-16 00:27:34 +03:00
Dmitry Podgorny
7eab7c3f5e Android: add missed files and -DHAVE_DECL_VA_COPY 2020-09-16 00:21:01 +03:00
Dmitry Podgorny
4d93b6c0d6 Check for va_copy declaration
ctx.c contains a workaround for systems without va_copy() support.
Improve this workaround in the way which is described in the autoconf
manual.

Also check for va_copy declaration in configure time.

Fixes #160.
2020-09-16 00:07:41 +03:00
Dmitry Podgorny
65b3a0996f README: mention about license 2020-09-15 03:14:54 +03:00
Dmitry Podgorny
4dd78be10d tls/openssl: fix compilation with older openssl
Not all error codes are present in older versions of openssl.
2020-09-15 03:07:02 +03:00
Dmitry Podgorny
8c076ac969 stanza: remove attribute xmlns in xmpp_stanza_reply() 2020-09-15 02:21:18 +03:00
Dmitry Podgorny
7c68aced35 stanza: add xmpp_stanza_reply_error()
This function makes an error reply stanza as described in
https://tools.ietf.org/html/rfc6120#section-8.3
2020-09-15 02:19:20 +03:00
Dmitry Podgorny
197896ba1b tls/openssl: improve logging
Log error names and codes to increase verbosity in debug mode.
2020-06-18 22:18:59 +03:00
Dmitry Podgorny
bef411f319 sha1: fix zeroing storage
Compilers remove dead assignments in optimization phase. But SHA1 code
uses them for wiping of sensitive data.  Fix this with the trick used
by openssl.
2020-06-18 22:14:04 +03:00
Dmitry Podgorny
7f5ce7d2ae sha1: remove SHA1HANDSOFF
We don't support configuration when SHA1 functions overwrite user's
data. So remove this code not to confuse.
2020-06-19 00:51:33 +03:00
Dmitry Podgorny
47e27c837a examples/basic: add new conn flags
Add options:
  --trust-tls
  --legacy-auth
2020-06-07 20:43:41 +03:00
Dmitry Podgorny
50a4a983cd handler: add documentation for global timed handlers 2020-06-06 00:11:24 +03:00
Dmitry Podgorny
428681a185 stanza: add xmpp_stanza_new_from_string()
This function creates a stanza object from a string. The string must
contain a single complete stanza. Multiple stanzas will lead to memory
leak and this is not handled in the patch.
2020-06-04 03:44:21 +03:00
Dmitry Podgorny
3dcc5a60f2 Introduce global timed handlers
There are situations when applications need more predictable timed
handlers that don't depend on connection status. Other usecase of
the global handlers is to manage offline connections, for example,
reconnect after a disconnection.
2020-06-03 02:11:40 +03:00
Dmitry Podgorny
cba21b5598 examples/register: replace getline() with fgets()
getline(3) doesn't exist on all systems.
2020-05-31 22:05:21 +03:00
Dmitry Podgorny
a65dd1059d resolver: don't build legacy win32 code with c-ares
Fix ifdef macros to exclude legacy win32 code when it is not needed.
2020-05-31 21:43:09 +03:00
Dmitry Podgorny
7c83eb0efa windows: rewrite headers in lower case
Otherwise, compilation fails on systems that are case sensitive.

Reference: #159.
2020-05-31 21:03:44 +03:00
Steffen Jaeckel
f2c658723f clean-up 2020-04-30 12:03:57 +02:00
Dmitry Podgorny
7ac4b1cc12 Update ChangeLog 2020-04-29 00:24:42 +03:00
Dmitry Podgorny
1c4cb66f5a conn: remove ternary operator
Remove ternary operator where it is redundant.
2020-04-28 23:40:46 +03:00
Dmitry Podgorny
6a50c9cea6 ChangeLog: mention about fixed IPv6 issue 2020-04-02 22:14:15 +03:00
Dmitry Podgorny
37b12ad788 configure: enable check for snprintf()
b2e64587 adds the check but it is commented for unknown reason. Enable
it to use advantage of system snprintf() implementation.
2020-04-02 22:12:30 +03:00
Dmitry Podgorny
5530ac690f rpm: remove libstrophe.spec
Major Linux distributions have added libstrophe to their repositories.
We don't have to support our own spec file. If someone needs spec to
build rpm manually, refer to Linux distributions, for example:

https://build.opensuse.org/package/show/network/libstrophe
https://copr-dist-git.fedorainfracloud.org/cgit/linkdupont/fedora-link-extras/libstrophe.git
2020-04-02 19:38:52 +03:00
Dmitry Podgorny
3063d886bd sasl: remove unused argument
_add_key() is a static function which is used only for DIGEST_MD5. Its
argument 'len' is not used and remains 0. Remove it.
2020-03-31 18:58:36 +03:00
Oleg Synelnykov
198bdd77d0 Remove -Wno-unused-parameter
Introduced UNUSED macro with cast to void in commoh.h for internal
use. Used cast to void directly in those files which do not
include common.h. Although this change doesn't fix semantic issues
with unused function parameters, it does explicitly mark all those
places, which might require attention in future.
2020-03-31 17:37:12 +03:00
Dmitry Podgorny
20c039fdb8 sock: replace sockaddr with sockaddr_storage
sockaddr structure may be insufficient to hold IPv6 address. Replace it
with sockaddr_storage structure.

Fixes #153.
2020-03-31 16:29:09 +03:00
Dmitry Podgorny
c92fc849e7 Remove C++ files
Libstrophe used to provide incomplete C++ API which missed connection
class and wasn't supported for a long time. Remove this API.
2020-02-22 23:19:40 +02:00
Dmitry Podgorny
6fdac3ae72 tests/sha512: add test case for a million of a 2020-01-31 01:59:42 +02:00
Dmitry Podgorny
623ff164de tests/scram: add HMAC test vector
Add test for HMAC and make other tests independent from SHA1.
2020-01-31 01:59:42 +02:00
Dmitry Podgorny
faa46d28a5 scram: fix HMAC-SHA-512
HMAC was originally implemented for SHA1 digest according to RFC2104.
The RFC defined blocksize for all digests as 64 bytes. But RFC4868
requires blocksize for SHA384/SHA512 to be 128 bytes.

Extend HMAC implementation to support variable blocksize and fix it for
SHA384/SHA512.
2020-01-31 01:59:42 +02:00
Dmitry Podgorny
1ca10fd167 Fix SASL SCRAM implementation
* Add SCRAM_DIGEST_SIZE macro for maximum possible digest size. It will
  avoid new buffer overflow errors when new digests are added.
* Fix buffer overflow in sasl_scram(). Buffers were allocated for SHA1
  digest size.
* Fix bug with handler re-registration when a SASL SCRAM mechanism
  fails.
2020-01-31 01:59:42 +02:00
Steffen Jaeckel
709e41fd10 add SHA256&SHA512 tests 2020-01-31 01:59:42 +02:00
Steffen Jaeckel
771d5865ae add SCRAM-SHA-256 and SCRAM-SHA-512 support 2020-01-31 01:59:42 +02:00
Steffen Jaeckel
fc064bc883 re-factor SCRAM to be independent of the hash 2020-01-31 01:34:23 +02:00
Steffen Jaeckel
2ca2fcfedd add stripped-down & slightly-modified version of LibTomCrypt SHA512 2020-01-31 01:34:09 +02:00
Steffen Jaeckel
d78b02ee24 add stripped-down & slightly-modified version of LibTomCrypt SHA256 2020-01-31 01:33:51 +02:00
Steffen Jaeckel
2a94a49cef add required LibTomCrypt load/store functions 2020-01-31 01:32:45 +02:00
Dmitry Podgorny
562a06425b Unify coding style
@sjaeckel integrated clang-format with formal coding style. Run his
script and commit changes.

There are pros and cons of this commit.

Mixed coding style is a "broken window". A good single style simplifies
reading and writing code.

On the other hand, this is a big change which will lead to conflicts.
2020-01-31 01:16:50 +02:00
Steffen Jaeckel
eef07cef36 add code style check to travis 2020-01-31 01:14:32 +02:00
Steffen Jaeckel
2e21d5a021 add new format make target 2020-01-31 01:14:32 +02:00
Steffen Jaeckel
abd1b08a97 trim trailing spaces 2020-01-31 01:14:32 +02:00
Steffen Jaeckel
3feed16e2e ignore eclipse related stuff 2020-01-31 01:14:32 +02:00
Dmitry Podgorny
6b38216124 util: replace time_stamp() implementation
Replace Windows/Xbox implementation with solution from NetXMS project.
2020-01-26 00:52:54 +02:00
Dmitry Podgorny
8cfb2d2a03 XEP-0077: add examples/register
XEP-0077 will not be implemented as part of libstrophe's API, but an
example is provided. The example shows how to use the "raw" connection
to register a user.

Closes #54.
2020-01-25 22:59:19 +02:00
Michael Vetter
1b10e8f987 resolver: include headers for select(2)
C-ares implementation uses select(2) but doesn't include required
headers.

Fixes #149.
2020-01-25 22:06:07 +02:00
Dmitry Podgorny
178f566ae2 Unify include of strophe.h 2020-01-10 01:28:47 +02:00
Dmitry Podgorny
0326e5c5e2 handler: avoid unnamed union
Unnamed unions appeared only in C11 standard. Don't use them to
increase portability.
2020-01-10 01:22:53 +02:00
Dmitry Podgorny
212df6d036 tests: add test for stanza functions
Check reference counting in xmpp_stanza_add_child() amd
xmpp_stanza_add_child_ex().
2020-01-10 00:50:22 +02:00
Dmitry Podgorny
4b04db6b2d stanza: add xmpp_stanza_add_child_ex()
This function is introduced by NetXMS project.
2020-01-10 00:49:45 +02:00
Dmitry Podgorny
fb327d4bd1 parser/expat: provide memory handling suite for 1 ctx
Libstrophe doesn't override memory allocation functions in XML libraries
and default allocators are used as result.

Current patch solves this issue partially for expat module. The module
caches 1st strophe context and overrides expat's allocator for parsers
within this context. For all other contexts, fallback to default
allocator.

This is modified version of solution from UnrealEngine project.
2020-01-07 22:45:15 +02:00
Dmitry Podgorny
a811c868ff util: add time_stamp() implementation for XBOX
Backported from UnrealEngine project.
2020-01-07 22:24:10 +02:00
Dmitry Podgorny
3f05bafffb event: move buffer size to a macro
Move buffer size for incoming messages to a macro. So it can be changed
in one place.
2020-01-07 22:20:12 +02:00
Dmitry Podgorny
133abd096d conn: disconnect if user destroys an active connection object
Backported from UnrealEngine project.
2020-01-07 22:16:11 +02:00
Dmitry Podgorny
cecd6b20e8 stanza: add xmpp_stanza_get_context()
This function is backported from UnrealEngine project.
2020-01-07 22:13:39 +02:00
Dmitry Podgorny
2272681ea2 conn: add state checking API
New boolean functions:
  xmpp_conn_is_connecting()
  xmpp_conn_is_connected()
  xmpp_conn_is_disconnected()

These functions are backported from UnrealEngine project.
2020-01-07 22:12:11 +02:00
Dmitry Podgorny
17f79cbbb8 auth: replace stricmp() with _stricmp()
Newer visual studios deprecate stricmp(). Replace it to fix compilation.
2020-01-07 00:24:57 +02:00
Dmitry Podgorny
2570084cae event: don't redefine error codes
Error codes can be defined in visual studio. Unconditional redefine
causes warnings.
2020-01-07 00:14:59 +02:00
Dmitry Podgorny
b2e6458736 autotools: check for snprintf and vsnprintf
src/snprinf.[ch] rely on HAVE_SNPRINTF macro. The check is missed in
configure script and libstrophe always use internal implementation
of snprinf() which may be inefficient. Add check to configure.ac.
2020-01-05 01:14:22 +02:00
Dmitry Podgorny
c192a2b181 md5: remove unused parameter 2020-01-05 00:30:11 +02:00
Dmitry Podgorny
2e325e5e68 tests/sha1: fix type mismatch warning 2020-01-05 00:28:01 +02:00
Manuel Kasper
f0436490b0 Enable SNI with OpenSSL 0.9.8f as well 2019-12-24 09:07:51 +01:00
Manuel Kasper
243664926f Enable TLS SNI 2019-12-23 14:34:19 +01:00
Dmitry Podgorny
466fa2adbf sha1: don't clean local variable i on exit
In this place value of variable i is constant and predictable. No point
to clean it. Furthermore, since the value is not used after assignment,
compiler will optimize and remove the assignment.

This patch fixes a warning from the clang static analyzer.
2019-11-10 16:04:17 +02:00
Dmitry Podgorny
284e8f4421 tls/openssl: check return code
Check return code of SSL_CTX_set_default_verify_paths() and fail TLS on
an error. However, ignore the error when XMPP_CONN_FLAG_TRUST_TLS is
set.
2019-11-10 15:57:23 +02:00
Dmitry Podgorny
6852da97ae ChangeLog: xmpp_stanza_get_child_by_name_and_ns() 2019-11-30 20:39:17 +02:00
Dmitry Podgorny
35f37e8c23 autotools: link against libm with manually detected libxml2
Fixes build on QNX.
2019-11-30 20:36:34 +02:00
Michael Vetter
cbe6701c14 Add xmpp_stanza_get_child_by_name_and_ns()
I think it's fairly common in the XMPP world to actually want a stanza
by it's NS but also want a certain name.

For example this was needed in Profanity:
68af0aad65
2019-11-25 13:58:05 +00:00
Dmitry Podgorny
296df2fca9 tls/openssl: don't use deprecated function in 1.1.0+
SSLv23_client_method() was deprecated in OpenSSL 1.1.0. It is left as
macro to TLS_client_method.
2019-11-25 13:45:51 +00:00
Dmitry Podgorny
7caf88a6a8 rand: avoid VLA in xmpp_rand_nonce()
Don't use additional buffer. Instead, use half of the output buffer.
2019-11-19 01:46:35 +02:00
Dmitry Podgorny
ebaad72a35 ChangeLog: mention recent changes 2019-11-19 01:11:56 +02:00
François Revol
962bf4de5e Haiku: skip -Wextra -Wno-unused-parameter for GCC 2.95
This is needed to compile for the primary arch.
2019-11-17 15:34:27 +01:00
François Revol
884dace400 Haiku: link with libnetwork 2019-11-17 14:29:24 +01:00
François Revol
234bef4025 Haiku: C89 2019-11-17 14:28:26 +01:00
François Revol
45cbd51633 Haiku: fake va_copy for now 2019-11-17 14:21:37 +01:00
Dmitry Podgorny
484e971323 travis-ci: check --enable-cares 2019-10-14 20:37:41 +03:00
Dmitry Podgorny
038404cb99 sock: reduce copy-paste
Move setting (non)block mode to a single static function.
2019-10-14 20:13:04 +03:00
Dmitry Podgorny
6f075bfc71 resolver: fix gcc 9.2.0 warning
message_name_get() adds "." to the tail and it leads to:
    strncpy(&dest, ".", 1);
gcc 9.2.0 generates a warning, because string is truncated without
copying trailing '\0'. Replace strncpy() with memcpy() to fix the
warning.
2019-10-14 17:36:11 +03:00
Dmitry Podgorny
7aed036896 sha1: fixed libstrophe build issues on Windows
Backported from NetXMS.
2019-10-14 09:54:54 +03:00
Dmitry Podgorny
0e8c88a5e4 resolver: backport patch from netxms
explicitely use non-UNICODE structures in libstrophe DNS resolver on Windows
2019-10-14 09:44:11 +03:00
Dmitry Podgorny
a13847d994 resolver: add c-ares support
This patch based on the patch from @gbour (see #7). With
--enable-cares old implementation will be replaced with c-ares API.
2019-10-14 06:51:08 +03:00
Dmitry Podgorny
18b67d6eaf tls/openssl: add LibreSSL support
OpenSSL and LibreSSL versions are incompatible. Moreover, LibreSSL
always define OPENSSL_VERSION_NUMBER as 0x20000000L. Instead of checking
for LibreSSL everywhere explicitly, redefine OPENSSL_VERSION_NUMBER.

See similar issues with nginx project: https://trac.nginx.org/nginx/ticket/1605
2019-10-11 01:59:34 +03:00
Viktor Ivanov
886f45584c Fix bug with xmpp chat not connecting
The facebook suddenly stopped connecting on my dev machine. I still cannot
explain what caused this change in behaviour, but I was finally able to
diagnose and fix the problem.

Chat was not connecting due to an error in tls_start. The windows function
InitializeSecurityContext was returning a SEC_E_INCOMPLETE_MESSAGE status
which was being treated as an error. However, the documentation states
that this is not an error. This status simply indicates that more data
needs to be read. This commit does precisely that -- when
SEC_E_INCOMPLETE_MESSAGE we read more data from the socket and call
InitializeSecurityContext again.
2019-10-11 00:54:37 +03:00
Dmitry Podgorny
4f915d7515 Release libstrophe-0.9.3 2019-10-10 21:47:08 +03:00
Dmitry Podgorny
e25ca7719f conn: don't hardcode buffer size
Use sizeof instead of hardcoded size, so the buffer can be changed
easily.
2019-10-02 03:26:36 +03:00
Dmitry Podgorny
2bf5cc0e41 auth: disable PLAIN when other mechanisms are supported
PLAIN mechanism sends password in BASE64 encoding which everyone can
read. As result, debug logs expose password and users often don't remove
it when post the logs in the Internet.

Usually, both a secure mechanism and PLAIN are used in the scenario when
username or password is incorrect. PLAIN fails in this scenario anyway.
2019-10-01 23:38:50 +03:00
Dmitry Podgorny
30f3cc9263 auth: disable legacy auth by default
Legacy authentication can expose password in plaintext. Since this is
not widely used mechanism, disable it by default. It can be enabled back
with connection option XMPP_CONN_FLAG_LEGACY_AUTH.
2019-10-01 22:34:31 +03:00
Dmitry Podgorny
e120109644 Android: update makefiles
This patch makes libstrophe build with NDK r20 and new libexpat.
2019-09-25 11:00:52 +00:00
Dmitry Podgorny
b4f13205d8 ChangeLog: notes for 0.9.3 2019-09-25 10:58:09 +00:00
Dmitry Podgorny
4797522d88 ChangeLog: mention missed notes for 0.9.2 2019-09-25 10:57:03 +00:00
Hoenig Mark (TT/EIS3-Lol)
5ee06776ee tls/openssl: don't call SSL_shutdown() after a fatal error
According to SSL_shutdown(3), the function must not be called
if previous fatal error occurred.
2019-07-03 15:14:01 +02:00
Dmitry Podgorny
c40b4c12c1 parser/expat: don't cast name to char*
name already has type char*, remove redundant code.
2019-03-04 18:01:29 +02:00
Dmitry Podgorny
d8329960c5 rand: don't pass NULL to memcpy()
Fixes a compiler's warning.
2019-03-04 18:00:01 +02:00
Andrey Starodubtsev
94ece33f16 attempt to free already freed memory fixed 2018-12-26 15:52:02 +03:00
Andrey Starodubtsev
b179c2d277 some memory leaks fixed 2018-12-26 15:44:55 +03:00
Andrey Starodubtsev
ab568a2666 memory leak in hash values fixed 2018-12-13 14:28:41 +03:00
Nikolay Lineckiy
c10c930e8f Fix incomplete send queue cleanup
Due to incomplete reset xmpp_conn_t object could not be safely reused
for reconnection
2018-12-04 18:58:36 +03:00
Dmitry Podgorny
6ddf424192 travis-ci: add -Werror flag
This flag has been removed from Makefile.am.
2018-11-06 11:45:39 +02:00
Dmitry Podgorny
6aff44184e autotools: remove AM_CFLAGS
AM_CFLAGS is ignored when target specific target_CFLAGS are present.
2018-11-06 11:35:52 +02:00
Dmitry Podgorny
9cc9ea86bb tls/openssl: log some info about certificate
Log subject name and issuer name from certificate after TLS connection
is established or fails to connect.
2018-11-06 10:53:46 +02:00
Dmitry Podgorny
13a0bc463c rand: fix comment 2018-10-24 22:28:54 +03:00
jngd
8ff510a3af Fix annoying comment about how disconnect can be performed 2018-09-26 19:15:58 +02:00
Dmitry Podgorny
633d1e9b52 travis-ci: remove check package from dependecies 2018-07-30 20:45:08 +03:00
Dmitry Podgorny
a13ba65cfb Don't pass NULL to strcmp()
There are places where code relies on well-formed stanzas and expects
that ns attribute is always present. Check whether ns NULL or not before
passing it to strcmp().

Fixes #121.
2018-07-30 20:38:43 +03:00
Stu Tomlinson
0e6b0ef84d Only require session if it is not optional
RFC6121 removes the precondition of establishing a session before a client
may exchange XML stanzas on an XMPP network.

This PR allows libstrophe to not request session where it is indicated
as optional by the server, saving a round trip.

https://tools.ietf.org/html/rfc6121#section-1.4
2018-06-08 14:27:29 +00:00
Xavi
9a86efabe5 Remove unused variable 2018-02-23 23:21:37 +01:00
Dmitry Podgorny
a4b05cf156 Release libstrophe-0.9.2 2018-02-23 14:19:15 +02:00
Dmitry Podgorny
06da6d5125 autotools: remove -Werror
We need it only in development versions. Let releases build with
warnings.
2018-02-23 14:19:15 +02:00
Dmitry Podgorny
1bcf77608d crypto: fix typos in comments 2018-02-23 14:05:56 +02:00
Dmitry Podgorny
aab0cc3e3e parser/libxml2: clear parser->stanza properly
- Release stanza in parser_free()
 - Clear parser->stanza even if xmlCreatePushParserCtxt() fails
2018-02-18 14:51:16 +02:00
Dmitry Podgorny
822f95071a sasl: fix memory leak in DIGEST-MD5 implementation
Don't add "qop" two times to hash table, otherwise, an entry is lost
without freeing memory.
2018-02-18 14:02:17 +02:00
Dmitry Podgorny
27f613bc84 stanza: small fixes in xmpp_error_new()
- Receive const text
 - Move variable declaration to the beginning of the block
2018-02-18 13:18:43 +02:00
Dmitry Podgorny
af1ef3855b resolver: suppress warning in windows code 2018-02-18 13:08:16 +02:00
Dmitry Podgorny
7ede9c6d03 tls/openssl: suppress error in special case in tls_stop()
When peer closes connection instead of proper shutdown SSL_shutdown()
fails in bidirectional mode. Handle this case and suppress the error.
2018-02-18 12:03:11 +02:00
Dmitry Podgorny
20c6f225b9 ChangeLog: add missed changes 2018-02-18 10:42:21 +02:00
Scott M Anderson
5b204b0938 fix strtok_r test on MSW 2018-02-18 09:55:33 +02:00
Dmitry Podgorny
6e6093ee7b stanza: fix xmpp_error_new() in #115
* Don't set text to a tag stanza. It returns XMPP_INVOP.
 * Don't use stanza after releasing own reference.
2017-09-12 17:45:11 +03:00
Dmitry Podgorny
f12d43989f stanza: removed tabs and trailing spaces 2017-09-12 17:36:28 +03:00
Ilya Orlov
032e8ec89d Added new stanza for error responce, libstrophe now will be responce <stream:error> if parser failed 2017-09-04 11:46:09 +03:00
Dmitry Podgorny
d0644c5e95 tls/openssl: print errno on unrecoverable error 2017-08-23 09:23:51 +03:00
Dmitry Podgorny
b1b827cde6 conn: clear features support on connection reset
Features may be changed when client reconnects.
2017-07-15 21:32:55 +03:00
Dmitry Podgorny
e73b577e0b auth: use xmpp_iq_new() for iq stanzas 2017-07-15 20:35:16 +03:00
Dmitry Podgorny
8d2d59e914 tls/openssl: add OpenSSL-1.1.0 support (#109)
OpenSSL-1.1.0 marks cleanup functions as deprecated and changes
initialization function. It implements implicit de-initialization.

Reported by @zygmund2000.
2017-07-12 02:07:14 +03:00
Dmitry Podgorny
212f3ea760 snprintf: fix compilation with GCC 7 (#108)
GCC 7 enables -Wimplicit-fallthrough=3 with -Wextra. Add comments to
suppress these warning explicitly.

Reported by @akoww
2017-07-11 03:21:31 +03:00
Dmitry Podgorny
4aa7ea7e5b parser/expat: update #47
@curse666 suggested solution for #46 which reduces memory usage in expat
module. Current patch fixes few issues:
  - Inner text is lost for depth 1 stanzas
  - Incorrect handling of mixed xml <x>text<y/></x>
  - Reduced number of realloc() calls
2017-07-06 22:32:20 +03:00
curse666
5f9c0399c3 Update parser_expat.c
solution for #46.
2017-07-06 19:25:20 +03:00
Dmitry Podgorny
b83347b464 examples: use xmpp_iq_new() 2017-07-06 15:47:19 +03:00
Dmitry Podgorny
d8a86a54d5 event: set socket to wfds when sending queue is not empty
Don't sleep in select(2) when system is ready to send data.
2017-07-05 20:36:34 +03:00
Dmitry Podgorny
49289a9c05 event: reduce copy-paste 2017-07-05 20:34:38 +03:00
Dmitry Podgorny
456083d878 stanza: fix memory leaks
Free attribute "to" before replacing it in xmpp_stanza_reply().
Free value on insertion error in xmpp_stanza_set_attribute().
Don't copy attributes manually. Use xmpp_stanza_set_attribute() instead.
2017-07-05 20:20:45 +03:00
Dmitry Podgorny
15e7032f0d examples/bot: handle quit message 2017-07-05 19:40:25 +03:00
Dmitry Podgorny
cd2cf6eb23 ctx: set default event loop timeout to 1 second
Default timeout was 1 millisecond and it consumed CPU by running the
loop in xmpp_run(). The CPU usage was unacceptable and thus xmpp_run()
was not used by users. Also user can set the timeout by
xmpp_ctx_set_timeout() now.

Event loop already handles the situation when there is a timed handler
which needs to be run before the timeout is reached. Also system awakes
event loop when data arrives to socket.

The only drawback is when timed handler sends data it will be actually
sent after select(2) returns. But this situation can be handled easily by
adding socket to wfds when xmpp_conn_t contains data in sending queue.
2017-07-05 15:47:13 +03:00
Dmitry Podgorny
666995531a ChangeLog: mention recent changes
- Certificate verification is enabled for all openssl versions by
   default
 - xmpp_ctx_set_timeout()
 - Move xmpp_ctx_set_timeout() to event loop section in strophe.h
2017-07-04 19:22:26 +03:00
Dmitry Podgorny
9269d6b0d5 conn: add flag XMPP_CONN_FLAG_TRUST_TLS
TLS modules accept invalid server's certificates when the flag is set.
2017-07-04 17:20:33 +03:00
Dmitry Podgorny
c319ee6583 handler: fix memory leak and use-after-free errors
The errors are related to newly added handler_system_delete_all().
2017-07-01 16:24:30 +03:00
Dmitry Podgorny
cc53012cfa tls/openssl: enable cert verification for openssl older then 1.0.2
TODO: add flag to trust certificate even if verification fails.
2017-07-01 15:51:49 +03:00
Dmitry Podgorny
cc297eff45 handler: shut compiler up
Fixes the next warning:
    src/handler.c:683:25: error: ‘key2’ may be used uninitialized in
    this function [-Werror=uninitialized]

In fact key2 initialised and used under the same condition.
2017-06-29 20:43:03 +03:00
Dmitry Podgorny
e151096834 handler: don't call time_stamp() twice in handler_fire_timed() 2017-06-29 20:22:55 +03:00
Dmitry Podgorny
82040403bf tests/hash: enable the test 2017-06-29 20:22:55 +03:00
Dmitry Podgorny
a845c7ec3d hash: avoid allocation when replace value in hash_add()
Memory allocation may fail. We don't need to allocate new entry when we
already have one.
2017-06-29 20:22:55 +03:00
Dmitry Podgorny
5ecb0c5d0c conn: clear system handlers on reconnect
xmpp_conn_t object can be reused with saving user's handlers.
However, saving system handlers can lead to a fail during connection
process. This is because old object may contain timed handlers for
missed features or other handlers that would handle incoming stanzas
incorrectly.
2017-06-29 20:22:55 +03:00
Dmitry Podgorny
da8dc2d3cb auth: don't delete stream:error handler
Instead, keep the same handler until connection is disconnected. Stream
is opened 3 times during usual workflow. Deleting/restoring the handler
every time brings more places for possible mistakes.
2017-06-29 20:13:16 +03:00
Dmitry Podgorny
182695d1ca resolver: fix type mismatch in windows part
Backported from NetXMS project.
2017-06-29 20:00:25 +03:00
Dmitry Podgorny
0d039f7192 parser/libxml2: fix missed {} for multi-line block 2017-06-29 19:33:26 +03:00
Dmitry Podgorny
ebe97e8f2a travis-ci: don't use custom CFLAGS
Makefile.am provides -Wall and -Werror now.
2017-06-29 19:00:54 +03:00
Dmitry Podgorny
9505cace5e ctx: don't check whether log level is less than XMPP_LEVEL_DEBUG
XMPP_LEVEL_DEBUG is always 0.
2017-06-29 18:49:54 +03:00
Dmitry Podgorny
8ef027c54a ctx: fix indentation in xmpp_ctx_set_timeout() 2017-06-29 18:49:54 +03:00
Dmitry Podgorny
0bdb5f1d5b examples/vcard: fix warn_unused_result related to fwrite() 2017-06-29 18:49:54 +03:00
Steffen Jaeckel
49e7572059 fix function-pointer storage
the C standard says that you're not allowed to cast a function pointer to
something else

 6.3.2.3 §8:

A pointer to a function of one type may be converted to a pointer
to a function of another type and back again
2017-06-29 16:58:11 +02:00
Steffen Jaeckel
c074a6166c enable some warnings & fix 'em 2017-06-29 16:58:11 +02:00
Steffen Jaeckel
4c504ee42b add xmpp_ctx_set_timeout() 2017-06-29 16:58:11 +02:00
Steffen Jaeckel
f226891520 fix openssl memory leaks 2017-06-29 16:54:41 +02:00
Dmitry Podgorny
43a68479ad ChangeLog: mention about disabled insecure SSL/TLS
See f47609c1f0
2017-06-26 01:09:24 +03:00
Dmitry Podgorny
1e422a70dd crypto: add public function xmpp_sha1_digest()
For example, XEP-0115 requires binary digest to be base64-encoded.
Add short function to simplify such a digest computation for a single
buffer. This function doesn't allocate memory. Therefore, it can't
fail and doesn't require strophe context (xmpp_ctx_t).
2017-06-26 00:56:03 +03:00
Dmitry Podgorny
2a474bd809 tls/schannel: fix memory leak
Assume InitializeSecurityContext() doesn't change pszTargetName.
Reported by @youzpp in metajack/libstrophe#35.
2017-06-25 22:12:16 +03:00
Dmitry Podgorny
704c157d24 hash: don't use signed types during index calculation
Reported by @ITikhonov in pasis/libcouplet#10
2017-06-25 21:59:27 +03:00
Dmitry Podgorny
d8528b597c parser/libxml2: shorten parser_feed() 2017-06-25 21:19:15 +03:00
Dmitry Podgorny
c149caa32a examples: add vcard example
New exmaple shows how to request vCard and handle result. See XEP-0054
for details.
PHOTO element is stored to an image file.
2017-06-23 22:09:28 +03:00
Dmitry Podgorny
fa63aac731 auth: delete error handler before re-opening stream 2017-06-23 21:51:44 +03:00
Dmitry Podgorny
ba40bf1040 handler: fix possible corruption of handler lists
User's handler may delete other handlers. If deleted handler is the next
or the previous to the running one it leads to a list corruption.
Don't cache previous/next items when we walk through a handler's list.
2017-06-23 21:47:10 +03:00
Alexander Krotov
2f9bcfe145 Test xmpp_jid_bare 2017-06-23 20:00:41 +03:00
Alexander Krotov
b957213fdc Simplify xmpp_jid_{bare,domain,resource} 2017-06-23 19:55:12 +03:00
Alexander Krotov
f51b01a5b3 Remove unused mutex wrappers 2017-06-23 17:58:42 +03:00
Dmitry Podgorny
d2efc85ce3 Makefile.am: refactoring
Put filenames to separated lines to simplify adding/removing new files.
Add missed files to EXTRA_DIST.
2017-06-23 17:00:26 +03:00
Dmitry Podgorny
940d76e9e2 rpm: update dependencies 2017-06-23 16:57:27 +03:00
Dmitry Podgorny
59eb8d67ed ChangeLog: mention significant changes since 0.9.1 2017-06-23 16:55:31 +03:00
Dmitry Podgorny
78592650e5 tls/schannel: reflect changes of the tls interface
tls_new() accepts xmpp_conn_t object now.
2017-06-23 15:37:09 +03:00
Alexander Krotov
92d006a41b Disable hostname verification for pre-1.0.2 OpenSSL 2017-06-23 13:45:24 +03:00
Alexander Krotov
0741820711 Verify certificate hostname when using OpenSSL (fixes #100) 2017-06-23 03:00:26 +03:00
Alexander Krotov
c9ddc2b7ef Make tls_new accept xmpp_conn_t 2017-06-23 02:59:05 +03:00
Alexander Krotov
f776b34d8c Fix tls_openssl.c indentation 2017-06-22 19:44:41 +03:00
Alexander Krotov
f47609c1f0 Disable insecure SSL/TLS versions 2017-06-20 22:44:39 +03:00
Alexander Krotov
443b4d3116 Use more variables in bot.c message_handler()
to avoid calling the same function twice.
2017-06-20 01:38:53 +03:00
Steffen Jaeckel
4cf7a0cb79 split travis build into multiple jobs 2017-04-04 18:25:27 +02:00
Dmitry Podgorny
9a5fe8b96d handler: allow to add same handler callback with different userdata
According to #97. A single callback can distinguish different cases
using the userdata. Current implementation doesn't allow to delete a
particular couple (handler, userdata), but rather deletes all handlers
regardless of the userdata.
2017-03-24 22:38:16 +02:00
Dmitry Podgorny
80a7ea0b7c Fix spacing in handler.c 2016-12-02 16:56:45 +02:00
Dmitry Podgorny
1092447edf Fix spacing in c++ files 2016-09-27 15:40:08 +03:00
Dmitry Podgorny
d2c1daf4d5 Fix spacing in ctx.c and common.h 2016-09-27 15:14:38 +03:00
Dariusz Dwornikowski
d1a41c9d7d Fixed occurred spelling error fixes: #96 2016-09-23 08:31:31 +02:00
Dmitry Podgorny
9931ad4fa2 Updated ChangeLog
- Fixed bug #95 (DNS lookup failing on Cygwin)
- Removed dependency on the check package
2016-09-16 01:10:25 +03:00
Dmitry Podgorny
5a1f282292 README: remove mention of the check dependency 2016-09-16 01:07:21 +03:00
Dmitry Podgorny
b360316b8b tests/resolver: added test case with pointers 2016-09-16 01:01:43 +03:00
Dmitry Podgorny
c6aaa96dcb resolver: fixed few bugs
Fixed a bug which caused resolver to return a wrong target name with a
missed ".". The bug was in the part of message_name_get() where a pointer
is handled.
Added checks for buffer overflow.
Added 2 test cases to check a corner case and error handling.
2016-09-15 19:26:10 +03:00
Dmitry Podgorny
f0706f56c4 tests: remove dependency on check package
Only check_parser depends on the check. Convert it. All tests will be
rewritten at once when new testsuite is implemented.
2016-09-15 12:46:11 +03:00
James Booth
4ef2740c36 Update version to 0.9.1 2016-09-14 19:53:25 +01:00
Dmitry Podgorny
5e0330f0ad libstrophe 0.9.0
See ChangeLog for significant changes or follow the link to see all the
changes:
  https://github.com/strophe/libstrophe/compare/0.8.8...strophe:0.9.0
2016-09-14 18:55:48 +03:00
Dmitry Podgorny
717a19859f doc: describe parameters to suppress doxygen warnings 2016-09-14 18:54:40 +03:00
Dmitry Podgorny
209f21905e Update Doxyfile
Updated Doxyfile with doxygen -u. Removed DOT_FONTNAME. Changed version
to 0.9.
2016-09-14 18:52:20 +03:00
Dmitry Podgorny
38aa05624d Add warning about disabled TLS in build-android.sh
OpenSSL or BoringSSL will be added to build-android.sh in the future.
But for now users need to build libstrophe with openssl manually if
they use ndk-build system.
2016-09-14 18:12:47 +03:00
Dmitry Podgorny
136055024e conn: update documentation for xmpp_connect_raw() 2016-09-14 17:49:10 +03:00
Michael Vetter
8d3f3d837a Remove trailing dots from changelog
Without the trailing dots it looks more coherent to me.
2016-09-14 14:21:18 +02:00
Dmitry Podgorny
151debf7ee stanza: remove inline keyword from _render_update()
Let compiler make decisions about static funcions. Also we don't need to
support workarounds for specific compilers.
2016-09-08 15:04:34 +03:00
Dmitry Podgorny
d8da993a73 Don't write outside a buffer boundary
When xmpp_vsnprintf() received count == 0 it still performs write to the
buffer. This leads to a write outside allocated memory, when
xmpp_stanza_to_text() parses a specific stanza. Because of error in
_render_update() writing exactly buflen+1 with xmpp_snprintf() is counted
as valid operation and next xmpp_snprintf will be performed to a buffer
with zero length. This leads to wrong rendering of 1024-length stanzas
and invalid write for specific larger stanzas:

==22436== Invalid write of size 1
==22436==    at 0x4E4B3A1: xmpp_vsnprintf (snprintf.c:709)
==22436==    by 0x4E4B479: xmpp_snprintf (snprintf.c:722)
==22436==    by 0x4E4C011: _render_stanza_recursive (stanza.c:328)
==22436==    by 0x4E4C3B3: _render_stanza_recursive (stanza.c:384)
==22436==    by 0x4E4C3B3: _render_stanza_recursive (stanza.c:384)
==22436==    by 0x4E4C3B3: _render_stanza_recursive (stanza.c:384)
==22436==    by 0x4E4C3B3: _render_stanza_recursive (stanza.c:384)
==22436==    by 0x4E4C3B3: _render_stanza_recursive (stanza.c:384)
==22436==    by 0x4E4C4E5: xmpp_stanza_to_text (stanza.c:435)
==22436==    by 0x4E407A8: _handle_stream_stanza (conn.c:1157)
==22436==    by 0x4E4EF2B: _end_element (parser_expat.c:157)
==22436==    by 0x571005A: doContent (in /usr/lib64/libexpat.so.1.6.2)
==22436==  Address 0x63b95b0 is 0 bytes after a block of size 1,024 alloc'd
==22436==    at 0x4C2BFE0: malloc (vg_replace_malloc.c:299)
==22436==    by 0x4E41947: _malloc (ctx.c:116)
==22436==    by 0x4E41A54: xmpp_alloc (ctx.c:204)
==22436==    by 0x4E4C4A3: xmpp_stanza_to_text (stanza.c:428)
==22436==    by 0x4E407A8: _handle_stream_stanza (conn.c:1157)
==22436==    by 0x4E4EF2B: _end_element (parser_expat.c:157)
==22436==    by 0x571005A: doContent (in /usr/lib64/libexpat.so.1.6.2)
==22436==    by 0x57109FB: contentProcessor (in ...
==22436==    by 0x5712A9F: XML_ParseBuffer (in ...
==22436==    by 0x4E4F1E7: parser_feed (parser_expat.c:247)
==22436==    by 0x4E42BBC: xmpp_run_once (event.c:284)
==22436==    by 0x4E42D62: xmpp_run (event.c:336)

Fix xmpp_vsnprintf() and _render_update() behaviour.
2016-09-08 14:52:36 +03:00
Dmitry Podgorny
72173087a9 conn: move _disconnect_cleanup() to other static functions
Also remove trailing spaces.
2016-09-06 22:17:24 +03:00
Dmitry Podgorny
229215b49c conn: xmpp_conn_open_stream() sends user-supplied tag
Default opening tag doesn't allow to implement XEP-0174. In this case
client needs to send specific attributes. As solution user passes
attributes that are sent inside opening tag. Previous variant of this
function is renamed to xmpp_conn_open_stream_default().

The way to pass attributes is not the best one, but it's used to conform
existent interface of xmpp_stanza_get_attributes().

Remove "raw" suffix from functions that work with a raw connection.
2016-09-06 22:06:29 +03:00
Fredrik Fornwall
dd7aeff5d2 Avoid trying to link against libresolv for Android
Android does not have a separate libresolv library.
2016-09-06 01:24:26 +02:00
Dmitry Podgorny
3e95806d2e conn: suppress gcc warning
gcc with -O3 flag reports that 'host' and 'port' can be uninitialised.
But this is not true. Suppress the message.
2016-09-05 11:51:24 +03:00
Tim Nieradzik
7ad42add73 test_string: Fix failing strdup test
`rc` may be initialised with -1.
2016-09-05 09:11:55 +03:00
Garick Hamlin
936ddb0d15 Compute correct send_queue_len.
Previously this was incremented, but not decremented.  I've corrected
the code to decrement it.  It doesn't appear to be used anywhere, but
it is helpful to implement xmpp_graceful_stop().
2016-09-04 03:37:56 +03:00
Dmitry Podgorny
233094af10 tests/parser: include string.h explicitly 2016-09-04 03:35:28 +03:00
Dmitry Podgorny
94e84f0643 README: add travis status icon 2016-09-04 02:17:03 +03:00
Dmitry Podgorny
8662611a34 travis-ci: remove make install 2016-09-04 02:16:19 +03:00
Dmitry Podgorny
60e6c191e9 README: mention about pkg-config dependency 2016-09-04 02:06:26 +03:00
Dmitry Podgorny
211170822f Integrate with Travis-CI 2016-09-04 02:05:00 +03:00
Dmitry Podgorny
c80f99bc68 tests: add tests for string functions 2016-09-04 00:53:24 +03:00
Dmitry Podgorny
53e44aa0e3 util: added strtok_r implementation for old compilers
Visual studios older than 2005 don't have strtok_s() and according to
MSDN vs2005 has NOT thread-safe strtok().
2016-09-04 00:34:26 +03:00
Dmitry Podgorny
2b249130c9 ChangeLog: mention that xmpp_conn_t is reusable 2016-09-03 17:10:26 +03:00
Dmitry Podgorny
84fa4466ee conn: clear authenticated flag on reset 2016-09-03 17:09:21 +03:00
Dmitry Podgorny
beaaab5742 tls_schannel: don't force RC4 for TLS handshake
Leave default configuration. A particular server closes tcp connection
during negotiation otherwise.

tls_schannel is still broken and tls_read() fails with
SEC_E_UNSUPPORTED_FUNCTION error.
2016-09-03 16:38:43 +03:00
Dmitry Podgorny
f351d62355 tests/jid: fixed crash on QNX
QNX version of printf() doesn't check whether a string is valid or not.
Passing NULL pointer leads to a crash inside strlen(). Fix this with a
wrapper _s() which returns "<NULL>" for instead of NULL pointer.
Add checks before strcmp() to avoid crash in case of unexpected result
of the jid functions.
2016-09-02 19:57:25 +03:00
Dmitry Podgorny
b6fcb7035d rand: replace time() with time_stamp() as entropy source
time_stamp() has bigger precision.
2016-09-02 17:38:42 +03:00
Dmitry Podgorny
c0a224cf84 Remove const in parser_free() declaration
To match the definitions.
2016-09-02 17:10:51 +03:00
Dmitry Podgorny
384ef8023e auth: coding style 2016-09-02 17:10:14 +03:00
Dmitry Podgorny
f44232762e tls/schannel: remove unused variable 2016-09-02 17:09:47 +03:00
Dmitry Podgorny
111659cc91 ctx: add workaround for ms compilers without va_copy support 2016-09-02 15:22:31 +03:00
Dmitry Podgorny
4ae4b8c94b event: suppress warning about type overflow 2016-09-02 15:22:14 +03:00
Dmitry Podgorny
2b2de142ef tests/resolver: fix the test and add new testcase
It was broken because of changed API.
2016-09-02 12:45:28 +03:00
Dmitry Podgorny
a45af3a929 resolver: srv_rr_list can be uninitialised
Resolver assumes that srv_rr_list is empty at the beginning. Make it so
explicitly and let user pass uninitialised list as argument.
2016-09-02 12:39:10 +03:00
Dmitry Podgorny
3e77afb27e Fix type mismatch
Backported from NetXMS project.
2016-09-02 11:59:54 +03:00
Dmitry Podgorny
0c60e8d384 tls_openssl: coding style 2016-09-02 00:49:32 +03:00
Dmitry Podgorny
2bff3f8722 conn: unify error codes for API functions
Return error code from strophe.h instead of -1.
2016-09-02 00:49:32 +03:00
Dmitry Podgorny
dc56737b71 conn: implemented xmpp_connect_raw()
This function is similar to xmpp_connect_client(), but doesn't perform
authentication. Instead, it calls user's connection handler immediately
after establishing the connection. Hence, user can implement own
authendication or registration procedures.

Such a "raw" connection can be useful when user interaction is required
(e.g. Data Forms in XEP-0077, OAuth2).
2016-09-02 00:47:58 +03:00
Dmitry Podgorny
3869906357 Fixed spacing in event.c 2016-08-30 23:35:17 +03:00
Dmitry Podgorny
75b19be572 unify _sleep() macro in event.c 2016-08-30 19:43:05 +03:00
Dmitry Podgorny
b0a1842a53 examples/bot: fix memory leaks and use stanza helpers 2016-08-30 15:32:10 +03:00
Dmitry Podgorny
14066d0bff resolver: fix commit 171b285 2016-08-29 20:09:45 +03:00
Chih-Wei Chen
171b285cbe sock : Add DNS-SRV list sorted by priority and weight, check result connection. 2016-08-29 11:32:20 +03:00
Dmitry Podgorny
f46cf669a3 Android: added make files 2016-08-22 15:52:31 +03:00
James Booth
09e5103eee Update ChangeLog 2016-08-22 13:10:23 +01:00
Dmitry Podgorny
1a657c67e2 Fixed typo 2016-05-21 01:28:39 +03:00
Dmitry Podgorny
3e86987f5f sock: fixed build on cygwin
Cygwin doesn't have TCP_KEEPIDLE nor TCP_KEEPALIVE. Therefore, don't
change any tcp keepalive parameter on this system.
2016-05-11 11:30:21 +03:00
Dmitry Podgorny
e83d0d6b62 SCRAM-SHA-1: fixed base64-decoding of salt
Salt can contain 0x00, therefore must be decoded with
xmpp_base64_decode_bin().
2016-05-08 09:01:20 +03:00
Dmitry Podgorny
2ffb278aa4 Implemented public Base64 interface
Base64 is required for vCard avatars. It will also be useful for
an authentication mechanism with future xmpp_connect_raw() interface.
2016-04-30 18:09:01 +00:00
Dmitry Podgorny
7f57e3987a tests/resolver: don't link against -lstrophe 2016-04-30 16:40:10 +00:00
Dmitry Podgorny
c62d1d64ab tests/ctx: enable the test 2016-04-30 16:37:18 +00:00
Dmitry Podgorny
c9a5cba113 tests/jid: enable the test 2016-04-30 16:15:45 +00:00
Dmitry Podgorny
e4a4bdd3a3 Implemented public SHA1 interface
A number of XEPs require SHA1. Publishing its interface helps user to
reduce number of dependencies.
2016-04-29 22:51:23 +00:00
Dmitry Podgorny
565ace7982 tests/snprintf: undef HAVE_SNPRINTF and HAVE_VSNPRINTF 2016-04-29 22:51:23 +00:00
Dmitry Podgorny
71d90d60a5 Fixed comments inside strophe.h
Don't use doxygen comments in strophe.h since functions are documented
in *.c files.
Remove mutex from xmpp_log_t because libstrophe is single-thread
library.
2016-04-29 22:51:23 +00:00
Dmitry Podgorny
b5d9b33b6f Refactor rand
* Removed dependency from internal headers in rand.h. xmpp_rand
  interface can be public in the future;
* xmpp_rand functions accept xmpp_rand_t object instead of xmpp_ctx_t.
2016-04-29 22:51:23 +00:00
Dmitry Podgorny
063f4deb22 Refactor autotools scripts
* Add --disable-tls
* Check for libxml2 if expat is not found
* Single coding style
2016-04-28 22:37:27 +00:00
Dmitry Podgorny
192ab59608 resolver: fixed uninitialised var 2016-04-28 20:40:16 +00:00
Dmitry Podgorny
a3da77b8f6 stanza: return const char* for non allocated strings
If interface function returns char* the result must be freed with
xmpp_free().

In case of const char* the result must not be changed by user. Also, the
result is valid only during stanza lifetime.
2016-04-27 11:56:50 +00:00
Dmitry Podgorny
c0aad0bf00 README: describe autotools dependency 2016-04-27 11:54:04 +00:00
Dmitry Podgorny
a2834f8f92 conn: fixed doxygen comments 2016-04-26 20:27:25 +00:00
Dmitry Podgorny
2ef432f632 conn: xmpp_conn_set_keepalive() stores parameters in conn
Store keepalive parameters in conn object. This allows call the function
with a disconnected conn. Also, the parameters are not cleared on
reconnect and applied once a socket is created.
2016-04-26 19:48:34 +00:00
Dmitry Podgorny
a50d706bcd stanza: added helpers for particular stanzas
These are wrappers for message, iq and presence stanzas.
2016-04-26 19:19:51 +00:00
Dmitry Podgorny
ca7af9c837 Fixed indentation and typos 2016-04-26 19:19:51 +00:00
Dmitry Podgorny
a65679dd72 conn: removed unused fields from xmpp_conn_t 2016-04-26 13:03:16 +00:00
Dmitry Podgorny
211748e65a Replace bit mask hex values with shifts 2016-04-26 13:01:22 +00:00
Dmitry Podgorny
4c0c12343f sha1: moved SHA1HANDSOFF definition 2016-04-22 21:30:17 +00:00
Dmitry Podgorny
96873984b2 conn: handle stream tag properly
If `id == NULL' there were two issues: use-after-free error or
logging wrong error description.
Also open stream handler was called even when error occured and
conn object was disconnected.
2016-04-22 21:04:29 +00:00
Dmitry Podgorny
b06599963b conn: make xmpp_conn_t object reusable
Disconnected xmpp_conn_t object can be connected again. The object
is reset before connection, but the next fields remain untoucned:
  * all handlers
  * flags
  * jid and password

Main use-case of this change is reconnect when an error occurs.
2016-04-22 20:52:36 +00:00
Dmitry Podgorny
ab80d72518 tls/openssl: handle SSL_shutdown() properly
Handle SSL_ERROR_WANT_READ/WRITE and return value 0.
2016-04-19 19:57:52 +00:00
Dmitry Podgorny
ba7422c893 tls/openssl: be more verbose
Log non-recoverable errors. The openssl error queue can contain useful
information.
2016-04-19 18:21:27 +00:00
Dmitry Podgorny
4b444ea699 tls/openssl: fixed indentation 2016-04-19 16:33:47 +00:00
Dmitry Podgorny
96dc61c772 sock: use sock_close() instead of close() 2016-04-16 20:19:16 +00:00
Andrey (Stanson) Kozin
f5c2c81142 Added conn_send_queue_purge() function.
Function frees all data and clean send queue in
    xmpp_conn_release()
    xmpp_connect_client()
    xmpp_connect_component()
2016-04-15 16:08:24 +00:00
Dmitry Podgorny
838ed493a3 updated README.markdown 2016-04-15 15:56:22 +00:00
Dmitry Podgorny
9d7b3394e7 Makefile.am: updated EXTRA_DIST
'make dist' prepares complete tarballs now.
2016-04-15 15:51:12 +00:00
Dmitry Podgorny
9e4e274c4d updated examples/README.md 2016-04-14 15:23:38 +00:00
Tusharsb
e3b9fa1e53 tips for compiling example code
Me and a few more users (at
https://groups.google.com/forum/#!topic/strophe/sPw7-Vpszxs) were struggling
to compile example files. I think this readme file will help others as well.
2016-04-14 14:20:38 +00:00
Dmitry Podgorny
85bf3f4241 conn: don't set flag in xmpp_conn_disable_tls() directly
Instead, use xmpp_conn_set_flags() interface to force all necessary
checks.
2016-04-13 12:43:03 +00:00
Dmitry Podgorny
6d70bf4b0d tests/sha1: fixed indentation 2016-04-13 12:40:42 +00:00
Dmitry Podgorny
3aafeef215 res_query_dump: added comment with printable symbols 2016-04-13 12:38:53 +00:00
Dmitry Podgorny
e60895087e event: let event loop start after xmpp_stop() 2016-04-13 10:25:15 +00:00
Dmitry Podgorny
b169c0a91e conn: refactore connect functions
Common code is moved to a separated function.
2016-04-12 20:19:16 +00:00
Dmitry Podgorny
968cb7dcdf conn: fixed indentation 2016-04-12 20:07:58 +00:00
Dmitry Podgorny
367aeb5844 conn: don't set tcp keepalives for disconnected connection
In XMPP_CONN_DISCONNECTED state conn->sock is undefined.
2016-04-08 18:17:07 +00:00
Dmitry Podgorny
5518a9e10a sock: refactore tcp keepalive setting
- Leave default number of keepalive probes rather than hardcoded value
- QNX and OSX use TCP_KEEPALIVE instead of TCP_KEEPIDLE
- QNX doesn't have TCP_KEEPINTVL
2016-04-08 18:12:48 +00:00
Andrey (Stanson) Kozin
6c1c2a86f8 TCP keepalive setting support 2016-04-08 16:40:56 +00:00
Chih-Wei Chen
cb79f5c10b Revert "Added expat submodule for tracking vendor releases."
This reverts commit 24abd94a82.

Signed-off-by: Chih-Wei Chen <changeway@gmail.com>
2016-03-16 16:38:09 +08:00
Dmitry Podgorny
b7e308d057 Implemented resolver to replace sock_srv_lookup() 2016-02-20 00:15:08 +02:00
Andrey (Stanson) Kozin
714b2f9c03 Free send queue on connection release 2016-02-06 00:10:21 +00:00
Dmitry Podgorny
a7c18ba6c7 autotools: choose correct library for res_query()
See #72 for further information.
2016-02-05 23:37:55 +00:00
Dmitry Podgorny
fc82aaeef2 git: added vim's swp files to .gitignore 2016-02-05 22:46:36 +00:00
Dmitry Podgorny
c291513663 Makefile: remove unused src/thread.c
See #72 for further information.
2016-02-05 21:58:00 +00:00
Rafael Sadowski
a413c68e52 fix: generated syntax errors on BSD's SH(1) 2016-02-05 21:43:34 +00:00
Dmitry Podgorny
5b41247bac rand: remove sizeof(uint8_t) because it's always 1 2016-01-10 13:05:30 +02:00
vf
fc563272fe Fix for MSVC 2016-01-09 17:57:32 +03:00
Dmitry Podgorny
38545af533 parser/expat: reduce code duplication 2015-12-21 13:46:51 +02:00
James Booth
6f612d4f4d Add -no-undefined to LDFLAGS 2015-12-14 00:28:37 +00:00
Dmitry Podgorny
a1bc5e82cf sha1: don't make in-place operations
SHA1HANDSOFF makes SHA1_Transform() use an internal buffer.
2015-12-06 11:11:52 +02:00
Dmitry Podgorny
98c8afd5fc tests: updated SHA1 test
* added new testcase with 128 'a'
* added check that SHA1 functions don't change original strings
2015-12-04 09:57:09 +02:00
Dmitry Podgorny
cff4a4cd71 Merge pull request #76 from Tusharsb/patch-2
Making bot.c compatible to C++
2015-12-02 11:41:58 +02:00
Tusharsb
7bbfd00344 Making bot.c compatible to C++
By this small type casting change, I am able to compile bot.c with both ``gcc`` as well as with ``g++``

I am now able to use bot.c in my c++ programs
2015-12-02 14:53:52 +05:30
Dmitry Podgorny
01af2deed9 conn: fixed memory leak
Memory leak appeared when xmpp_connect_client() was called for a
connection more than once. In this case conn->domain wasn't freed.
2015-10-23 01:28:19 +03:00
Dmitry Podgorny
a54def3e92 examples/bot: fixed memory leak 2015-10-23 00:40:13 +03:00
Dmitry Podgorny
2aea1a627c stanza: avoid copy-paste in 'get' functions 2015-10-21 02:13:55 +03:00
Dmitry Podgorny
e475ca13ea stanza: handle errors better
* Free memory on error in xmpp_stanza_set_attribute()
* Handle memory allocation error in xmpp_stanza_set_text()
2015-10-21 02:11:03 +03:00
Dmitry Podgorny
31582cac92 ChangeLog: added new stanza API 2015-10-20 21:06:35 +03:00
Dmitry Podgorny
c012f20c99 stanza: handle EMEM in xmpp_stanza_set_name() 2015-10-20 16:26:43 +03:00
Tristan Le Guern
d460a5eb16 Convert bot.c to the new helpers 2015-10-20 16:26:43 +03:00
Tristan Le Guern
3a56d68f47 Convert active.c to the new helpers 2015-10-20 16:26:43 +03:00
Tristan Le Guern
9de0293405 Implement xmpp_stanza_reply()
This function creates a copy of the given stanza minus its children and
set the “to” attribute with the value of the original “from”.
2015-10-20 16:26:43 +03:00
Tristan Le Guern
21f742f888 Implement xmpp_stanza_del_attribute() 2015-10-20 16:26:43 +03:00
Tristan Le Guern
0d4bcc0228 Extract the copy loop into its own function
It will help reduce code duplication for xmpp_stanza_copy
2015-10-20 16:26:43 +03:00
Tristan Le Guern
90a595f8ef Implements helpers for to and from attributes
xmpp_stanza_{get,set}_{from,to}
2015-10-20 15:22:20 +03:00
Dmitry Podgorny
64d65b3aed examples: mention about xmpp_conn_set_flags()
xmpp_conn_set_flags() provides features that people sometimes look for.
Adding comment to examples will simplify the search.
2015-10-20 14:36:43 +03:00
Dmitry Podgorny
a39267c1b0 conn: use altport for legacy ssl if specified 2015-10-19 22:42:44 +03:00
Dmitry Podgorny
331db6f565 tests: added test vectors for MD5 2015-10-18 03:05:38 +03:00
Dmitry Podgorny
9cea7e4002 md5: cleanup
* Indentation
* Removed MD5DumpBytes()
* Removed ASM_MD5
2015-10-18 03:03:15 +03:00
Dmitry Podgorny
f63aba88d4 conn: fixed indentations 2015-10-18 02:22:15 +03:00
Dmitry Podgorny
a78201bae0 sock: use xmpp_snprintf instead of snprintf 2015-10-16 14:58:18 +03:00
Tristan Le Guern
a61dba0c13 Move declarations of xmpp_{v,}snprintf.
Use HAVE_SNPRINTF and HAVE_VSNPRINTF to conditionnaly use an internal
version of these functions or use the libc one.
2015-10-16 14:48:10 +03:00
Dmitry Podgorny
60cfcc72e6 snprintf: moved self-test to a separated test 2015-10-16 00:05:26 +03:00
Dmitry Podgorny
4cc9ba79c4 sock: choose target with the lowest priority
sock_srv_lookup chose the last target in the answer section. But it must
choose one with the lowest priority it can reach (due to RFC2052).
2015-10-15 17:49:44 +03:00
Dmitry Podgorny
182803926b conn: added new API xmpp_conn_set_flags()
xmpp_conn_set_flags() and xmpp_conn_get_flags() unify interface of
connection configuration.
This interface allows compile-time check of supported features or even
run-time check. Therefore, applications can be built with older
libstrophe which doesn't support some optional flags.
2015-10-15 16:01:13 +03:00
Dmitry Podgorny
e41b7a06e4 sha1: moved self test to separated test 2015-10-15 00:35:19 +03:00
Dmitry Podgorny
0994022066 scram: renamed BLOCK_SIZE
BLOCK_SIZE is too common name, so remove it to HMAC_BLOCK_SIZE.
2015-10-15 00:11:09 +03:00
Dmitry Podgorny
c156e13791 sock: removed unused static functions 2015-10-14 01:45:25 +03:00
Tristan Le Guern
a47b71e52b netbuf_* and friends should be static 2015-10-13 23:17:09 +02:00
Tristan Le Guern
db908563d0 xmpp_default_logger should be static 2015-10-13 23:16:44 +02:00
Dmitry Podgorny
b5e49a0600 base64: fixed crash when len is small 2015-10-13 23:20:41 +03:00
Dmitry Podgorny
e0a13ecdaa tests/base64: updated and added to Makefile.am 2015-10-13 23:19:25 +03:00
Dmitry Podgorny
fc856f36a3 tests/parser: fixed gcc warning
tests/check_parser.c:59:9: error: variable ‘ret’ set but not used
2015-10-13 22:39:27 +03:00
Dmitry Podgorny
9b67acbfc4 tests: moved common routines to test.[ch] 2015-10-13 22:38:18 +03:00
Dmitry Podgorny
b04c40d3ea tls: don't hang with openssl implementation
* Exit from tls_start() on fatal errors. If SSL_connect() fails and
  returns -1 this leads to endless loop in case of fatal error.
* Don't set writefds on SSL_ERROR_WANT_READ. Otherwise, this makes
  select(2) exit immediately what leads to CPU usage.
2015-10-13 02:43:23 +03:00
Tristan Le Guern
442914c375 Shorten the nlen+dlen+rlen part 2015-10-13 01:11:45 +03:00
Tristan Le Guern
a4558b69df Publicly expose JID manipulation functions 2015-10-13 01:11:25 +03:00
Dmitry Podgorny
07751974ac conn: fixed typo 2015-10-13 00:53:15 +03:00
Dmitry Podgorny
8ff46add8c conn: don't remember tls state from old connection
* Clear conn->secured and conn->tls_failed. These values could be
  saved after re-connect.
* conn is not secured if conn->tls is NULL. This condition handles
  disconnected connections.
2015-10-13 00:44:23 +03:00
Dmitry Podgorny
b7fbc81740 conn: added API xmpp_conn_is_secured()
This patch is provided by @kjkao. New API is already used in
WorksSystems/wks_xep0047.
2015-10-13 00:10:02 +03:00
Dmitry Podgorny
36af7afa27 conn: implemented old-style SSL connection
Added new API xmpp_conn_set_old_style_ssl(). This function forces
using of old-style SSL connection.
2015-10-12 22:25:39 +03:00
Dmitry Podgorny
254b9d73a1 sock: do nothing if srv lookup fails
Proper action is performed in xmpp_connect_client.
2015-10-12 22:06:27 +03:00
Dmitry Podgorny
8fcac79301 Makefile.am: removed whitespaces 2015-10-12 20:51:24 +03:00
Dmitry Podgorny
a48add8f13 ChangeLog: new features in 0.8.9 2015-10-12 16:12:34 +03:00
Dmitry Podgorny
8627fbb13f Implemented UUID generation (RFC4122)
This patch adds new API:
  * xmpp_uuid_gen()
2015-10-12 15:17:22 +03:00
Dmitry Podgorny
5e64c850bd Added stddef.h to strophe.h to define size_t 2015-10-12 15:02:59 +03:00
Dmitry Podgorny
533664903a tls: fixed GnuTLS implementation 2015-10-11 16:30:50 +03:00
Dmitry Podgorny
cda805163f conn: cleanup of headers
* added stdarg.h
* removed unused headers
2015-10-11 05:38:12 +03:00
Dmitry Podgorny
3d4c657467 Removed stdio.h from strophe.h 2015-10-11 05:25:19 +03:00
Dmitry Podgorny
8339fd0420 sock: socket should be non-blocking
Event loop handles asynchronous connections.
2015-10-11 05:20:31 +03:00
Dmitry Podgorny
63946ff7ef sock: set AI_ADDRCONFIG only if defined
AI_ADDRCONFIG is undefined on some systems.
2015-10-10 23:09:48 +03:00
Dmitry Podgorny
e6bbfa7a62 Updated license in new files 2015-10-10 01:11:08 +03:00
Chih-Wei Chen
b2a98fb9b6 sock : Add IPv6 support 2015-10-10 00:24:08 +03:00
Dmitry Podgorny
f326c2f42f sha1: added prefix crypto_ to SHA1 API
sha1.c contains symbols that overlap with libcrypto. This patch
fixes linking with libstrophe as static library.

Prefix crypto_ will help to group other crypto API in the future.
2015-10-09 12:14:23 +03:00
Dennis Schridde
3b55c20879 Fix --without-libxml2
The arguments of AC_ARG_WITH are (package, help-string, [action-if-given], [action-if-not-given]).
  action-if-given takes effect when the flag is given in **either** form (--with or --without).
  The proper code for action-if-given would thus be with_libxml2=$withval, which is already the default.
  Thus leaving this argument empty is the proper course of action.

cf. https://autotools.io/autoconf/arguments.html
2015-10-09 02:20:01 +03:00
christoph
a335466450 Fix invalid write in dopr(...)
Fix an invalid write in dopr(...) that occured when maxlen == 0 and
buffer != 0 since 0-1 > 0 using size_t.
2015-10-07 22:45:44 +03:00
Dmitry Podgorny
c73ddf065b autotools: set development version to 0.8.9 2015-10-07 22:27:26 +03:00
Dmitry Podgorny
fa585fd107 Removed outdated vs2008 directory 2015-10-07 22:20:22 +03:00
Dmitry Podgorny
7a782df9f5 auth: memory leak in _make_scram_sha1_init_msg() 2015-10-07 20:02:45 +03:00
Tristan Le Guern
982d840472 Signed to unsigned conversion to appease GCC 2015-10-07 19:48:35 +03:00
Tristan Le Guern
75938845fd Add missing prototypes to strophe.h 2015-10-07 19:46:55 +03:00
Dmitry Podgorny
e70e755e19 tests: added UT for SCRAM-SHA1 functions 2015-10-07 19:42:16 +03:00
Dmitry Podgorny
5e37de2b63 tests: added test vectors for Hash_DRBG 2015-10-07 19:35:42 +03:00
Dmitry Podgorny
10656ead46 Introduced PRNG based on Hash_DRBG (NIST SP 800-90A)
This pseudo-random number generator solves problem with
platform-independent generation of randomized nonces.
Current implementation uses weak entropy, especially when
kernel.randomize_va_space = 0. But it can be improved by
adding new sources to xmpp_rand_reseed().

New internal API introduced:
  xmpp_rand_new
  xmpp_rand_free
  xmpp_rand
  xmpp_rand_bytes
  xmpp_rand_nonce
2015-10-07 19:35:42 +03:00
Dmitry Podgorny
75b1a633d6 sha1: moved wrapper SHA1() to sha1.c 2015-10-07 19:35:42 +03:00
Tristan Le Guern
109aaa71a5 Fix behaviour of libstrophe on signed char arch
The use of 0xff as a separator for libexpat only works on arch where
char is defined as an unsigned char.  This is an unportable behaviour.

Before this patch this is what I experienced:

xmpp DEBUG sock_connect to im.bouledef.eu:5222 returned 3
xmpp DEBUG attempting to connect to im.bouledef.eu
xmpp DEBUG connection successful
conn DEBUG SENT: <?xml version="1.0"?><stream:stream to="bouledef.eu" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">
conn ERROR Server did not open valid stream.
xmpp DEBUG Closing socket.
DEBUG: disconnected
event DEBUG Stopping event loop.
xmpp DEBUG RECV: <http://etherx.jabber.org/streamsÿfeatures><urn:ietf:params:xml:ns:xmpp-tlsÿstarttls><urn:ietf:params:xml:ns:xmpp-tlsÿrequired/></urn:ietf:params:xml:ns:xmpp-tlsÿstarttls></http://etherx.jabber.org/streamsÿfeatures>
event DEBUG Event loop completed.
name = http://etherx.jabber.org/streamsÿstream
2015-10-07 15:01:14 +03:00
Dmitry Podgorny
92709a773c hash: fixed hash_new() prototype 2015-09-28 14:31:48 +03:00
Tristan Le Guern
93f9a5d1e8 Don't lie about array parameters 2015-09-28 14:26:25 +03:00
Tristan Le Guern
0acec5c335 -Wshadow for hash.c 2015-09-28 14:26:25 +03:00
Tristan Le Guern
0e8d407f93 Correct a type in scram.c 2015-09-28 14:26:25 +03:00
Tristan Le Guern
ed5f982312 Don't lie about array parameters
Passing an array as a parameter to a function in C doesn't work, they decay
into pointer.  Defining them is pointless and error prone, so just
declare a pointer.
2015-09-28 14:26:24 +03:00
James Booth
cd92f69c76 Fixed Changelog 2015-08-18 22:07:06 +01:00
James Booth
73080e0660 Fixed indentation in Changelog 2015-08-18 22:00:43 +01:00
James Booth
7cdef1e319 Check for name element when reading mechanisms
Fixes an issue where the server sends whitespace between
elements, a segfault would occur when performing a strcmp
on the name.
2015-08-03 23:05:04 +01:00
James Booth
527bc0573a Fixed _log_open_tag to remove namespace prefix with libexpat 2015-03-28 17:37:11 +00:00
James Booth
59d2dd55d2 Added codelite project to .gitignore 2015-03-26 20:58:05 +00:00
James Booth
b1cbcffdc7 Bumped version, added to Changelog 2015-03-26 20:52:48 +00:00
James Booth
154b70ed2c Merge remote-tracking branch 'cwtitan/namespace' 2015-03-26 20:44:10 +00:00
Codewalker
e33a98e498 Streamline xmpp_stanza_to_text for the namespace patch by following XML
namespace scoping guidelines and not outputting the xmlns attribute in
contexts where it can be inferred from the parent element.
2015-01-23 11:11:23 -06:00
shooding
239d365ea1 fix Makefile.am to respect openssl_CFLAGS, which is useful if openssl is not installed in the default path, e.g. cross-compiling. 2015-01-14 18:06:35 +08:00
Dmitry Podgorny
57ed30b2bc autotools: fixed build with old versions of automake
Automake-1.11 and some newer versions fail if m4/ doesn't exist. This
behaviour was changed to warning in version 1.13.2.
2015-01-03 01:01:07 +02:00
Codewalker
86f913b001 Fix use of snprintf (which does not exist on all platforms) in auth.c
by replacing it with xmpp_snprintf in order to be consistent with the
rest of the code. Also fixes compile error on MSVC.
2015-01-02 20:21:59 +02:00
Codewalker
e2f1c1e94a Add XML namespace support. 2014-12-28 22:46:16 -06:00
Codewalker
f62d817c71 Fix error spam in event loop when no connections are open.
On Win32 at least, select() will immediately fail with an error if
there are no file descriptors to wait for (max == 0). In the simple case
of a single XMPP session and an event loop that runs only while the
connection is open, this isn't a problem, but doesn't work for more
complex scenarios.

Add some handling so that if xmpp_run_once is called and there are no
active connections, simply sleep for duration of the timeout and
return.
2014-12-24 12:08:05 -06:00
Codewalker
b753e6cf18 Fix a bug where select() could fail due to negative max socket fd.
Connection objects in the disconnected state should not have their
fd taken into account when determining the maximum socket fd number,
as they are not set in the bitmask with FD_SET, and their fd could
be anything, even a wildly large or small number.
2014-12-24 12:04:39 -06:00
Dmitry Podgorny
0fed8a55e4 examples: updated header in component.c 2014-12-15 14:47:47 +02:00
Dmitry Podgorny
d28520ba30 added examples/component to .gitignore 2014-12-15 14:42:50 +02:00
Milan Kubik
c52ba85846 XEP-0114: Added an example for component connection.
The example is based on basic.c program.
To run the example, a configured server is needed.
2014-12-15 14:42:50 +02:00
Dmitry Podgorny
3b906f6ceb XEP-0114: minor fixes to auth.c
- src/sha1.c is used instead of openssl;
 - xmpp_alloc/free should be used instead of malloc/free;
 - coding style fixes.
2014-12-15 14:42:50 +02:00
Milan Kubík
5d76e052ec XEP-0114: external component authentication
New API xmpp_connect_component() introduced.
2014-12-15 14:41:18 +02:00
Dariusz Dwornikowski
37a40dec23 Merge pull request #42 from tdi/master
Update headers not to mention LICENSE.txt but licenses directly Closes #31
2014-11-13 13:13:37 +01:00
Dmitry Podgorny
2093aac817 rpm: include shared library and pkg-config file 2014-10-31 04:13:49 +02:00
Kristofer White
29fa1bf27e Updates to rpm/libstrophe.spec 2014-10-31 04:11:13 +02:00
Dmitry Podgorny
f364ed184b autotools: fixed building of check_parser
By default libtool choose dynamic library for linking. Since non-public
API is hidden in libstrophe.so check_parser has been broken. As solution
libtool takes static libstophe.a now.
2014-10-30 23:17:16 +02:00
Dmitry Podgorny
40c9fe22b3 .gitignore: added files generated by make check 2014-10-30 23:16:40 +02:00
Dmitry Podgorny
c33c0a6998 hash: removed useless condition in hash_iter_next
Situation ((entry != NULL) && (i >= table->length)) can never happen.
2014-10-29 18:43:06 +02:00
Sergey Anufrienko
92e674e412 fix bug leading to losing of hash entries in hash_iter_next 2014-10-28 16:42:28 +03:00
Dmitry Podgorny
9f82aac8ce sha1: compile-time endianness check for some compilers
Newer gcc and clang provide macro __BYTE_ORDER__.
2014-10-25 16:37:21 +03:00
Dmitry Podgorny
a3f1b83f3a sha1: run-time check for endianness
WORDS_BIGENDIAN is never defined what breaks sha1 on big-endian
architectures. Instead, make run-time check.
2014-10-25 16:03:18 +03:00
Dariusz Dwornikowski
71f75b2e2e Closes #31 2014-10-23 08:44:52 +02:00
Dmitry Podgorny
b23104f920 ChangeLog: set future version to 0.8.7 2014-10-21 10:23:12 +03:00
Dmitry Podgorny
057f9068c6 autotools: removed duplication in configure.ac 2014-10-21 10:21:55 +03:00
Fabian Freyer
3231214344 Added --with-pkgconfigdir 2014-10-08 18:13:32 +02:00
Fabian Freyer
55efc601cb Refactored configure.ac, added pkg-config support 2014-10-08 18:13:31 +02:00
Fabian Freyer
dcbf3fb4d5 autotools: check for ssl.h if pkg-config failed
Some distros provides libssl without openssl.pc.
2014-09-30 16:02:43 +03:00
Fabian Freyer
e2acd98612 autotools: added AC_CONFIG_MACRO_DIR 2014-09-30 16:00:36 +03:00
Dmitry Podgorny
ee4f6d4e3c ctx: fixed memory leak in xmpp_log()
Also fixed 2 bugs:
  - only 1022 bytes of the message is printed when length is 1023.
  - va_end() must be called after va_copy() otherwise we get
    undefined behaviour according to C99.
2014-09-10 01:03:34 +03:00
Dmitry Podgorny
db14f2bd4a auth: fixed memory leaks in _handle_scram_sha1_challenge() 2014-09-09 22:55:23 +03:00
Dmitry Podgorny
e768604533 autotools: fixed libstrophe's version
Repository contains tag 0.8.6, but version in configure.ac is still
0.8.5. So change it to the future release.
2014-09-09 20:54:01 +03:00
Dmitry Podgorny
1afcd485db conn: [style] fixed spacing 2014-09-09 20:53:30 +03:00
Vitaly Takmazov
58613ea7f3 fix MSVC build 2014-09-08 13:41:30 +03:00
Dmitry Podgorny
bf5bf96e95 sha1: removed xmpp_ prefix from SHA1 symbols
Prefix was introduced in a981c0a as a workaround. Now, non-public API is
not exported, so this patch hides SHA1 symbols for dynamic library.
2014-09-07 00:02:14 +03:00
Dmitry Podgorny
529a9bed44 autotools: export only public API for dynamic library 2014-09-06 23:52:42 +03:00
Dmitry Podgorny
fd4f26e5d4 tls_schannel: [style] removed trailing spaces 2014-09-06 23:43:43 +03:00
Dmitry Podgorny
e7f87c652e tls_schannel: [style] use LF for EOL, not CR+LF 2014-09-06 22:32:16 +03:00
Dmitry Podgorny
65d2535302 tls_openssl: removed unused openssl/rand.h 2014-09-06 22:31:53 +03:00
Dmitry Podgorny
a981c0a733 sha1: workaround for name collision with libcrypto
src/sha1.c is used only for internal purposes. So, just add prefix
xmpp_ to the SHA1 symbols.
2014-08-31 01:47:03 +03:00
Dmitry Podgorny
b08625ef4a md5: MD5Transform should be static 2014-08-31 01:46:19 +03:00
Dmitry Podgorny
ea719cd02a SCRAM-SHA-1 authentication mechanism support 2014-08-31 00:44:08 +03:00
Dmitry Podgorny
52dcd26b8c sasl: don't use uninitialized variable
base64_decode() used uninitialized variable when unable to allocate memory.

Reported-by: Fabian Freyer <fabian.freyer@physik.tu-berlin.de>
2014-08-30 15:14:22 +03:00
Dmitry Podgorny
cf361d35fe auth: removed useless static declaration
_handle_open_tls() declared as static function but not defined.
2014-08-26 14:53:15 +03:00
Dmitry Podgorny
077afae693 Added tags to .gitignore
This file is generated by ctags.
2014-08-26 14:26:43 +03:00
Damian Obernikowicz
783bad0897 Fixed memory leak 2014-08-20 00:36:59 +03:00
127 changed files with 21597 additions and 8955 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.
-->

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

@@ -0,0 +1,145 @@
name: CI
on:
push:
branches:
- master
- next
pull_request:
branches: [master]
jobs:
linux-tests:
runs-on: ubuntu-20.04
strategy:
matrix:
valgrind:
- { configure: '' , make: 'check' }
- { configure: '--enable-valgrind' , 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"
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-22.04
strategy:
matrix:
xssl_versions:
- { version: "master", continue: true, libressl: true }
- { version: "OPENBSD_7_4", continue: true, libressl: true }
- { version: "v3.8.1", continue: true, libressl: true }
- { version: "OPENBSD_7_3", continue: true, libressl: true }
- { version: "OPENBSD_7_2", continue: true, libressl: true }
- { version: "OPENBSD_7_1", continue: true, libressl: true }
# https://github.com/libressl-portable/portable/issues/760
# - { 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.10", continue: false, libressl: false }
- { version: "openssl-3.1", continue: true, libressl: false }
- { version: "openssl-3.1.2", continue: false, libressl: false }
valgrind:
- { configure: '' , make: 'check' }
- { configure: '--enable-valgrind' , make: 'check-valgrind' }
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
./configure ${{ matrix.valgrind.configure }} PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
make -j$(nproc)
- name: Run tests
run: |
make -j$(nproc) ${{ matrix.valgrind.make }}
- name: Error logs
if: ${{ failure() }}
run: |
cat test-suite*.log || true
release-test:
runs-on: ubuntu-20.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
code-style:
runs-on: ubuntu-20.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

57
.gitignore vendored
View File

@@ -1,8 +1,8 @@
*.swp
*.orig
Makefile
test-driver
.libs
src/*.lo
*.la
Makefile.in
configure
libtool
@@ -20,24 +20,69 @@ autom4te.cache
.dirstamp
.deps
.sconsign*
src/*.gcda
src/*.gcno
src/*.o
src/*.lo
examples/*.o
tests/*.o
tests/check_parser
*.a
*.la
*.pc
*.tar.gz
*.tar.bz2
*.tar.xz
*.zip
docs/html
TAGS
examples/basic
tags
examples/active
examples/roster
examples/basic
examples/bot
examples/complex
examples/component
examples/perf
examples/register
examples/roster
examples/uuid
examples/vcard
testbuild*.log
test-release/
test_stamp
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
tests/test_rand
tests/test_resolver
tests/test_sasl
tests/test_scram
tests/test_send_queue
tests/test_sha1
tests/test_sha256
tests/test_sha512
tests/test_snprintf
tests/test_sock
tests/test_stanza
tests/test_string
tests/test_xmppaddr
m4/lt*
m4/libtool*
libstrophe.project
libs/
obj/
expat/
.settings/
.project
.cproject
coverage/
coverage.info
configure~
fuzz-*.log

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.

224
ChangeLog
View File

@@ -1,3 +1,223 @@
0.8.5
- libtoolize to generate .so
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
- SCRAM-SHA-256 and SCRAM-SHA-512 support
- c-ares support
- LibreSSL support
- Introduced global timed handlers that fire periodically regardless of
connections status, such a handler can be used to implement deferred
re-connection
- examples/register implements XEP-0077
- Fixed issue with IPv6 on Windows (#153)
- Improved portability across systems such as Haiku, Windows
- New API:
- xmpp_stanza_get_child_by_name_and_ns()
- xmpp_conn_is_connecting()
- xmpp_conn_is_connected()
- xmpp_conn_is_disconnected()
- xmpp_stanza_new_from_string()
- xmpp_stanza_add_child_ex()
- xmpp_stanza_get_context()
- xmpp_stanza_reply_error()
- xmpp_global_timed_handler_add()
- xmpp_global_timed_handler_delete()
0.9.3
- PLAIN mechanism is used only when no other mechanisms are supported
- Legacy authentication is disabled by default, can be enabled with
connection flag XMPP_CONN_FLAG_LEGACY_AUTH
- Session is not established if it is optional
- Fixed a bug causing a reused connection not to cleanup properly
- Improved debug logging in OpenSSL module
- Few memory leaks fixed
0.9.2
- OpenSSL tls module verifies certificate by default. Set flag
XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification
- Certificate hostname verification is forced for openssl-1.0.2 and
newer
- OpenSSL tls module disables insecure SSLv2 SSLv3 and TLSv1
- Support of handlers with the same callback function, but different
userdata
- System handlers are deleted on xmpp_conn_t reconnection. Old system
handlers could cause problems
- Default timeout for xmpp_run() is increased from 1 millisecond to 1
second in order to reduce CPU consumption
- Reduced memory usage in expat module
- New functions:
- xmpp_error_new()
- xmpp_send_error()
- xmpp_ctx_set_timeout()
- xmpp_sha1_digest()
0.9.1
- Fixed bug #95 (DNS lookup failing on Cygwin)
- Removed dependency on the check package
0.9.0
- IPv6 support
- Legacy SSL support
- Initial Android support
- Resolver returns all SRV records instead of one. Lookup is performed
according to RFC2052
- xmpp_connect_raw() provides access to a xmpp_conn object just after
establishing TCP connection. This allows to implement in-band
registration, authentication mechanisms or serverless messaging
- xmpp_conn_t object is reusable now and can be reconnected with saving
all handlers, flags, jid and password
- New API:
- xmpp_uuid_gen()
- xmpp_connect_raw()
- xmpp_conn_open_stream_default()
- xmpp_conn_open_stream()
- xmpp_conn_tls_start()
- xmpp_conn_get_flags()
- xmpp_conn_set_flags()
- xmpp_conn_set_keepalive()
- xmpp_conn_is_secured()
- xmpp_stanza_del_attribute()
- xmpp_stanza_get_to()
- xmpp_stanza_get_from()
- xmpp_stanza_set_to()
- xmpp_stanza_set_from()
- xmpp_stanza_reply()
- xmpp_message_new()
- xmpp_message_get_body()
- xmpp_message_set_body()
- xmpp_iq_new()
- xmpp_presence_new()
- Exposed private API:
- xmpp_jid_new()
- xmpp_jid_bare()
- xmpp_jid_node()
- xmpp_jid_domain()
- xmpp_jid_resource()
- xmpp_stanza_get_attribute_count()
- xmpp_stanza_get_attributes()
0.8.8
- XML namespace support
- XEP-0114 support
0.8.7
- SCRAM-SHA-1 authentication mechanism
- pkg-config support
0.8.5
- libtoolize to generate .so

2744
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

@@ -1,27 +1,109 @@
AUTOMAKE_OPTIONS = subdir-objects
AM_CFLAGS = -g -Wall
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@
SSL_LIBS = @openssl_LIBS@
ZLIB_CFLAGS=@ZLIB_CFLAGS@
ZLIB_LIBS=@ZLIB_LIBS@
STROPHE_FLAGS = -I$(top_srcdir)
STROPHE_LIBS = libstrophe.la
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 = $(COVERAGE_PRE) libstrophe.la $(COVERAGE_POST) $(COVERAGE_LDFLAGS)
## Main build targets
lib_LTLIBRARIES = libstrophe.la
libstrophe_la_CFLAGS=$(STROPHE_FLAGS) $(PARSER_CFLAGS)
libstrophe_la_LDFLAGS=$(SSL_LIBS) $(PARSER_LIBS)
libstrophe_la_SOURCES = src/auth.c src/conn.c src/ctx.c \
src/event.c src/handler.c src/hash.c \
src/jid.c src/md5.c src/sasl.c src/sha1.c \
src/snprintf.c src/sock.c src/stanza.c src/thread.c \
src/tls_openssl.c src/util.c \
src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \
src/sasl.h src/sha1.h src/sock.h src/thread.h src/tls.h src/util.h
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS) $(ZLIB_CFLAGS)
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) $(ZLIB_LIBS) -no-undefined
# Export only public API
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
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 \
src/jid.c \
src/md5.c \
src/rand.c \
src/resolver.c \
src/sasl.c \
src/scram.c \
src/sha1.c \
src/sha256.c \
src/sha512.c \
src/sock.c \
src/stanza.c \
src/tls.c \
src/util.c \
src/uuid.c
libstrophe_la_SOURCES += \
src/common.h \
src/hash.h \
src/md5.h \
src/ostypes.h \
src/parser.h \
src/resolver.h \
src/sasl.h \
src/scram.h \
src/sha.h \
src/sha1.h \
src/sha256.h \
src/sha512.h \
src/snprintf.h \
src/sock.h \
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
@@ -30,30 +112,236 @@ libstrophe_la_SOURCES += src/parser_libxml2.c
endif
include_HEADERS = strophe.h
noinst_HEADERS = strophepp.h
EXTRA_DIST = docs
pkgconfig_DATA = libstrophe.pc
EXTRA_DIST = \
_clang-format \
Doxyfile \
GPL-LICENSE.txt \
LICENSE.txt \
MIT-LICENSE.txt \
bootstrap.sh \
build-android.sh \
testbuild.sh \
examples/README.md \
jni/Android.mk \
jni/Application.mk \
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/roster examples/basic examples/bot
noinst_PROGRAMS = \
examples/active \
examples/basic \
examples/bot \
examples/complex \
examples/component \
examples/perf \
examples/register \
examples/roster \
examples/uuid \
examples/vcard
examples_active_SOURCES = examples/active.c
examples_active_CFLAGS = $(STROPHE_FLAGS)
examples_active_LDADD = $(STROPHE_LIBS)
examples_roster_SOURCES = examples/roster.c
examples_roster_CFLAGS = $(STROPHE_FLAGS)
examples_roster_LDADD = $(STROPHE_LIBS)
examples_active_LDADD = $(STROPHE_LIBS)
examples_basic_SOURCES = examples/basic.c
examples_basic_CFLAGS = $(STROPHE_FLAGS)
examples_basic_LDADD = $(STROPHE_LIBS)
examples_basic_LDADD = $(STROPHE_LIBS)
examples_bot_SOURCES = examples/bot.c
examples_bot_CFLAGS = $(STROPHE_FLAGS)
examples_bot_LDADD = $(STROPHE_LIBS)
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)
examples_uuid_SOURCES = examples/uuid.c
examples_uuid_CFLAGS = $(STROPHE_FLAGS)
examples_uuid_LDADD = $(STROPHE_LIBS)
examples_vcard_SOURCES = examples/vcard.c
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
examples_vcard_LDADD = $(STROPHE_LIBS)
endif
## Tests
TESTS = tests/check_parser
check_PROGRAMS = tests/check_parser
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
tests_check_parser_CFLAGS = @check_CFLAGS@ $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
STATIC_TESTS = \
tests/check_parser \
tests/test_hash \
tests/test_jid \
tests/test_ctx \
tests/test_send_queue \
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_base64 \
tests/test_snprintf \
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_check_parser_LDADD = @check_LIBS@ $(STROPHE_LIBS)
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
tests_check_parser_LDADD = $(STROPHE_LIBS)
tests_check_parser_LDFLAGS = -static
tests_test_ctx_SOURCES = tests/test_ctx.c
tests_test_ctx_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_ctx_LDADD = $(STROPHE_LIBS)
tests_test_ctx_LDFLAGS = -static
tests_test_base64_SOURCES = tests/test_base64.c tests/test.c tests/test.h
tests_test_base64_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_base64_LDADD = $(STROPHE_LIBS)
tests_test_base64_LDFLAGS = -static
tests_test_hash_SOURCES = tests/test_hash.c
tests_test_hash_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_hash_LDADD = $(STROPHE_LIBS)
tests_test_hash_LDFLAGS = -static
tests_test_jid_SOURCES = tests/test_jid.c
tests_test_jid_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_jid_LDADD = $(STROPHE_LIBS)
tests_test_jid_LDFLAGS = -static
tests_test_resolver_SOURCES = tests/test_resolver.c tests/test.h
tests_test_resolver_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_resolver_LDADD = $(STROPHE_LIBS)
tests_test_resolver_LDFLAGS = -static
tests_test_rand_SOURCES = tests/test_rand.c tests/test.c src/sha1.c
tests_test_rand_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_scram_SOURCES = tests/test_scram.c tests/test.c src/sha1.c \
src/sha256.c src/sha512.c
tests_test_scram_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_sha1_SOURCES = tests/test_sha1.c src/sha1.c
tests_test_sha1_CFLAGS = -I$(top_srcdir)/src
tests_test_sha256_SOURCES = tests/test_sha256.c tests/test.c src/sha256.c
tests_test_sha256_CFLAGS = -I$(top_srcdir)/src
tests_test_sha512_SOURCES = tests/test_sha512.c tests/test.c src/sha512.c
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_snprintf_SOURCES = tests/test_snprintf.c
tests_test_snprintf_CFLAGS = -I$(top_srcdir)/src
tests_test_string_SOURCES = tests/test_string.c tests/test.h
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.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 -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
@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,16 +1,14 @@
# 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 both Linux, Unix, and Windows based platforms.
runs well on Linux, Unix and Windows based platforms.
Its goals are:
libstrophe is dual licensed under MIT or GPLv3.
- usable quickly
- well documented
- reliable
## Build Instructions
Build Instructions
------------------
If you are building from a source control checkout, run:
@@ -23,9 +21,7 @@ From the top-level directory, run the following commands:
./configure
make
This will create a static library, also in the top-level
directory, which can be linked into other programs. The
public API is defined in `strophe.h` which is also in the
The public API is defined in `strophe.h` which is in the
top-level directory.
The `examples` directory contains some examples of how to
@@ -41,26 +37,44 @@ another path use the `--prefix` option during configure, e.g.:
./configure --prefix=/usr
## Requirements
### Android
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
- libresolv on UNIX systems - make sure you include -lresolv
if you are compiling by hand.
- libtool
- openssl or GnuTLS on UNIX systems - openssl is default; use
`--with-gnutls` to switch
In addition, if you wish to run the unit tests, you will need the
check package.
To build libstrophe using autotools you will need `autoconf`,
`automake`, `libtool` and `pkg-config`.
### OS X (with Homebrew package manager)
To run code coverage analysis you will need `gcov` and `lcov`.
You can install libstrophe with:
Installation
------------
brew install libstrophe
libstrophe package has been added to popular Linux distributions,
BSD systems and OSX package managers.
## Documentation
Documentation
-------------
API documentation is inline with the code and conforms to Doxygen
standards. You can generate an HTML version of the API documentation
@@ -68,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/

18
_clang-format Normal file
View File

@@ -0,0 +1,18 @@
---
AlignConsecutiveAssignments: 'false'
AlignEscapedNewlines: Left
AllowShortBlocksOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: 'false'
BinPackParameters: 'false'
BreakBeforeBraces: Linux
ColumnLimit: '80'
DerivePointerAlignment: 'false'
IndentGotoLabels: false
IndentWidth: '4'
PointerAlignment: Right
SortIncludes: 'false'
TabWidth: '4'
UseTab: Never
...

View File

@@ -1,3 +1,5 @@
#!/bin/sh
# Some versions of aclocal fail if m4/ doesn't exist
mkdir -p m4
autoreconf -i

39
build-android.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
DIR=`dirname $0`
EXPAT_PATH="$DIR/expat"
EXPAT_FILE='lib/expat.h'
echo
echo Attention: libstrophe will be built without TLS support. To enable TLS
echo you need to replace \'src/tls_dummy.c\' with \'src/tls_opnessl.c\' in
echo \'jni/Android.mk\', add path to the openssl headers and link your program
echo with libssl and libcrypto.
echo
if [ ! -d $EXPAT_PATH ]; then
mkdir $EXPAT_PATH
fi
# TODO Accept expat tarball as argument and extract it to the right place.
# Or download sources from sf.net.
if [ ! -d $EXPAT_PATH/lib -o ! -f "$EXPAT_PATH/$EXPAT_FILE" ]; then
cat <<EOT
Error: expat sources not found.
Extract expat sources to $EXPAT_PATH. Make sure $EXPAT_PATH/$EXPAT_FILE and
other source files exist.
EOT
exit 1
fi
ndk-build -C "$DIR" clean || exit 1
ndk-build -C "$DIR" || exit 1
echo
echo "basic example:"
ls -l "$DIR"/libs/*/basic
echo
echo "libstrophe.a:"
ls -l "$DIR"/obj/local/*/libstrophe.a

View File

@@ -1,4 +1,14 @@
AC_INIT([libstrophe], [0.8.5], [jack@metajack.im])
m4_define([v_maj], [0])
m4_define([v_min], [13])
m4_define([v_patch], [1])
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])
@@ -8,61 +18,294 @@ PKG_PROG_PKG_CONFIG
AC_CANONICAL_HOST
AS_CASE([$host_os],
[freebsd*], [PLATFORM="freebsd"],
[PLATFORM="nix"])
[freebsd*], [PLATFORM="bsd"],
[openbsd*], [PLATFORM="bsd"],
[netbsd*], [PLATFORM="bsd"],
[*nto*|*qnx*], [PLATFORM="qnx"],
[*solaris*], [PLATFORM="solaris"],
[*android*], [PLATFORM="android"],
[*haiku*], [PLATFORM="haiku"],
[*mingw*], [PLATFORM="win32"
MINGW_LIBS="-lws2_32"],
[PLATFORM="nix"])
PKG_CHECK_MODULES([openssl], [openssl], [], [AC_MSG_ERROR([openssl not found; openssl required])])
PKG_CHECK_MODULES([check], [check >= 0.9.4], [], [AC_MSG_WARN([libcheck not found; unit tests will not be compilable])])
WARNING_FLAGS="-Wall"
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])],
[with_libxml2=check],
[with_libxml2=no])
[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])])
if test "x$with_libxml2" != xno; then
PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7],
[with_libxml2=yes], [dummy=1])
if test "x$with_libxml2" != xyes; then
old_CFLAGS=$CFLAGS
CFLAGS="-I${includedir}/libxml2"
AC_CHECK_HEADER([libxml/parser.h],
[with_libxml2=yes; libxml2_CFLAGS="-I${includedir}/libxml2"; libxml2_LIBS="-lxml2"],
[AC_MSG_ERROR([libxml2 not found.])])
CFLAGS=$old_CFLAGS
fi
else
PKG_CHECK_MODULES([expat], [expat >= 2.0.0], [],
[AC_CHECK_HEADER(expat.h, [expat_LIBS="-lexpat"], [AC_MSG_ERROR([expat not found; expat required.])])])
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
if test "x$with_libxml2" = xyes; then
with_parser=libxml2
PARSER_NAME=libxml2
PARSER_CFLAGS=$libxml2_CFLAGS
PARSER_LIBS=$libxml2_LIBS
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
with_parser=expat
PARSER_NAME=expat
PARSER_CFLAGS=$expat_CFLAGS
PARSER_LIBS=$expat_LIBS
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], [], [have_snprintf=no])
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],
[
openssl_LIBS="-lssl -lcrypto"
PC_LIBS="${openssl_LIBS} ${PC_LIBS}"
],
[AC_MSG_ERROR([openssl not found; openssl required])]
)])
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],
[
with_parser="expat"
PC_REQUIRES="expat ${PC_REQUIRES}"
],
[AC_CHECK_HEADER([expat.h],
[
with_parser="expat"
expat_LIBS="-lexpat"
PC_LIBS="${expat_LIBS} ${PC_LIBS}"
])
])
fi
if test "x$with_libxml2" != xno -a "x$with_parser" = x; then
PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7],
[
with_parser="libxml2"
PC_REQUIRES="libxml-2.0 ${PC_REQUIRES}"
],
[
old_CFLAGS=$CFLAGS
CFLAGS="-I${includedir}/libxml2"
AC_CHECK_HEADER([libxml/parser.h],
[
with_parser="libxml2"
libxml2_CFLAGS="-I${includedir}/libxml2"
libxml2_LIBS="-lxml2 -lm"
PC_LIBS="${libxml2_LIBS} ${PC_LIBS}"
PC_CFLAGS="${libxml2_CFLAGS} ${PC_CFLAGS}"
])
CFLAGS=$old_CFLAGS
])
fi
if test "x$with_parser" = xlibxml2; then
PARSER_CFLAGS=$libxml2_CFLAGS
PARSER_LIBS=$libxml2_LIBS
elif test "x$with_parser" = xexpat; then
PARSER_CFLAGS=$expat_CFLAGS
PARSER_LIBS=$expat_LIBS
else
AC_MSG_ERROR([no XML parser was found, libstrophe requires expat or libxml2])
fi
AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
AC_SEARCH_LIBS([socket], [socket])
if test "x$PLATFORM" != xfreebsd; then
AC_CHECK_LIB([resolv], [res_query], [],
[AC_CHECK_LIB([resolv], [__res_query], [],
[AC_MSG_ERROR([libresolv not found; libresolv required. ])])])
if test "x$enable_cares" = xyes; then
PKG_CHECK_MODULES([libcares], [libcares >= 1.7.0],
[
PC_REQUIRES="libcares ${PC_REQUIRES}"
RESOLV_CFLAGS=$libcares_CFLAGS
RESOLV_LIBS=$libcares_LIBS
AC_DEFINE([HAVE_CARES])
],
[AC_MSG_ERROR([libcares not found])])
# TODO: if pkg-config doesn't find, check the library manually
else
AC_MSG_NOTICE([skipping libresolv checks for freebsd])
AS_CASE([$PLATFORM],
[bsd], [RESOLV_LIBS=""],
[qnx], [RESOLV_LIBS="-lsocket"],
[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([
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
int main() {
(void)res_query(NULL, 0, 0, NULL, 0);
return 0;
}
])],
[],
[AC_MSG_ERROR([res_query() not found with LIBS="${LIBS}"])])
LIBS="${LIBS_TMP}"
PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}"
fi
AC_CHECK_HEADERS([arpa/nameser_compat.h])
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],
[install directory for libstrophe.pc pkg-config file])],
[], [with_pkgconfigdir='$(libdir)/pkgconfig'])
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
AC_SUBST(PARSER_NAME)
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_CONFIG_FILES([Makefile])
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>

7
examples/README.md Normal file
View File

@@ -0,0 +1,7 @@
Build Instructions
==================
Compile example files with the following gcc flags:
```
gcc -o bot bot.c `pkg-config --cflags --libs libstrophe`
```

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* active.c
** libstrophe XMPP client library -- basic usage example
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* This example demonstrates basic handler functions by printing out
@@ -22,23 +20,23 @@
#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;
char *type;
const char *type;
(void)userdata;
type = xmpp_stanza_get_type(stanza);
if (strcmp(type, "error") == 0)
fprintf(stderr, "ERROR: query failed\n");
fprintf(stderr, "ERROR: query failed\n");
else {
query = xmpp_stanza_get_child_by_name(stanza, "query");
printf("Active Sessions:\n");
for (item = xmpp_stanza_get_children(query); item;
item = xmpp_stanza_get_next(item))
printf("\t %s\n", xmpp_stanza_get_attribute(item, "jid"));
printf("END OF LIST\n");
query = xmpp_stanza_get_child_by_name(stanza, "query");
printf("Active Sessions:\n");
for (item = xmpp_stanza_get_children(query); item;
item = xmpp_stanza_get_next(item))
printf("\t %s\n", xmpp_stanza_get_attribute(item, "jid"));
printf("END OF LIST\n");
}
/* disconnect */
@@ -47,44 +45,46 @@ 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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
/* create iq stanza for request */
iq = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(iq, "iq");
xmpp_stanza_set_type(iq, "get");
xmpp_stanza_set_id(iq, "active1");
xmpp_stanza_set_attribute(iq, "to", "xxxxxxxxx.com");
fprintf(stderr, "DEBUG: connected\n");
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS);
xmpp_stanza_set_attribute(query, "node", "sessions");
/* create iq stanza for request */
iq = xmpp_iq_new(ctx, "get", "active1");
xmpp_stanza_set_to(iq, "xxxxxxxxx.com");
xmpp_stanza_add_child(iq, query);
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS);
xmpp_stanza_set_attribute(query, "node", "sessions");
/* we can release the stanza since it belongs to iq now */
xmpp_stanza_release(query);
xmpp_stanza_add_child(iq, query);
/* set up reply handler */
xmpp_id_handler_add(conn, handle_reply, "active1", ctx);
/* we can release the stanza since it belongs to iq now */
xmpp_stanza_release(query);
/* send out the stanza */
xmpp_send(conn, iq);
/* set up reply handler */
xmpp_id_handler_add(conn, handle_reply, "active1", ctx);
/* release the stanza */
xmpp_stanza_release(iq);
/* send out the stanza */
xmpp_send(conn, iq);
/* release the stanza */
xmpp_stanza_release(iq);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
}
@@ -94,8 +94,8 @@ int main(int argc, char **argv)
xmpp_conn_t *conn;
if (argc != 3) {
fprintf(stderr, "Usage: active <jid> <pass>\n\n");
return 1;
fprintf(stderr, "Usage: active <jid> <pass>\n\n");
return 1;
}
/* initialize lib */
@@ -107,6 +107,13 @@ int main(int argc, char **argv)
/* 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
*/
/* setup authentication information */
xmpp_conn_set_jid(conn, argv[1]);
xmpp_conn_set_pass(conn, argv[2]);

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,74 +7,126 @@
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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)
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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
xmpp_disconnect(conn);
}
else {
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
}
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;
char *jid = NULL, *password = NULL, *host = NULL;
long flags = 0;
int i;
unsigned long port = 0;
/* take a jid and password on the command line */
if (argc < 3 || argc > 4) {
fprintf(stderr, "Usage: basic <jid> <pass> [<host>]\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], "--jid") == 0) && (++i < argc))
jid = argv[i];
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
password = argv[i];
else
break;
}
if ((!jid) || (argc - i) > 2) {
usage(1);
}
jid = argv[1];
pass = argv[2];
host = NULL;
if (i < argc)
host = argv[i];
if (i + 1 < argc)
port = strtoul(argv[i + 1], NULL, 0);
if (argc == 4)
host = argv[3];
/*
* 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 */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log);
/* create a connection */
conn = xmpp_conn_new(ctx);
/* configure connection properties (optional) */
xmpp_conn_set_flags(conn, flags);
/* 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,19 +1,17 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* bot.c
** libstrophe XMPP client library -- basic usage example
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* simple bot example
**
**
** This example was provided by Matthew Wild <mwild1@gmail.com>.
**
** This bot responds to basic messages and iq version requests.
@@ -22,113 +20,209 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef _WIN32
#include <conio.h>
#include <ctype.h>
#endif
#include <strophe.h>
static int reconnect;
int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *reply, *query, *name, *version, *text;
char *ns;
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from"));
reply = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(reply, "iq");
xmpp_stanza_set_type(reply, "result");
xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_t *reply, *query, *name, *version, *text;
const char *ns;
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
printf("Received version request from %s\n", xmpp_stanza_get_from(stanza));
reply = xmpp_stanza_reply(stanza);
xmpp_stanza_set_type(reply, "result");
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
if (ns) {
xmpp_stanza_set_ns(query, ns);
}
name = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(name, "name");
xmpp_stanza_add_child(query, name);
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, "libstrophe example bot");
xmpp_stanza_add_child(name, text);
version = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(version, "version");
xmpp_stanza_add_child(query, version);
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, "1.0");
xmpp_stanza_add_child(version, text);
xmpp_stanza_add_child(reply, query);
name = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(name, "name");
xmpp_stanza_add_child(query, name);
xmpp_stanza_release(name);
xmpp_send(conn, reply);
xmpp_stanza_release(reply);
return 1;
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, "libstrophe example bot");
xmpp_stanza_add_child(name, text);
xmpp_stanza_release(text);
version = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(version, "version");
xmpp_stanza_add_child(query, version);
xmpp_stanza_release(version);
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, "1.0");
xmpp_stanza_add_child(version, text);
xmpp_stanza_release(text);
xmpp_stanza_add_child(reply, query);
xmpp_stanza_release(query);
xmpp_send(conn, reply);
xmpp_stanza_release(reply);
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)
{
xmpp_stanza_t *reply, *body, *text;
char *intext, *replytext;
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
if(!xmpp_stanza_get_child_by_name(stanza, "body")) return 1;
if(xmpp_stanza_get_attribute(stanza, "type") !=NULL && !strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error")) return 1;
intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
printf("Incoming message from %s: %s\n", xmpp_stanza_get_attribute(stanza, "from"), intext);
reply = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(reply, "message");
xmpp_stanza_set_type(reply, xmpp_stanza_get_type(stanza)?xmpp_stanza_get_type(stanza):"chat");
xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
body = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(body, "body");
replytext = malloc(strlen(" to you too!") + strlen(intext) + 1);
strcpy(replytext, intext);
strcat(replytext, " to you too!");
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, replytext);
xmpp_stanza_add_child(body, text);
xmpp_stanza_add_child(reply, body);
xmpp_send(conn, reply);
xmpp_stanza_release(reply);
free(replytext);
return 1;
(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;
body = xmpp_stanza_get_child_by_name(stanza, "body");
if (body == NULL)
return 1;
type = xmpp_stanza_get_type(stanza);
if (type != NULL && strcmp(type, "error") == 0)
return 1;
intext = xmpp_stanza_get_text(body);
printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza),
intext);
reply = xmpp_stanza_reply(stanza);
if (xmpp_stanza_get_type(reply) == NULL)
xmpp_stanza_set_type(reply, "chat");
if (strcmp(intext, "quit") == 0) {
replytext = strdup("bye!");
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);
strcat(replytext, " to you too!");
}
xmpp_free(ctx, intext);
xmpp_message_set_body(reply, replytext);
xmpp_send(conn, reply);
xmpp_stanza_release(reply);
free(replytext);
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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
xmpp_stanza_t* pres;
fprintf(stderr, "DEBUG: connected\n");
xmpp_handler_add(conn,version_handler, "jabber:iq:version", "iq", NULL, ctx);
xmpp_handler_add(conn,message_handler, NULL, "message", NULL, ctx);
/* Send initial <presence/> so that we appear online to contacts */
pres = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(pres, "presence");
xmpp_send(conn, pres);
xmpp_stanza_release(pres);
xmpp_stanza_t *pres;
fprintf(stderr, "DEBUG: connected\n");
xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL,
ctx);
xmpp_handler_add(conn, message_handler, NULL, "message", NULL, ctx);
/* Send initial <presence/> so that we appear online to contacts */
pres = xmpp_presence_new(ctx);
xmpp_send(conn, pres);
xmpp_stanza_release(pres);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
#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)
@@ -136,40 +230,109 @@ 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;
}
jid = argv[1];
pass = argv[2];
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);
/* init library */
xmpp_initialize();
/* pass NULL instead to silence output */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log);
create_connection:
reconnect = 0;
/* create a connection */
conn = xmpp_conn_new(ctx);
/* 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;
}

101
examples/component.c Normal file
View File

@@ -0,0 +1,101 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* component.c
** libstrophe XMPP client library -- external component (XEP-0114) 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.
*/
/* This example demonstrates simple connection to a server
** as an external component. See XEP-0114 for more details.
** This program requires correctly configured server to run.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strophe.h>
/* define a handler for connection events */
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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
xmpp_disconnect(conn);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
}
int main(int argc, char **argv)
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
char *jid, *pass, *host, *port_err = NULL;
unsigned short port = 0;
/* take a jid and password on the command line */
if (argc < 4 || argc > 5) {
fprintf(stderr, "Usage: component <jid> <pass> <host> [<port>]\n\n");
return 1;
}
jid = argv[1];
pass = argv[2];
host = argv[3];
if (argc == 5) {
short tmp_port = (short)strtol(argv[4], &port_err, 10);
if (tmp_port < 0 || *port_err != '\0') {
fprintf(stderr, "Invalid value of <port> [%s].\n", argv[4]);
return 1;
}
port = (unsigned short)tmp_port;
}
/* 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);
/* create a connection */
conn = xmpp_conn_new(ctx);
/* setup authentication information */
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
/* initiate connection */
xmpp_connect_component(conn, host, port, conn_handler, ctx);
/* 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;
}

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

364
examples/register.c Normal file
View File

@@ -0,0 +1,364 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* register.c
* strophe XMPP client library -- In-band registration (XEP-0077)
*
* Copyright (C) 2020 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 or GPLv3 licenses.
*/
/*
* This example uses a "raw" connection to establish connection to a server
* without account. Then it queries server to register new account according
* to XEP-0077.
*
* How to use it. After the application connects and receives instructions
* from the server, user will be prompted to type information such as
* username, password, etc. Press enter without typing if you want to skip
* a field and not to send it to the server.
*
* Notice, the example doesn't implement forms. Therefore, it won't work
* in complicated scenarios.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strophe.h>
typedef struct {
xmpp_ctx_t *ctx;
const char *jid;
} xmpp_reg_t;
#define FEATURES_TIMEOUT 5000 /* 5 seconds */
static void
iq_reg_send_form(xmpp_reg_t *reg, xmpp_conn_t *conn, xmpp_stanza_t *stanza)
{
xmpp_ctx_t *ctx = reg->ctx;
xmpp_stanza_t *query;
xmpp_stanza_t *next;
xmpp_stanza_t *elem;
xmpp_stanza_t *text;
xmpp_stanza_t *iq;
const char *name;
size_t len;
char buf[256];
char *s;
query = xmpp_stanza_get_child_by_name(stanza, "query");
if (!query) {
xmpp_disconnect(conn);
return;
}
next = xmpp_stanza_get_children(query);
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_REGISTER);
while (next) {
name = xmpp_stanza_get_name(next);
if (name && strcmp(name, "instructions") == 0) {
s = xmpp_stanza_get_text(next);
printf("instructions: %s\n", s);
xmpp_free(ctx, s);
} else {
printf("%s: ", name);
s = fgets(buf, sizeof(buf), stdin);
if (s != NULL) {
len = strlen(s);
if (len > 0 && s[len - 1] == '\n') {
s[len - 1] = '\0';
--len;
}
if (len > 0) {
elem = xmpp_stanza_new(ctx);
text = xmpp_stanza_new(ctx);
xmpp_stanza_set_text(text, s);
xmpp_stanza_set_name(elem, name);
xmpp_stanza_add_child(elem, text);
xmpp_stanza_add_child(query, elem);
xmpp_stanza_release(text);
xmpp_stanza_release(elem);
}
}
}
next = xmpp_stanza_get_next(next);
}
if (xmpp_stanza_get_children(query) == NULL) {
fprintf(stderr, "DEBUG: nothing to send\n");
xmpp_disconnect(conn);
} else {
iq = xmpp_iq_new(ctx, "set", "reg2");
xmpp_stanza_add_child(iq, query);
xmpp_stanza_release(query);
xmpp_send(conn, iq);
xmpp_stanza_release(iq);
}
}
static int iq_reg2_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
const char *type;
(void)userdata;
type = xmpp_stanza_get_type(stanza);
if (!type || strcmp(type, "error") == 0) {
fprintf(stderr, "DEBUG: error during registration\n");
goto quit;
}
if (strcmp(type, "result") != 0) {
fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type);
goto quit;
}
fprintf(stderr, "DEBUG: successful registration\n");
quit:
xmpp_disconnect(conn);
return 0;
}
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;
xmpp_stanza_t *query;
const char *type;
type = xmpp_stanza_get_type(stanza);
if (!type || strcmp(type, "error") == 0) {
fprintf(stderr, "DEBUG: error during registration\n");
xmpp_disconnect(conn);
goto quit;
}
if (strcmp(type, "result") != 0) {
fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type);
xmpp_disconnect(conn);
goto quit;
}
query = xmpp_stanza_get_child_by_name(stanza, "query");
if (query)
registered = xmpp_stanza_get_child_by_name(query, "registered");
if (registered != NULL) {
fprintf(stderr, "DEBUG: already registered\n");
xmpp_disconnect(conn);
goto quit;
}
xmpp_id_handler_add(conn, iq_reg2_cb, "reg2", reg);
iq_reg_send_form(reg, conn, stanza);
quit:
return 0;
}
static int
_handle_error(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
(void)stanza;
(void)userdata;
fprintf(stderr, "DEBUG: received stream error\n");
xmpp_disconnect(conn);
return 0;
}
static int _handle_proceedtls_default(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata)
{
const char *name = xmpp_stanza_get_name(stanza);
(void)userdata;
if (strcmp(name, "proceed") == 0) {
fprintf(stderr, "DEBUG: proceeding with TLS\n");
if (xmpp_conn_tls_start(conn) == 0) {
xmpp_handler_delete(conn, _handle_error);
xmpp_conn_open_stream_default(conn);
} else {
fprintf(stderr, "DEBUG: TLS failed\n");
/* failed tls spoils the connection, so disconnect */
xmpp_disconnect(conn);
}
}
return 0;
}
static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
fprintf(stderr, "DEBUG: timeout\n");
xmpp_disconnect(conn);
return 0;
}
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;
xmpp_stanza_t *child;
xmpp_stanza_t *iq;
char *domain;
xmpp_timed_handler_delete(conn, _handle_missing_features);
/* secure connection if possible */
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0)) {
fprintf(stderr, "DEBUG: server supports TLS, try to establish\n");
child = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(child, "starttls");
xmpp_stanza_set_ns(child, XMPP_NS_TLS);
xmpp_handler_add(conn, _handle_proceedtls_default, XMPP_NS_TLS, NULL,
NULL, NULL);
xmpp_send(conn, child);
xmpp_stanza_release(child);
return 0;
}
/* check whether server supports in-band registration */
child = xmpp_stanza_get_child_by_name(stanza, "register");
if (child && strcmp(xmpp_stanza_get_ns(child), XMPP_NS_REGISTER) == 0) {
fprintf(stderr, "DEBUG: server doesn't support in-band registration\n");
xmpp_disconnect(conn);
return 0;
}
fprintf(stderr, "DEBUG: server supports in-band registration\n");
domain = xmpp_jid_domain(ctx, reg->jid);
iq = xmpp_iq_new(ctx, "get", "reg1");
xmpp_stanza_set_to(iq, domain);
child = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(child, "query");
xmpp_stanza_set_ns(child, XMPP_NS_REGISTER);
xmpp_stanza_add_child(iq, child);
xmpp_handler_add(conn, iq_reg_cb, XMPP_NS_REGISTER, "iq", NULL, reg);
xmpp_send(conn, iq);
xmpp_free(ctx, domain);
xmpp_stanza_release(child);
xmpp_stanza_release(iq);
return 0;
}
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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_RAW_CONNECT) {
fprintf(stderr, "DEBUG: raw connection established\n");
xmpp_conn_open_stream_default(conn);
} else if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: stream opened\n");
secured = xmpp_conn_is_secured(conn);
fprintf(stderr, "DEBUG: connection is %s.\n",
secured ? "secured" : "NOT secured");
/* setup handler for stream:error */
xmpp_handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL,
NULL);
/* setup handlers for incoming <stream:features> */
xmpp_handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features",
NULL, reg);
xmpp_timed_handler_add(conn, _handle_missing_features, FEATURES_TIMEOUT,
NULL);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(reg->ctx);
}
}
xmpp_reg_t *xmpp_reg_new(void)
{
xmpp_reg_t *reg;
reg = malloc(sizeof(*reg));
if (reg != NULL) {
memset(reg, 0, sizeof(*reg));
}
return reg;
}
void xmpp_reg_release(xmpp_reg_t *reg)
{
free(reg);
}
int main(int argc, char **argv)
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
xmpp_reg_t *reg;
const char *jid;
const char *host = NULL;
char *domain;
if (argc < 2 || argc > 3) {
fprintf(stderr, "Usage: %s <jid> [<host>]\n", argv[0]);
return 1;
}
jid = argv[1];
if (argc > 2)
host = argv[2];
/*
* Note, this example doesn't handle errors. Applications should check
* return values of non-void functions.
*/
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
/* jid can be a jid or domain for "raw" connection */
domain = xmpp_jid_domain(ctx, jid);
xmpp_conn_set_jid(conn, domain);
xmpp_free(ctx, domain);
/* private data */
reg = xmpp_reg_new();
reg->ctx = ctx;
reg->jid = jid;
xmpp_connect_raw(conn, host, 0, conn_handler, reg);
xmpp_run(ctx);
/* release private data */
xmpp_reg_release(reg);
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* roster.c
** libstrophe XMPP client library -- handler example
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/* This example demonstrates basic handler functions by printing out
@@ -21,31 +19,29 @@
#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;
char *type, *name;
const char *type, *name;
(void)userdata;
type = xmpp_stanza_get_type(stanza);
if (strcmp(type, "error") == 0)
fprintf(stderr, "ERROR: query failed\n");
fprintf(stderr, "ERROR: query failed\n");
else {
query = xmpp_stanza_get_child_by_name(stanza, "query");
printf("Roster:\n");
for (item = xmpp_stanza_get_children(query); item;
item = xmpp_stanza_get_next(item))
if ((name = xmpp_stanza_get_attribute(item, "name")))
printf("\t %s (%s) sub=%s\n",
name,
xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription"));
else
printf("\t %s sub=%s\n",
xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription"));
printf("END OF LIST\n");
query = xmpp_stanza_get_child_by_name(stanza, "query");
printf("Roster:\n");
for (item = xmpp_stanza_get_children(query); item;
item = xmpp_stanza_get_next(item))
if ((name = xmpp_stanza_get_attribute(item, "name")))
printf("\t %s (%s) sub=%s\n", name,
xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription"));
else
printf("\t %s sub=%s\n", xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription"));
printf("END OF LIST\n");
}
/* disconnect */
@@ -54,42 +50,44 @@ 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;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n");
/* create iq stanza for request */
iq = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(iq, "iq");
xmpp_stanza_set_type(iq, "get");
xmpp_stanza_set_id(iq, "roster1");
fprintf(stderr, "DEBUG: connected\n");
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_ROSTER);
/* create iq stanza for request */
iq = xmpp_iq_new(ctx, "get", "roster1");
xmpp_stanza_add_child(iq, query);
query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_ROSTER);
/* we can release the stanza since it belongs to iq now */
xmpp_stanza_release(query);
xmpp_stanza_add_child(iq, query);
/* set up reply handler */
xmpp_id_handler_add(conn, handle_reply, "roster1", ctx);
/* we can release the stanza since it belongs to iq now */
xmpp_stanza_release(query);
/* send out the stanza */
xmpp_send(conn, iq);
/* set up reply handler */
xmpp_id_handler_add(conn, handle_reply, "roster1", ctx);
/* release the stanza */
xmpp_stanza_release(iq);
/* send out the stanza */
xmpp_send(conn, iq);
/* release the stanza */
xmpp_stanza_release(iq);
} else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx);
}
}
@@ -99,8 +97,8 @@ int main(int argc, char **argv)
xmpp_conn_t *conn;
if (argc != 3) {
fprintf(stderr, "Usage: roster <jid> <pass>\n\n");
return 1;
fprintf(stderr, "Usage: roster <jid> <pass>\n\n");
return 1;
}
/* initialize lib */
@@ -112,6 +110,13 @@ int main(int argc, char **argv)
/* 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
*/
/* setup authentication information */
xmpp_conn_set_jid(conn, argv[1]);
xmpp_conn_set_pass(conn, argv[2]);

23
examples/uuid.c Normal file
View File

@@ -0,0 +1,23 @@
#include <stdio.h>
#include <strophe.h>
int main()
{
xmpp_ctx_t *ctx;
char *uuid;
int rc = 0;
ctx = xmpp_ctx_new(NULL, NULL);
uuid = xmpp_uuid_gen(ctx);
if (uuid != NULL) {
printf("%s\n", uuid);
xmpp_free(ctx, uuid);
} else {
fprintf(stderr, "Couldn't allocate memory.\n");
rc = 1;
}
xmpp_ctx_free(ctx);
return rc;
}

282
examples/vcard.c Normal file
View File

@@ -0,0 +1,282 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* vcard.c
* strophe XMPP client library -- vCard example
*
* Copyright (C) 2016 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 or GPLv3 licenses.
*/
#include <assert.h>
#include <libgen.h> /* basename */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <strophe.h>
typedef struct {
xmpp_ctx_t *ctx;
const char *recipient;
const char *img_path;
} vcard_t;
typedef void (*vcard_cb_t)(vcard_t *, xmpp_stanza_t *);
#define REQ_TIMEOUT 5000
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static void vcard_photo(vcard_t *vc, xmpp_stanza_t *stanza)
{
xmpp_stanza_t *tmp;
char *s;
char *tok;
char *saveptr = NULL;
char *copy;
unsigned char *img;
size_t img_size;
size_t written;
FILE *fd;
tmp = xmpp_stanza_get_child_by_name(stanza, "TYPE");
assert(tmp != NULL);
s = xmpp_stanza_get_text(tmp);
assert(s != NULL);
printf("PHOTO: %s, saving to file %s\n", s, vc->img_path);
xmpp_free(vc->ctx, s);
tmp = xmpp_stanza_get_child_by_name(stanza, "BINVAL");
assert(tmp != NULL);
s = xmpp_stanza_get_text(tmp);
assert(s != NULL);
/* remove \n and \r */
copy = (char *)malloc(strlen(s) + 1);
assert(copy != NULL);
copy[0] = '\0';
tok = strtok_r(s, "\n\r", &saveptr);
while (tok != NULL) {
strcat(copy, tok);
tok = strtok_r(NULL, "\n\r", &saveptr);
}
xmpp_base64_decode_bin(vc->ctx, copy, strlen(copy), &img, &img_size);
assert(img != NULL);
fd = fopen(vc->img_path, "w");
assert(fd != NULL);
written = fwrite(img, 1, img_size, fd);
if (written < img_size)
printf("Saving photo failed\n");
fclose(fd);
free(copy);
xmpp_free(vc->ctx, s);
xmpp_free(vc->ctx, img);
}
static void
vcard_print_string(vcard_t *vc, xmpp_stanza_t *stanza, const char *info)
{
char *s = xmpp_stanza_get_text(stanza);
assert(s != NULL);
printf("%s: %s\n", info, s);
xmpp_free(vc->ctx, s);
}
static void vcard_bday(vcard_t *vc, xmpp_stanza_t *stanza)
{
vcard_print_string(vc, stanza, "Birthday");
}
static void vcard_desc(vcard_t *vc, xmpp_stanza_t *stanza)
{
vcard_print_string(vc, stanza, "Description");
}
static void vcard_email(vcard_t *vc, xmpp_stanza_t *stanza)
{
xmpp_stanza_t *userid = xmpp_stanza_get_child_by_name(stanza, "USERID");
if (userid != NULL)
vcard_print_string(vc, userid, "E-mail");
}
static void vcard_fn(vcard_t *vc, xmpp_stanza_t *stanza)
{
vcard_print_string(vc, stanza, "Full name");
}
static void vcard_name(vcard_t *vc, xmpp_stanza_t *stanza)
{
xmpp_stanza_t *name = xmpp_stanza_get_child_by_name(stanza, "GIVEN");
xmpp_stanza_t *family = xmpp_stanza_get_child_by_name(stanza, "FAMILY");
if (name != NULL)
vcard_print_string(vc, name, "Given name");
if (family != NULL)
vcard_print_string(vc, family, "Family name");
}
static void vcard_nick(vcard_t *vc, xmpp_stanza_t *stanza)
{
vcard_print_string(vc, stanza, "Nickname");
}
static void vcard_url(vcard_t *vc, xmpp_stanza_t *stanza)
{
vcard_print_string(vc, stanza, "URL");
}
static vcard_cb_t vcard_cb_get(xmpp_stanza_t *stanza)
{
vcard_cb_t cb = NULL;
const char *tag;
size_t i;
static struct {
const char *tag;
vcard_cb_t cb;
} vcard_tbl[] = {
{"PHOTO", vcard_photo}, {"BDAY", vcard_bday}, {"DESC", vcard_desc},
{"EMAIL", vcard_email}, {"FN", vcard_fn}, {"N", vcard_name},
{"NICKNAME", vcard_nick}, {"URL", vcard_url},
};
tag = xmpp_stanza_get_name(stanza);
if (tag == NULL)
goto exit;
for (i = 0; i < ARRAY_SIZE(vcard_tbl); ++i) {
if (strcmp(tag, vcard_tbl[i].tag) == 0) {
cb = vcard_tbl[i].cb;
break;
}
}
exit:
return cb;
}
static int timedout(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
fprintf(stderr, "Timeout reached.\n");
xmpp_disconnect(conn);
return 0;
}
static int recv_vcard(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
vcard_t *vc = userdata;
vcard_cb_t cb;
xmpp_stanza_t *child;
char *s;
size_t s_size;
int rc;
printf("Received vCard.\n\n");
child = xmpp_stanza_get_child_by_name(stanza, "error");
if (child != NULL) {
rc = xmpp_stanza_to_text(child, &s, &s_size);
assert(rc == XMPP_EOK);
printf("Error returned: %s.\n", s);
xmpp_free(vc->ctx, s);
goto exit;
}
child = xmpp_stanza_get_child_by_name(stanza, "vCard");
assert(child != NULL);
child = xmpp_stanza_get_children(child);
while (child != NULL) {
cb = vcard_cb_get(child);
if (cb != NULL)
cb(vc, child);
child = xmpp_stanza_get_next(child);
}
exit:
xmpp_disconnect(conn);
return 0;
}
static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id)
{
printf("Requesting vCard from %s.\n", to);
xmpp_send_raw_string(conn,
"<iq from='%s' to='%s' type='get' id='%s'>"
"<vCard xmlns='vcard-temp'/></iq>",
xmpp_conn_get_bound_jid(conn), to, id);
}
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;
if (status == XMPP_CONN_CONNECT) {
send_vcard_req(conn, vc->recipient, "vc1");
xmpp_id_handler_add(conn, recv_vcard, "vc1", vc);
xmpp_timed_handler_add(conn, timedout, REQ_TIMEOUT, NULL);
} else {
if (error != 0)
fprintf(stderr, "Disconnected with error=%d.\n", error);
if (stream_error != NULL)
fprintf(stderr, "Stream error type=%d text=%s.\n",
stream_error->type, stream_error->text);
xmpp_stop(vc->ctx);
}
}
int main(int argc, char **argv)
{
xmpp_log_t *log;
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
const char *jid;
const char *pass;
char *prog;
vcard_t vcard;
if (argc < 4 || argc > 5) {
prog = argc > 0 ? strdup(argv[0]) : NULL;
printf("Usage: %s <login-jid> <password> <recipient-jid> "
"[image-file]\n\n",
prog == NULL ? "vcard" : basename(prog));
printf("If vCard contains a photo it will be stored to "
"image-file. If you don't provide the image-file "
"default filename will be generated.\n");
free(prog);
return 1;
}
jid = argv[1];
pass = argv[2];
vcard.recipient = argv[3];
vcard.img_path = argc > 4 ? argv[4] : "vcard.jpg";
xmpp_initialize();
log = xmpp_get_default_logger(XMPP_LEVEL_INFO);
ctx = xmpp_ctx_new(NULL, log);
conn = xmpp_conn_new(ctx);
xmpp_conn_set_jid(conn, jid);
xmpp_conn_set_pass(conn, pass);
vcard.ctx = ctx;
xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard);
xmpp_run(ctx);
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
xmpp_shutdown();
return 0;
}

1
expat

Submodule expat deleted from adf19a3186

80
jni/Android.mk Normal file
View File

@@ -0,0 +1,80 @@
LOCAL_PATH:= $(call my-dir)
#
# examples/basic
#
include $(CLEAR_VARS)
LOCAL_MODULE := basic
LOCAL_CFLAGS :=
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/..
LOCAL_SRC_FILES := \
../examples/basic.c
LOCAL_STATIC_LIBRARIES := libstrophe libexpat
include $(BUILD_EXECUTABLE)
#
# libstrohe (static library)
#
include $(CLEAR_VARS)
LOCAL_MODULE := libstrophe
LOCAL_CFLAGS := -DHAVE_DECL_VA_COPY
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \
$(LOCAL_PATH)/../src \
$(LOCAL_PATH)/../expat/lib
LOCAL_SRC_FILES := \
../src/auth.c \
../src/conn.c \
../src/crypto.c \
../src/ctx.c \
../src/event.c \
../src/handler.c \
../src/hash.c \
../src/jid.c \
../src/md5.c \
../src/parser_expat.c \
../src/rand.c \
../src/resolver.c \
../src/sasl.c \
../src/scram.c \
../src/sha1.c \
../src/sha256.c \
../src/sha512.c \
../src/snprintf.c \
../src/sock.c \
../src/stanza.c \
../src/tls_dummy.c \
../src/util.c \
../src/uuid.c
LOCAL_STATIC_LIBRARIES := libstrophe
include $(BUILD_STATIC_LIBRARY)
#
# expat
#
include $(CLEAR_VARS)
LOCAL_MODULE := libexpat
LOCAL_CFLAGS := -DHAVE_MEMMOVE -DXML_DEV_URANDOM
#LOCAL_C_INCLUDES := \
# $(LOCAL_PATH)/expat
LOCAL_SRC_FILES := \
../expat/lib/xmlparse.c \
../expat/lib/xmlrole.c \
../expat/lib/xmltok.c \
../expat/lib/xmltok_impl.c \
../expat/lib/xmltok_ns.c
LOCAL_STATIC_LIBRARIES := libexpat
include $(BUILD_STATIC_LIBRARY)

2
jni/Application.mk Normal file
View File

@@ -0,0 +1,2 @@
APP_ABI := armeabi-v7a arm64-v8a
APP_PLATFORM := android-19

14
libstrophe.pc.in Normal file
View File

@@ -0,0 +1,14 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libstrophe
Description: A simple, lightweight C library for writing XMPP clients
URL: https://strophe.im/libstrophe/
Version: @VERSION@
Requires:
Requires.private: @PC_REQUIRES@
Libs: -L${libdir} -lstrophe
Libs.private: @PC_LIBS@
Cflags: -I${includedir} @PC_CFLAGS@

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])])
])

View File

@@ -1,31 +0,0 @@
1. Setup
yum install gcc make expat-devel libstrophe
Fedora:
yum install fedora-packager
cd ~
rpmdev-setuptree
CentOS:
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
Then make a tarball of sources:
tar czf libstrophe_git.tar.gz libstrophe
2. Build
cp libstrophe.spec ~/rpmbuild/SPECS
cp libstrophe_git.tar.gz ~/rpmbuild/SOURCES
rpmbuild -bb ~/rpmbuild/SPECS/libstrophe.spec
3. Install
yum localinstall ~/rpmbuild/RPMS/<arch>/libstrophe-1.1<dist>.<arch>.rpm
Or, if localinstall is not recognised:
rpm -i ~/rpmbuild/RPMS/<arch>/libstrophe-1.1<dist>.<arch>.rpm

View File

@@ -1,32 +0,0 @@
Name: libstrophe
Version: 1
Release: 1%{?dist}_git
Summary: xmpp library in C
Group: Application/System
License: MIT/GPLv3
URL: http://strophe.im/libstrophe/
Source0: libstrophe_git.tar.gz
BuildRequires: expat-devel
Requires: expat
%description
XMPP library in C
%prep
%setup -n libstrophe
./bootstrap.sh
%build
%configure
make %{?_smp_mflags}
%install
make install DESTDIR=%{buildroot}
%files
%{_libdir}/libstrophe.a
%{_includedir}/strophe.h
%changelog

2072
src/auth.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* common.h
** strophe XMPP client library -- internal common structures
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -21,17 +19,47 @@
#include <stdio.h>
#include <stdarg.h>
#ifndef _WIN32
#include <stdint.h>
#endif
#include "strophe.h"
#include "ostypes.h"
#include "sock.h"
#include "tls.h"
#include "hash.h"
#include "util.h"
#include "parser.h"
#include "snprintf.h"
/** handlers **/
typedef struct _xmpp_handlist_t xmpp_handlist_t;
struct _xmpp_handlist_t {
/* common members */
int user_handler;
int (*handler)();
void *userdata;
int enabled; /* handlers are added disabled and enabled after the
* handler chain is processed to prevent stanzas from
* getting processed by newly added handlers */
xmpp_handlist_t *next;
union {
/* timed handlers */
struct {
unsigned long period;
uint64_t last_stamp;
};
/* id handlers */
struct {
char *id;
};
/* normal handlers */
struct {
char *ns;
char *name;
char *type;
};
} u;
};
/** run-time context **/
@@ -49,52 +77,61 @@ 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;
xmpp_connlist_t *connlist;
xmpp_handlist_t *timed_handlers;
unsigned long timeout;
};
/* 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, ...);
/** jid */
/* these return new strings that must be xmpp_free()'d */
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
const char *domain,
const char *resource);
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
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 **/
@@ -105,78 +142,147 @@ 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;
xmpp_send_queue_t *next;
};
typedef struct _xmpp_handlist_t xmpp_handlist_t;
struct _xmpp_handlist_t {
/* common members */
int user_handler;
void *handler;
int wip;
xmpp_send_queue_owner_t owner;
void *userdata;
int enabled; /* handlers are added disabled and enabled after the
* handler chain is processed to prevent stanzas from
* getting processed by newly added handlers */
xmpp_handlist_t *next;
uint32_t sm_h;
union {
/* timed handlers */
struct {
unsigned long period;
uint64_t last_stamp;
};
/* id handlers */
struct {
char *id;
};
/* normal handlers */
struct {
char *ns;
char *name;
char *type;
};
};
xmpp_send_queue_t *prev, *next;
};
#define SASL_MASK_PLAIN 0x01
#define SASL_MASK_DIGESTMD5 0x02
#define SASL_MASK_ANONYMOUS 0x04
#define UNUSED(x) ((void)(x))
typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
#define MAX_DOMAIN_LEN 256
#define SASL_MASK_PLAIN (1 << 0)
#define SASL_MASK_DIGESTMD5 (1 << 1)
#define SASL_MASK_ANONYMOUS (1 << 2)
#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_SCRAM_PLUS \
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_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,
XMPP_PORT_CLIENT_LEGACY_SSL = 5223,
XMPP_PORT_COMPONENT = 5347,
};
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;
int is_raw;
xmpp_conn_state_t state;
uint64_t timeout_stamp;
int error;
xmpp_stream_error_t *stream_error;
sock_t sock;
tls_t *tls;
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;
int tls_support;
int tls_disabled;
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 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;
char *connectdomain;
char *connectport;
char *jid;
char *pass;
char *bound_jid;
@@ -186,6 +292,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;
@@ -196,14 +303,15 @@ struct _xmpp_conn_t {
/* timeouts */
unsigned int connect_timeout;
/* event handlers */
/* event handlers */
/* 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;
void *userdata;
@@ -212,14 +320,16 @@ struct _xmpp_conn_t {
xmpp_handlist_t *timed_handlers;
hash_t *id_handlers;
xmpp_handlist_t *handlers;
xmpp_sockopt_callback sockopt_cb;
};
void conn_disconnect(xmpp_conn_t * const conn);
void conn_disconnect_clean(xmpp_conn_t * const conn);
void conn_open_stream(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,
@@ -232,7 +342,7 @@ struct _xmpp_stanza_t {
xmpp_ctx_t *ctx;
xmpp_stanza_type_t type;
xmpp_stanza_t *prev;
xmpp_stanza_t *next;
xmpp_stanza_t *children;
@@ -244,33 +354,53 @@ 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,
xmpp_timed_handler handler,
const unsigned long period,
void * const userdata);
void handler_add_id(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id,
void * const userdata);
void handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const ns,
const char * const name,
const char * const type,
void * const userdata);
void handler_add_timed(xmpp_conn_t *conn,
xmpp_timed_handler handler,
unsigned long period,
void *userdata);
void handler_add_id(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id,
void *userdata);
void handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
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 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_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);
/* replacement snprintf and vsnprintf */
int xmpp_snprintf (char *str, size_t count, const char *fmt, ...);
int xmpp_vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
/* 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;
}

2084
src/conn.c

File diff suppressed because it is too large Load Diff

509
src/crypto.c Normal file
View File

@@ -0,0 +1,509 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* crypto.c
* strophe XMPP client library -- public interface for digests, encodings
*
* Copyright (C) 2016 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 or GPLv3 licenses.
*/
/** @file
* Public interface for digests and encodings used in XEPs.
*/
/** @defgroup Digests Message digests
*/
/** @defgroup Encodings Encodings
*/
#include <assert.h>
#include <string.h> /* memset, memcpy */
#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, strophe_free */
struct _xmpp_sha1_t {
xmpp_ctx_t *xmpp_ctx;
SHA1_CTX ctx;
uint8_t digest[SHA1_DIGEST_SIZE];
};
static char *digest_to_string(const uint8_t *digest, char *s, size_t len)
{
int i;
if (len < SHA1_DIGEST_SIZE * 2 + 1)
return NULL;
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
strophe_snprintf(s + i * 2, 3, "%02x", digest[i]);
return s;
}
static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
{
char *s;
size_t slen;
slen = SHA1_DIGEST_SIZE * 2 + 1;
s = strophe_alloc(ctx, slen);
if (s) {
s = digest_to_string(digest, s, slen);
assert(s != NULL);
}
return s;
}
/** Compute SHA1 message digest.
* Returns an allocated string which represents SHA1 message digest in
* hexadecimal notation. The string must be freed with xmpp_free().
*
* @param ctx a Strophe context object
* @param data buffer for digest computation
* @param len size of the data buffer
*
* @return an allocated string or NULL on allocation error
*
* @ingroup Digests
*/
char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
{
uint8_t digest[SHA1_DIGEST_SIZE];
crypto_SHA1((const uint8_t *)data, len, digest);
return digest_to_string_alloc(ctx, digest);
}
/** Compute SHA1 message digest.
* Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE
* bytes long.
*
* @param data buffer for digest computation
* @param len size of the data buffer
* @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
*
* @ingroup Digests
*/
void xmpp_sha1_digest(const unsigned char *data,
size_t len,
unsigned char *digest)
{
crypto_SHA1((const uint8_t *)data, len, digest);
}
/** Create new SHA1 object.
* SHA1 object is used to compute SHA1 digest of a buffer that is split
* in multiple chunks or provided in stream mode. A single buffer can be
* processed by short functions xmpp_sha1() and xmpp_sha1_digest().
* Follow the next use-case for xmpp_sha1_t object:
* @code
* xmpp_sha1_t *sha1 = xmpp_sha1_new(ctx);
* // Repeat update for all chunks of data
* xmpp_sha1_update(sha1, data, len);
* xmpp_sha1_final(sha1);
* char *digest = xmpp_sha1_to_string_alloc(sha1);
* xmpp_sha1_free(sha1);
* @endcode
*
* @param ctx a Strophe context object
*
* @return new SHA1 object
*
* @ingroup Digests
*/
xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
{
xmpp_sha1_t *sha1;
sha1 = strophe_alloc(ctx, sizeof(*sha1));
if (sha1) {
memset(sha1, 0, sizeof(*sha1));
crypto_SHA1_Init(&sha1->ctx);
sha1->xmpp_ctx = ctx;
}
return sha1;
}
/** Destroy SHA1 object.
*
* @param sha1 a SHA1 object
*
* @ingroup Digests
*/
void xmpp_sha1_free(xmpp_sha1_t *sha1)
{
strophe_free(sha1->xmpp_ctx, sha1);
}
/** Update SHA1 context with the next portion of data.
* Can be called repeatedly.
*
* @param sha1 a SHA1 object
* @param data pointer to a buffer to be hashed
* @param len size of the data buffer
*
* @ingroup Digests
*/
void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len)
{
crypto_SHA1_Update(&sha1->ctx, data, len);
}
/** Finish SHA1 computation.
* Don't call xmpp_sha1_update() after this function. Retrieve resulting
* message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest().
*
* @param sha1 a SHA1 object
*
* @ingroup Digests
*/
void xmpp_sha1_final(xmpp_sha1_t *sha1)
{
crypto_SHA1_Final(&sha1->ctx, sha1->digest);
}
/** Return message digest rendered as a string.
* Stores the string to a user's buffer and returns the buffer. Call this
* function after xmpp_sha1_final().
*
* @param sha1 a SHA1 object
* @param s output string
* @param slen size reserved for the string including '\0'
*
* @return pointer s or NULL if resulting string is bigger than slen bytes
*
* @ingroup Digests
*/
char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
{
return digest_to_string(sha1->digest, s, slen);
}
/** Return message digest rendered as a string.
* 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
*
* @return an allocated string
*
* @ingroup Digests
*/
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1)
{
return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest);
}
/** Stores message digest to a user's buffer.
*
* @param sha1 a SHA1 object
* @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
*
* @ingroup Digests
*/
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest)
{
assert(SHA1_DIGEST_SIZE == XMPP_SHA1_DIGEST_SIZE);
memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
}
/* Base64 encoding routines. Implemented according to RFC 3548. */
/* map of all byte values to the base64 values, or to
'65' which indicates an invalid character. '=' is '64' */
static const unsigned char _base64_invcharmap[256] = {
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 62, 65, 65, 65, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 65, 65, 65, 64, 65, 65, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 65, 65, 65, 65,
65, 65, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65, 65};
/* map of all 6-bit values to their corresponding byte
in the base64 alphabet. Padding char is the value '64' */
static const char _base64_charmap[65] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'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(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 *buffer, size_t len)
{
size_t clen;
char *cbuf, *c;
uint32_t word, hextet;
size_t i;
clen = base64_encoded_len(len);
cbuf = strophe_alloc(ctx, clen + 1);
if (cbuf != NULL) {
c = cbuf;
/* loop over data, turning every 3 bytes into 4 characters */
for (i = 0; i + 2 < len; i += 3) {
word = buffer[i] << 16 | buffer[i + 1] << 8 | buffer[i + 2];
hextet = (word & 0x00FC0000) >> 18;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x0003F000) >> 12;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x00000FC0) >> 6;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x000003F);
*c++ = _base64_charmap[hextet];
}
/* zero, one or two bytes left */
switch (len - i) {
case 0:
break;
case 1:
hextet = (buffer[len - 1] & 0xFC) >> 2;
*c++ = _base64_charmap[hextet];
hextet = (buffer[len - 1] & 0x03) << 4;
*c++ = _base64_charmap[hextet];
*c++ = _base64_charmap[64]; /* pad */
*c++ = _base64_charmap[64]; /* pad */
break;
case 2:
hextet = (buffer[len - 2] & 0xFC) >> 2;
*c++ = _base64_charmap[hextet];
hextet = ((buffer[len - 2] & 0x03) << 4) |
((buffer[len - 1] & 0xF0) >> 4);
*c++ = _base64_charmap[hextet];
hextet = (buffer[len - 1] & 0x0F) << 2;
*c++ = _base64_charmap[hextet];
*c++ = _base64_charmap[64]; /* pad */
break;
}
/* add a terminal null */
*c = '\0';
}
return cbuf;
}
static size_t base64_decoded_len(const char *buffer, size_t len)
{
size_t nudge = 0;
unsigned char c;
size_t i;
if (len < 4)
return 0;
/* count the padding characters for the remainder */
for (i = len; i > 0; --i) {
c = _base64_invcharmap[(unsigned char)buffer[i - 1]];
if (c < 64)
break;
if (c == 64)
++nudge;
if (c > 64)
return 0;
}
if (nudge > 2)
return 0;
/* decoded steam is 3 bytes for every four */
return 3 * (len >> 2) - nudge;
}
static void base64_decode(xmpp_ctx_t *ctx,
const char *buffer,
size_t len,
unsigned char **out,
size_t *outlen)
{
size_t dlen;
unsigned char *dbuf, *d;
uint32_t word, hextet = 0;
size_t i;
/* len must be a multiple of 4 */
if (len & 0x03)
goto _base64_error;
dlen = base64_decoded_len(buffer, len);
if (dlen == 0)
goto _base64_error;
dbuf = strophe_alloc(ctx, dlen + 1);
if (dbuf != NULL) {
d = dbuf;
/* loop over each set of 4 characters, decoding 3 bytes */
for (i = 0; i + 3 < len; i += 4) {
hextet = _base64_invcharmap[(unsigned char)buffer[i]];
if (hextet & 0xC0)
break;
word = hextet << 18;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 1]];
if (hextet & 0xC0)
break;
word |= hextet << 12;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 2]];
if (hextet & 0xC0)
break;
word |= hextet << 6;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 3]];
if (hextet & 0xC0)
break;
word |= hextet;
*d++ = (word & 0x00FF0000) >> 16;
*d++ = (word & 0x0000FF00) >> 8;
*d++ = (word & 0x000000FF);
}
if (hextet > 64)
goto _base64_decode_error;
/* handle the remainder */
switch (dlen % 3) {
case 0:
/* nothing to do */
break;
case 1:
/* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
if (hextet & 0xC0)
goto _base64_decode_error;
word = hextet << 2;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet >> 4;
*d++ = word & 0xFF;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
if (hextet != 64)
goto _base64_decode_error;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
if (hextet != 64)
goto _base64_decode_error;
break;
case 2:
/* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
if (hextet & 0xC0)
goto _base64_decode_error;
word = hextet << 10;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet << 4;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet >> 2;
*d++ = (word & 0xFF00) >> 8;
*d++ = (word & 0x00FF);
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
if (hextet != 64)
goto _base64_decode_error;
break;
}
*d = '\0';
}
*out = dbuf;
*outlen = dbuf == NULL ? 0 : dlen;
return;
_base64_decode_error:
/* invalid character; abort decoding! */
strophe_free(ctx, dbuf);
_base64_error:
*out = NULL;
*outlen = 0;
}
/** Base64 encoding routine.
* Returns an allocated string which must be freed with xmpp_free().
*
* @param ctx a Strophe context
* @param data buffer to encode
* @param len size of the data buffer
*
* @return an allocated null-terminated string or NULL on error
*
* @ingroup Encodings
*/
char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
{
return base64_encode(ctx, data, len);
}
/** Base64 decoding routine.
* Returns an allocated string which must be freed with xmpp_free(). User
* calls this function when the result must be a string. When decoded buffer
* contains '\0' NULL is returned.
*
* @param ctx a Strophe context
* @param base64 encoded buffer
* @param len size of the buffer
*
* @return an allocated null-terminated string or NULL on error
*
* @ingroup Encodings
*/
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
{
unsigned char *buf = NULL;
size_t buflen;
if (len == 0) {
/* handle empty string */
buf = strophe_alloc(ctx, 1);
if (buf)
buf[0] = '\0';
buflen = 0;
} else {
base64_decode(ctx, base64, len, &buf, &buflen);
}
if (buf) {
if (buflen != strlen((char *)buf)) {
strophe_free(ctx, buf);
buf = NULL;
}
}
return (char *)buf;
}
/** Base64 decoding routine.
* Returns an allocated buffer which must be freed with xmpp_free().
*
* @param ctx a Strophe context
* @param base64 encoded buffer
* @param len size of the encoded buffer
* @param out allocated buffer is stored here
* @param outlen size of the allocated buffer
*
* @note on an error the `*out` will be NULL
*
* @ingroup Encodings
*/
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
const char *base64,
size_t len,
unsigned char **out,
size_t *outlen)
{
base64_decode(ctx, base64, len, out, outlen);
}

314
src/ctx.c
View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* ctx.c
** strophe XMPP client library -- run-time context implementation
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -39,7 +37,7 @@
* result in strange (and platform dependent) behavior.
*
* Specifically, the socket library on Win32 platforms must be initialized
* before use (although this is not the case on POSIX systems). The TLS
* before use (although this is not the case on POSIX systems). The TLS
* subsystem must also seed the random number generator.
*/
@@ -50,17 +48,37 @@
#include "strophe.h"
#include "common.h"
#include "resolver.h"
#include "util.h"
#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.
* This function initializes subcomponents of the Strophe library and must
* be called for Strophe to operate correctly.
*
* @ingroup Init
*/
void xmpp_initialize(void)
void xmpp_initialize(void)
{
sock_initialize();
resolver_initialize();
tls_initialize();
}
@@ -71,6 +89,7 @@
void xmpp_shutdown(void)
{
tls_shutdown();
resolver_shutdown();
sock_shutdown();
}
@@ -89,6 +108,14 @@ void xmpp_shutdown(void)
#define LIBXMPP_VERSION_MINOR (0)
#endif
#ifndef EVENT_LOOP_DEFAULT_TIMEOUT
/** @def EVENT_LOOP_DEFAULT_TIMEOUT
* The default timeout in milliseconds for the event loop.
* This is set to 1 second.
*/
#define EVENT_LOOP_DEFAULT_TIMEOUT 1000
#endif
/** Check that Strophe supports a specific API version.
*
* @param major the major version number
@@ -100,44 +127,41 @@ void xmpp_shutdown(void)
*/
int xmpp_version_check(int major, int minor)
{
return (major == LIBXMPP_VERSION_MAJOR) &&
(minor >= LIBXMPP_VERSION_MINOR);
return (major == LIBXMPP_VERSION_MAJOR) && (minor >= LIBXMPP_VERSION_MINOR);
}
/* We define the global default allocator, logger, and context here. */
/* Wrap stdlib routines malloc, free, and realloc for default memory
* management.
/* 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);
}
/* default memory function map */
static xmpp_mem_t xmpp_default_mem = {
_malloc, /* use the thinly wrapped stdlib routines by default */
_free,
_realloc,
NULL
};
_free, _realloc, NULL};
/* log levels and names */
static const char * const _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};
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};
/** Log a message.
* The default logger writes to stderr.
@@ -148,22 +172,25 @@ static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG,
* @param area the area the log message is for
* @param msg the log message
*/
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;
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
if (level >= filter_level)
fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
}
static const xmpp_log_t _xmpp_default_loggers[] = {
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}
};
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}};
/** Get a default logger with filtering.
* The default logger provides a basic logging setup which writes log
@@ -179,25 +206,25 @@ static const xmpp_log_t _xmpp_default_loggers[] = {
xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
{
/* clamp to the known range */
if (level > XMPP_LEVEL_ERROR) level = XMPP_LEVEL_ERROR;
if (level < XMPP_LEVEL_DEBUG) level = XMPP_LEVEL_DEBUG;
if (level > XMPP_LEVEL_ERROR)
level = XMPP_LEVEL_ERROR;
return (xmpp_log_t*)&_xmpp_default_loggers[level];
return (xmpp_log_t *)&_xmpp_default_loggers[level];
}
static xmpp_log_t xmpp_default_log = { NULL, NULL };
static xmpp_log_t xmpp_default_log = {NULL, NULL};
/* convenience functions for accessing the context */
/** Allocate memory in a Strophe context.
* All Strophe functions will use this to allocate memory.
* All Strophe functions will use this to allocate memory.
*
* @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
*/
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);
}
@@ -208,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.
*
@@ -222,8 +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);
}
@@ -232,8 +268,8 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
* 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
@@ -241,45 +277,67 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
* @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;
buf = smbuf;
va_copy(copy, ap);
ret = xmpp_vsnprintf(buf, 1023, fmt, ap);
if (ret > 1023) {
buf = (char *)xmpp_alloc(ctx, ret + 1);
if (!buf) {
buf = NULL;
xmpp_error(ctx, "log", "Failed allocating memory for log message.");
va_end(copy);
return;
}
oldret = ret;
ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
if (ret > oldret) {
xmpp_error(ctx, "log", "Unexpected error");
return;
}
} else {
va_end(copy);
}
if (!ctx->log->handler)
return;
if (ctx->log->handler)
ctx->log->handler(ctx->log->userdata, level, area, buf);
if (ctx->log->handler == xmpp_default_logger &&
level < *(xmpp_log_level_t *)ctx->log->userdata)
return;
va_copy(copy, ap);
ret = strophe_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
if (ret >= (int)sizeof(smbuf)) {
buf = (char *)strophe_alloc(ctx, ret + 1);
if (!buf) {
buf = NULL;
strophe_error(ctx, "log",
"Failed allocating memory for log message.");
va_end(copy);
return;
}
oldret = ret;
ret = strophe_vsnprintf(buf, ret + 1, fmt, copy);
if (ret > oldret) {
strophe_error(ctx, "log", "Unexpected error");
strophe_free(ctx, buf);
va_end(copy);
return;
}
} else {
buf = smbuf;
}
va_end(copy);
ctx->log->handler(ctx->log->userdata, level, area, buf);
if (buf != smbuf)
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.
* This is a convenience function for writing to the log at the
* ERROR level. It takes a printf-style format string followed by a
* ERROR level. It takes a printf-style format string followed by a
* variable list of arguments for formatting.
*
* @param ctx a Strophe context object
@@ -287,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);
}
@@ -309,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);
}
@@ -331,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);
}
@@ -353,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);
}
@@ -379,29 +455,35 @@ 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;
if (mem == NULL)
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
else
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
if (ctx != NULL) {
if (mem != NULL)
ctx->mem = mem;
else
ctx->mem = &xmpp_default_mem;
memset(ctx, 0, sizeof(xmpp_ctx_t));
if (log == NULL)
ctx->log = &xmpp_default_log;
else
ctx->log = log;
if (mem != NULL)
ctx->mem = mem;
else
ctx->mem = &xmpp_default_mem;
ctx->connlist = NULL;
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
if (log == NULL)
ctx->log = &xmpp_default_log;
else
ctx->log = log;
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
ctx->rand = xmpp_rand_new(ctx);
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
if (ctx->rand == NULL) {
strophe_free(ctx, ctx);
ctx = NULL;
}
}
return ctx;
@@ -413,9 +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_free(ctx, ctx); /* pull the hole in after us */
xmpp_rand_free(ctx, ctx->rand);
strophe_free(ctx, ctx); /* pull the hole in after us */
}
/** Set the verbosity level of a Strophe context.
*
* @param ctx a Strophe context object
* @param level the verbosity level
*
* @ingroup Context
*/
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
{
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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* event.c
** strophe XMPP client library -- event loop and management
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -17,15 +15,15 @@
*/
/** @defgroup EventLoop Event loop
* These functions manage the Strophe event loop.
*
* These functions manage the Strophe event loop.
*
* Simple tools can use xmpp_run() and xmpp_stop() to manage the life
* cycle of the program. A common idiom is to set up a few initial
* event handers, call xmpp_run(), and then respond and react to
* events as they come in. At some point, one of the handlers will
* call xmpp_stop() to quit the event loop which leads to the program
* terminating.
*
*
* More complex programs will have their own event loops, and should
* ensure that xmpp_run_once() is called regularly from there. For
* example, a GUI program will already include an event loop to
@@ -40,25 +38,43 @@
#ifndef _WIN32
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#define _sleep(x) usleep((x)*1000)
#else
#include <winsock2.h>
#ifndef ETIMEDOUT
#define ETIMEDOUT WSAETIMEDOUT
#endif
#ifndef ECONNRESET
#define ECONNRESET WSAECONNRESET
#endif
#ifndef ECONNABORTED
#define ECONNABORTED WSAECONNABORTED
#endif
#define _sleep(x) Sleep(x)
#endif
#include <strophe.h>
#include "strophe.h"
#include "common.h"
#include "parser.h"
#ifndef DEFAULT_TIMEOUT
/** @def DEFAULT_TIMEOUT
* The default timeout in milliseconds for the event loop.
* This is set to 1 millisecond.
*/
#define DEFAULT_TIMEOUT 1
#ifndef STROPHE_MESSAGE_BUFFER_SIZE
/** Max buffer size for receiving messages. */
#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
* xmpp_send and related functions and run through the Strophe even
@@ -72,242 +88,260 @@
*
* @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[4096];
char buf[STROPHE_MESSAGE_BUFFER_SIZE];
uint64_t next;
long usec;
uint64_t usec;
int tls_read_bytes = 0;
if (ctx->loop_status == XMPP_LOOP_QUIT) return;
ctx->loop_status = XMPP_LOOP_RUNNING;
if (ctx->loop_status == XMPP_LOOP_QUIT)
return;
/* send queued data */
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
if (conn->state != XMPP_STATE_CONNECTED) {
connitem = connitem->next;
continue;
}
conn = connitem->conn;
if (conn->state != XMPP_STATE_CONNECTED) {
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);
/* 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(intf);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
/* an error occured */
xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
}
}
if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
/* an error occurred */
strophe_debug(
ctx, "xmpp",
"Send error of pending data occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
goto next_item;
}
}
/* write all data from the send queue to the socket */
sq = conn->send_queue_head;
while (sq) {
towrite = sq->len - sq->written;
/* write all data from the send queue to the socket */
sq = conn->send_queue_head;
while (sq) {
towrite = sq->len - sq->written;
if (conn->tls) {
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
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 */
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
/* an error occured */
conn->error = tls_error(conn->tls);
break;
} else if (ret < towrite) {
/* not all data could be sent now */
if (ret >= 0) sq->written += ret;
break;
}
/* all data for this queue item written, delete and move on */
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--;
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);
}
} else {
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
/* pop the top item */
conn->send_queue_head = sq;
/* if we've sent everything update the tail */
if (!sq)
conn->send_queue_tail = NULL;
}
intf->flush(intf);
if (ret < 0 && !sock_is_recoverable(sock_error())) {
/* an error occured */
conn->error = sock_error();
break;
} else if (ret < towrite) {
/* not all data could be sent now */
if (ret >= 0) sq->written += ret;
break;
}
}
/* all data for this queue item written, delete and move on */
xmpp_free(ctx, sq->data);
tsq = sq;
sq = sq->next;
xmpp_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;
}
/* 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 occured, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
}
connitem = connitem->next;
/* tear down connection on error */
if (conn->error) {
/* FIXME: need to tear down send queues and random other things
* maybe this should be abstracted */
strophe_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn);
}
next_item:
connitem = connitem->next;
}
/* reset parsers if needed */
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
if (connitem->conn->reset_parser)
conn_parser_reset(connitem->conn);
if (connitem->conn->reset_parser)
conn_parser_reset(connitem->conn);
}
/* fire any ready timed handlers, then
make sure we don't wait past the time when timed handlers need
to be called */
/* fire any ready timed handlers, then make sure we don't wait past
the time when timed handlers need to be called */
next = handler_fire_timed(ctx);
usec = ((next < timeout) ? next : timeout) * 1000;
tv.tv_sec = usec / 1000000;
tv.tv_usec = usec % 1000000;
tv.tv_sec = (long)(usec / 1000000);
tv.tv_usec = (long)(usec % 1000000);
FD_ZERO(&rfds);
FD_ZERO(&rfds);
FD_ZERO(&wfds);
/* find events to watch */
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
/* connect has been called and we're waiting for it to complete */
/* connection will give us write or error events */
/* make sure the timeout hasn't expired */
if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
conn->connect_timeout)
FD_SET(conn->sock, &wfds);
else {
conn->error = ETIMEDOUT;
xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
conn_disconnect(conn);
}
break;
case XMPP_STATE_CONNECTED:
FD_SET(conn->sock, &rfds);
break;
case XMPP_STATE_DISCONNECTED:
/* do nothing */
default:
break;
}
/* Check if there is something in the SSL buffer. */
if (conn->tls) {
tls_read_bytes += tls_pending(conn->tls);
}
if (conn->sock > max) max = conn->sock;
conn = connitem->conn;
intf = &conn->intf;
connitem = connitem->next;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
/* connect has been called and we're waiting for it to complete */
/* connection will give us write or error events */
/* make sure the timeout hasn't expired */
if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
conn->connect_timeout)
FD_SET(conn->sock, &wfds);
else {
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:
FD_SET(conn->sock, &rfds);
if (conn->send_queue_len > 0)
FD_SET(conn->sock, &wfds);
break;
case XMPP_STATE_DISCONNECTED:
/* do nothing */
default:
break;
}
/* Check if there is something in the SSL buffer. */
if (conn->tls)
tls_read_bytes += tls_pending(intf);
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
max = conn->sock;
connitem = connitem->next;
}
/* check for events */
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
if (max > 0)
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
else {
if (timeout > 0)
_sleep(timeout);
return;
}
/* select errored */
if (ret < 0) {
if (!sock_is_recoverable(sock_error()))
xmpp_error(ctx, "xmpp", "event watcher internal error %d",
sock_error());
return;
if (!sock_is_recoverable(NULL, sock_error(NULL)))
strophe_error(ctx, "xmpp", "event watcher internal error %d",
sock_error(NULL));
return;
}
/* no events happened */
if (ret == 0 && tls_read_bytes == 0) return;
if (ret == 0 && tls_read_bytes == 0)
return;
/* process events */
connitem = ctx->connlist;
while (connitem) {
conn = connitem->conn;
conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) {
case XMPP_STATE_CONNECTING:
if (FD_ISSET(conn->sock, &wfds)) {
/* connection complete */
switch (conn->state) {
case XMPP_STATE_CONNECTING:
if (FD_ISSET(conn->sock, &wfds)) {
/* connection complete */
/* check for error */
if (sock_connect_error(conn->sock) != 0) {
/* connection failed */
xmpp_debug(ctx, "xmpp", "connection failed");
conn_disconnect(conn);
break;
}
/* check for error */
ret = sock_connect_error(conn->sock);
if (ret != 0) {
/* connection failed */
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");
conn->state = XMPP_STATE_CONNECTED;
strophe_debug(ctx, "xmpp", "connection successful");
conn_established(conn);
}
/* send stream init */
conn_open_stream(conn);
}
break;
case XMPP_STATE_CONNECTED:
if (FD_ISSET(conn->sock, &rfds) || intf->pending(intf)) {
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, 4096);
} else {
ret = sock_read(conn->sock, buf, 4096);
}
ret = intf->read(intf, buf, STROPHE_MESSAGE_BUFFER_SIZE);
if (ret > 0) {
ret = parser_feed(conn->parser, buf, ret);
if (!ret) {
/* parse error, we need to shut down */
/* FIXME */
xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
conn_disconnect(conn);
}
} 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 {
/* return of 0 means socket closed by server */
xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
conn->error = ECONNRESET;
conn_disconnect(conn);
}
}
}
if (ret > 0) {
ret = parser_feed(conn->parser, buf, ret);
if (!ret) {
strophe_debug(ctx, "xmpp", "parse error [%s]", buf);
xmpp_send_error(conn, XMPP_SE_INVALID_XML,
"parse error");
}
} else {
int err = intf->get_error(intf);
if (!intf->error_is_recoverable(intf, err)) {
strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
err);
conn->error = err;
conn_disconnect(conn);
} else if (!conn->tls) {
/* return of 0 means socket closed by server */
strophe_debug(ctx, "xmpp",
"Socket closed by remote host.");
conn->error = ECONNRESET;
conn_disconnect(conn);
}
}
}
break;
case XMPP_STATE_DISCONNECTED:
/* do nothing */
default:
break;
}
break;
case XMPP_STATE_DISCONNECTED:
/* do nothing */
default:
break;
}
connitem = connitem->next;
connitem = connitem->next;
}
/* fire any ready handlers */
@@ -324,14 +358,18 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
*/
void xmpp_run(xmpp_ctx_t *ctx)
{
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED)
return;
ctx->loop_status = XMPP_LOOP_RUNNING;
while (ctx->loop_status == XMPP_LOOP_RUNNING) {
xmpp_run_once(ctx, DEFAULT_TIMEOUT);
xmpp_run_once(ctx, ctx->timeout);
}
xmpp_debug(ctx, "event", "Event loop completed.");
/* make it possible to start event loop again */
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
strophe_debug(ctx, "event", "Event loop completed.");
}
/** Stop the event loop.
@@ -344,8 +382,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;
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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* handler.c
** strophe XMPP client library -- event handler management
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -23,14 +21,42 @@
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <stdint.h>
#else
#include "ostypes.h"
#endif
#include "strophe.h"
#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()
* must handle this situation correctly. Current function helps to avoid
* list corruption in described scenario.
*
* TODO Convert handler lists to double-linked lists. Current implementation
* works for O(n).
*/
static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
{
while (*head) {
if (*head == item) {
*head = item->next;
break;
}
head = &(*head)->next;
}
}
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
@@ -39,93 +65,82 @@
* @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, *prev;
char *id, *ns, *name, *type;
xmpp_handlist_t *item, *next, *head, *head_old;
const char *id, *ns, *name, *type;
int ret;
/* call id handlers */
id = xmpp_stanza_get_id(stanza);
if (id) {
prev = NULL;
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
while (item) {
xmpp_handlist_t *next = item->next;
head = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
/* enable all added handlers */
for (item = head; item; item = item->next)
item->enabled = 1;
if (item->user_handler && !conn->authenticated) {
item = next;
continue;
}
item = head;
while (item) {
/* 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;
}
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
/* handler is one-shot, so delete it */
if (prev)
prev->next = next;
else {
hash_drop(conn->id_handlers, id);
hash_add(conn->id_handlers, id, next);
}
xmpp_free(conn->ctx, item->id);
xmpp_free(conn->ctx, item);
item = NULL;
}
if (item)
prev = item;
item = next;
}
ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
next = item->next;
if (!ret) {
/* handler is one-shot, so delete it */
head_old = head;
_handler_item_remove(&head, item);
if (head != head_old) {
/* replace old value */
hash_add(conn->id_handlers, id, head);
}
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
}
item = next;
}
}
/* call handlers */
ns = xmpp_stanza_get_ns(stanza);
name = xmpp_stanza_get_name(stanza);
type = xmpp_stanza_get_type(stanza);
/* enable all added handlers */
for (item = conn->handlers; item; item = item->next)
item->enabled = 1;
item->enabled = 1;
prev = NULL;
item = conn->handlers;
while (item) {
/* skip newly added handlers */
if (!item->enabled) {
prev = item;
item = item->next;
continue;
}
/* 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;
}
/* don't call user handlers until authentication succeeds */
if (item->user_handler && !conn->authenticated) {
prev = item;
item = item->next;
continue;
}
next = item->next;
if ((!item->u.ns || (ns && strcmp(ns, item->u.ns) == 0) ||
xmpp_stanza_get_child_by_ns(stanza, item->u.ns)) &&
(!item->u.name || (name && strcmp(name, item->u.name) == 0)) &&
(!item->u.type || (type && strcmp(type, item->u.type) == 0))) {
if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) ||
xmpp_stanza_get_child_by_ns(stanza, item->ns)) &&
(!item->name || (name && strcmp(name, item->name) == 0)) &&
(!item->type || (type && strcmp(type, item->type) == 0)))
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
/* handler is one-shot, so delete it */
if (prev)
prev->next = item->next;
else
conn->handlers = item->next;
if (item->ns) xmpp_free(conn->ctx, item->ns);
if (item->name) xmpp_free(conn->ctx, item->name);
if (item->type) xmpp_free(conn->ctx, item->type);
xmpp_free(conn->ctx, item);
item = NULL;
}
if (item) {
prev = item;
item = item->next;
} else if (prev)
item = prev->next;
else
item = conn->handlers;
ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
/* list may be changed during execution of a handler */
next = item->next;
if (!ret) {
/* handler is one-shot, so delete it */
_handler_item_remove(&conn->handlers, item);
_free_handlist_item(conn->ctx, item);
}
}
item = next;
}
}
@@ -136,60 +151,88 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
*
* @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 *handitem, *temp;
int ret, fired;
xmpp_handlist_t *item, *next;
xmpp_conn_t *conn;
uint64_t elapsed, min;
uint64_t timestamp;
int ret;
min = (uint64_t)(-1);
connitem = ctx->connlist;
while (connitem) {
if (connitem->conn->state != XMPP_STATE_CONNECTED) {
connitem = connitem->next;
continue;
}
/* enable all handlers that were added */
for (handitem = connitem->conn->timed_handlers; handitem;
handitem = handitem->next)
handitem->enabled = 1;
conn = connitem->conn;
if (conn->state != XMPP_STATE_CONNECTED) {
connitem = connitem->next;
continue;
}
handitem = connitem->conn->timed_handlers;
while (handitem) {
/* skip newly added handlers */
if (!handitem->enabled) {
handitem = handitem->next;
continue;
}
/* enable all handlers that were added */
for (item = conn->timed_handlers; item; item = item->next)
item->enabled = 1;
/* only fire user handlers after authentication */
if (handitem->user_handler && !connitem->conn->authenticated) {
handitem = handitem->next;
continue;
}
item = conn->timed_handlers;
while (item) {
/* 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;
}
fired = 0;
elapsed = time_elapsed(handitem->last_stamp, time_stamp());
if (elapsed >= handitem->period) {
/* fire! */
fired = 1;
handitem->last_stamp = time_stamp();
ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
} else if (min > (handitem->period - elapsed))
min = handitem->period - elapsed;
temp = handitem;
handitem = handitem->next;
next = item->next;
timestamp = time_stamp();
elapsed = time_elapsed(item->u.last_stamp, timestamp);
if (elapsed >= item->u.period) {
/* fire! */
item->u.last_stamp = timestamp;
ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata);
/* list may be changed during execution of a handler */
next = item->next;
if (!ret) {
/* delete handler if it returned false */
_handler_item_remove(&conn->timed_handlers, item);
strophe_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
/* delete handler if it returned false */
if (fired && !ret)
xmpp_timed_handler_delete(connitem->conn, temp->handler);
}
item = next;
}
connitem = connitem->next;
connitem = connitem->next;
}
/*
* Check timed handlers in context. These handlers fire periodically
* regardless of connections state.
* TODO Reduce copy-paste.
*/
item = ctx->timed_handlers;
while (item) {
next = item->next;
timestamp = time_stamp();
elapsed = time_elapsed(item->u.last_stamp, timestamp);
if (elapsed >= item->u.period) {
/* fire! */
item->u.last_stamp = timestamp;
ret =
((xmpp_global_timed_handler)item->handler)(ctx, item->userdata);
/* list may be changed during execution of a handler */
next = item->next;
if (!ret) {
/* delete handler if it returned false */
_handler_item_remove(&ctx->timed_handlers, item);
strophe_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
item = next;
}
return min;
@@ -207,49 +250,64 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
handitem = conn->timed_handlers;
while (handitem) {
if ((user_only && handitem->user_handler) || !user_only)
handitem->last_stamp = time_stamp();
handitem = handitem->next;
if ((user_only && handitem->user_handler) || !user_only)
handitem->u.last_stamp = time_stamp();
handitem = handitem->next;
}
}
static void _timed_handler_add(xmpp_conn_t * const conn,
xmpp_timed_handler handler,
const unsigned long period,
void * const userdata,
const int user_handler)
static void _timed_handler_add(xmpp_ctx_t *ctx,
xmpp_handlist_t **handlers_list,
xmpp_void_handler handler,
unsigned long period,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item, *tail;
xmpp_handlist_t *item;
/* check if handler is already in the list */
for (item = conn->timed_handlers; item; item = item->next) {
if (item->handler == (void *)handler)
break;
for (item = *handlers_list; item; item = item->next) {
if (item->handler == handler && item->userdata == userdata) {
strophe_warn(ctx, "xmpp", "Timed handler already exists.");
break;
}
}
if (item) return;
if (item)
return;
/* build new item */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item) return;
item = strophe_alloc(ctx, sizeof(xmpp_handlist_t));
if (!item)
return;
item->user_handler = user_handler;
item->handler = (void *)handler;
item->handler = handler;
item->userdata = userdata;
item->enabled = 0;
item->next = NULL;
item->period = period;
item->last_stamp = time_stamp();
item->u.period = period;
item->u.last_stamp = time_stamp();
/* append item to list */
if (!conn->timed_handlers)
conn->timed_handlers = item;
else {
tail = conn->timed_handlers;
while (tail->next)
tail = tail->next;
tail->next = item;
item->next = *handlers_list;
*handlers_list = item;
}
static void _timed_handler_delete(xmpp_ctx_t *ctx,
xmpp_handlist_t **handlers_list,
xmpp_void_handler handler)
{
xmpp_handlist_t *item;
while (*handlers_list) {
item = *handlers_list;
if (item->handler == handler) {
*handlers_list = item->next;
strophe_free(ctx, item);
} else {
handlers_list = &item->next;
}
}
}
@@ -260,72 +318,56 @@ static void _timed_handler_add(xmpp_conn_t * const conn,
*
* @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)
{
xmpp_handlist_t *item, *prev;
if (!conn->timed_handlers) return;
prev = NULL;
item = conn->timed_handlers;
while (item) {
if (item->handler == (void *)handler)
break;
prev = item;
item = item->next;
}
if (item) {
if (prev)
prev->next = item->next;
else
conn->timed_handlers = item->next;
xmpp_free(conn->ctx, item);
}
_timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
}
static void _id_handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id,
void * const userdata, int user_handler)
static void _id_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item, *tail;
/* check if handler is already in the list */
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
while (item) {
if (item->handler == (void *)handler)
break;
item = item->next;
if (item->handler == handler && item->userdata == userdata) {
strophe_warn(conn->ctx, "xmpp", "Id handler already exists.");
break;
}
item = item->next;
}
if (item) return;
if (item)
return;
/* build new item */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item) return;
item = strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item)
return;
item->user_handler = user_handler;
item->handler = (void *)handler;
item->handler = handler;
item->userdata = userdata;
item->enabled = 0;
item->next = NULL;
item->id = xmpp_strdup(conn->ctx, id);
if (!item->id) {
xmpp_free(conn->ctx, item);
return;
item->u.id = strophe_strdup(conn->ctx, id);
if (!item->u.id) {
strophe_free(conn->ctx, item);
return;
}
/* put on list in hash table */
tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
if (!tail)
hash_add(conn->id_handlers, id, item);
hash_add(conn->id_handlers, id, item);
else {
while (tail->next)
tail = tail->next;
tail->next = item;
while (tail->next)
tail = tail->next;
tail->next = item;
}
}
@@ -337,99 +379,102 @@ static void _id_handler_add(xmpp_conn_t * const conn,
*
* @ingroup Handlers
*/
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id)
void xmpp_id_handler_delete(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id)
{
xmpp_handlist_t *item, *prev;
xmpp_handlist_t *item, *prev, *next;
prev = NULL;
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
if (!item) return;
if (!item)
return;
while (item) {
if (item->handler == (void *)handler)
break;
next = item->next;
prev = item;
item = item->next;
}
if (item->handler == handler) {
if (prev)
prev->next = next;
else {
hash_drop(conn->id_handlers, id);
hash_add(conn->id_handlers, id, next);
}
if (item) {
if (prev)
prev->next = item->next;
else {
hash_drop(conn->id_handlers, id);
hash_add(conn->id_handlers, id, item->next);
}
xmpp_free(conn->ctx, item->id);
xmpp_free(conn->ctx, item);
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
item = next;
} else {
prev = item;
item = next;
}
}
}
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,
xmpp_handler handler,
const char * const ns,
const char * const name,
const char * const type,
void * const userdata, int user_handler)
static void _handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *ns,
const char *name,
const char *type,
void *userdata,
int user_handler)
{
xmpp_handlist_t *item, *tail;
/* check if handler already in list */
for (item = conn->handlers; item; item = item->next) {
if (item->handler == (void *)handler)
break;
/* same handler function can process different stanzas and
distinguish them according to userdata. */
if (item->handler == handler && item->userdata == userdata) {
strophe_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
break;
}
}
if (item) return;
if (item)
return;
/* build new item */
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item) return;
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 = (void *)handler;
item->handler = handler;
item->userdata = userdata;
item->enabled = 0;
item->next = NULL;
if (ns) {
item->ns = xmpp_strdup(conn->ctx, ns);
if (!item->ns) {
xmpp_free(conn->ctx, item);
return;
}
} else
item->ns = NULL;
if (name) {
item->name = xmpp_strdup(conn->ctx, name);
if (!item->name) {
if (item->ns) xmpp_free(conn->ctx, item->ns);
xmpp_free(conn->ctx, item);
return;
}
} else
item->name = NULL;
if (type) {
item->type = xmpp_strdup(conn->ctx, type);
if (!item->type) {
if (item->ns) xmpp_free(conn->ctx, item->ns);
if (item->name) xmpp_free(conn->ctx, item->name);
xmpp_free(conn->ctx, item);
}
} else
item->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)
conn->handlers = item;
conn->handlers = item;
else {
tail = conn->handlers;
while (tail->next)
tail = tail->next;
tail->next = item;
tail = conn->handlers;
while (tail->next)
tail = tail->next;
tail->next = item;
}
return;
error_out:
_free_handlist_item(conn->ctx, item);
}
/** Delete a stanza handler.
@@ -439,33 +484,28 @@ 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;
if (!conn->handlers) return;
if (!conn->handlers)
return;
prev = NULL;
item = conn->handlers;
while (item) {
if (item->handler == (void *)handler)
break;
prev = item;
item = item->next;
}
if (item->handler == handler) {
if (prev)
prev->next = item->next;
else
conn->handlers = item->next;
if (item) {
if (prev)
prev->next = item->next;
else
conn->handlers = item->next;
if (item->ns) xmpp_free(conn->ctx, item->ns);
if (item->name) xmpp_free(conn->ctx, item->name);
if (item->type) xmpp_free(conn->ctx, item->type);
xmpp_free(conn->ctx, item);
_free_handlist_item(conn->ctx, item);
item = prev ? prev->next : conn->handlers;
} else {
prev = item;
item = item->next;
}
}
}
@@ -474,7 +514,7 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
* and continue firing regularly after that. Strophe will try its best
* to fire handlers as close to the period times as it can, but accuracy
* will vary depending on the resolution of the event loop.
*
*
* If the handler function returns true, it will be kept, and if it
* returns false, it will be deleted from the list of handlers.
*
@@ -485,12 +525,13 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
*
* @ingroup Handlers
*/
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
xmpp_timed_handler handler,
const unsigned long period,
void * const userdata)
void xmpp_timed_handler_add(xmpp_conn_t *conn,
xmpp_timed_handler handler,
unsigned long period,
void *userdata)
{
_timed_handler_add(conn, handler, period, userdata, 1);
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
userdata, 1);
}
/** Add a timed system handler.
@@ -502,12 +543,13 @@ 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,
xmpp_timed_handler handler,
const unsigned long period,
void * const userdata)
void handler_add_timed(xmpp_conn_t *conn,
xmpp_timed_handler handler,
unsigned long period,
void *userdata)
{
_timed_handler_add(conn, handler, period, userdata, 0);
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
userdata, 0);
}
/** Add an id based stanza handler.
@@ -526,10 +568,10 @@ void handler_add_timed(xmpp_conn_t * const conn,
*
* @ingroup Handlers
*/
void xmpp_id_handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id,
void * const userdata)
void xmpp_id_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id,
void *userdata)
{
_id_handler_add(conn, handler, id, userdata, 1);
}
@@ -543,10 +585,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,
xmpp_handler handler,
const char * const id,
void * const userdata)
void handler_add_id(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id,
void *userdata)
{
_id_handler_add(conn, handler, id, userdata, 0);
}
@@ -573,12 +615,12 @@ void handler_add_id(xmpp_conn_t * const conn,
*
* @ingroup Handlers
*/
void xmpp_handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const ns,
const char * const name,
const char * const type,
void * const userdata)
void xmpp_handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *ns,
const char *name,
const char *type,
void *userdata)
{
_handler_add(conn, handler, ns, name, type, userdata, 1);
}
@@ -594,12 +636,129 @@ 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,
xmpp_handler handler,
const char * const ns,
const char * const name,
const char * const type,
void * const userdata)
void handler_add(xmpp_conn_t *conn,
xmpp_handler handler,
const char *ns,
const char *name,
const char *type,
void *userdata)
{
_handler_add(conn, handler, ns, name, type, userdata, 0);
}
/** Delete all system handlers.
* This function is used to reset conn object before re-connecting.
*
* @param conn a Strophe connection object
*/
void handler_system_delete_all(xmpp_conn_t *conn)
{
xmpp_handlist_t *item, *next, *head, *head_old;
hash_iterator_t *iter;
const char *key, *key2;
/* TODO unify all kinds of handlers and avoid copy-paste below */
item = conn->handlers;
while (item) {
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&conn->handlers, item);
_free_handlist_item(conn->ctx, item);
item = next;
} else
item = item->next;
}
item = conn->timed_handlers;
while (item) {
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&conn->timed_handlers, item);
strophe_free(conn->ctx, item);
item = next;
} else
item = item->next;
}
iter = hash_iter_new(conn->id_handlers);
key = iter == NULL ? NULL : hash_iter_next(iter);
while (key != NULL) {
head = head_old = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
item = head;
while (item) {
if (!item->user_handler) {
next = item->next;
_handler_item_remove(&head, item);
strophe_free(conn->ctx, item->u.id);
strophe_free(conn->ctx, item);
item = next;
} else
item = item->next;
}
/* 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. */
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, key, head);
else
hash_drop(conn->id_handlers, key);
}
key = key2;
}
if (iter)
hash_iter_release(iter);
}
/** Add a global timed handler.
* The handler will fire for the first time once the period has elapsed,
* and continue firing regularly after that. Strophe will try its best
* to fire handlers as close to the period times as it can, but accuracy
* will vary depending on the resolution of the event loop.
*
* The main difference between global and ordinary handlers:
* - Ordinary handler is related to a connection, fires only when the
* connection is in connected state and is removed once the connection is
* destroyed.
* - Global handler fires regardless of connections state and is related to
* a Strophe context.
*
* The handler is executed in context of the respective event loop.
*
* If the handler function returns true, it will be kept, and if it
* returns false, it will be deleted from the list of handlers.
*
* Notice, the same handler pointer may be added multiple times with different
* userdata pointers. However, xmpp_global_timed_handler_delete() deletes
* all occurrences.
*
* @param ctx a Strophe context object
* @param handler a function pointer to a timed handler
* @param period the time in milliseconds between firings
* @param userdata an opaque data pointer that will be passed to the handler
*
* @ingroup Handlers
*/
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler,
unsigned long period,
void *userdata)
{
_timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1);
}
/** Delete a global timed handler.
*
* @param ctx a Strophe context object
* @param handler function pointer to the handler
*
* @ingroup Handlers
*/
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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* hash.c
** strophe XMPP client library -- hash table implementation
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -47,165 +45,177 @@ struct _hash_iterator_t {
hashentry_t *entry;
int index;
};
/** allocate and initialize a new hash table */
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
hash_free_func free)
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 *));
if (result->entries == NULL) {
xmpp_free(ctx, result);
return NULL;
}
memset(result->entries, 0, size * sizeof(hashentry_t *));
result->length = size;
result->entries = strophe_alloc(ctx, size * sizeof(hashentry_t *));
if (result->entries == NULL) {
strophe_free(ctx, result);
return NULL;
}
memset(result->entries, 0, size * sizeof(hashentry_t *));
result->length = size;
result->ctx = ctx;
result->free = free;
result->num_keys = 0;
/* give the caller a reference */
result->ref = 1;
result->ctx = ctx;
result->free = free_func;
result->num_keys = 0;
/* give the caller a reference */
result->ref = 1;
}
return result;
}
/** 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;
int i;
if (table->ref > 1)
table->ref--;
table->ref--;
else {
for (i = 0; i < table->length; i++) {
entry = table->entries[i];
while (entry != NULL) {
next = entry->next;
xmpp_free(ctx, entry->key);
if (table->free) table->free(ctx, entry->value);
xmpp_free(ctx, entry);
entry = next;
}
}
xmpp_free(ctx, table->entries);
xmpp_free(ctx, table);
for (i = 0; i < table->length; i++) {
entry = table->entries[i];
while (entry != NULL) {
next = entry->next;
strophe_free(ctx, entry->key);
if (table->free)
table->free(ctx, entry->value);
strophe_free(ctx, entry);
entry = next;
}
}
strophe_free(ctx, table->entries);
strophe_free(ctx, table);
}
}
/** hash a key for our table lookup */
static int _hash_key(hash_t *table, const char *key)
{
int hash = 0;
int shift = 0;
const char *c = key;
unsigned hash = 0;
unsigned shift = 0;
const unsigned char *c = (const unsigned char *)key;
while (*c != '\0') {
/* assume 32 bit ints */
hash ^= ((int)*c++ << shift);
shift += 8;
if (shift > 24) shift = 0;
}
while (*c != 0) {
/* assume 32 bit ints */
hash ^= ((unsigned)*c++ << shift);
shift += 8;
if (shift > 24)
shift = 0;
}
return hash % (unsigned)table->length;
}
return hash % table->length;
hashentry_t *_hash_entry_find(hash_t *table, const char *key)
{
hashentry_t *entry;
int table_index = _hash_key(table, key);
/* look up the hash entry */
entry = table->entries[table_index];
while (entry != NULL) {
/* traverse the linked list looking for the key */
if (!strcmp(key, entry->key)) {
/* match */
break;
}
entry = entry->next;
}
return entry;
}
/** 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)
{
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry = NULL;
int index = _hash_key(table, key);
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry = NULL;
int table_index = _hash_key(table, key);
/* drop existing entry, if any */
hash_drop(table, key);
/* find and replace existing entry, if any */
entry = _hash_entry_find(table, key);
/* allocate and fill a new entry */
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
if (!entry) return -1;
entry->key = xmpp_strdup(ctx, key);
if (!entry->key) {
xmpp_free(ctx, entry);
return -1;
}
entry->value = data;
/* insert ourselves in the linked list */
/* TODO: this leaks duplicate keys */
entry->next = table->entries[index];
table->entries[index] = entry;
table->num_keys++;
if (entry == NULL) {
/* allocate and fill a new entry */
entry = strophe_alloc(ctx, sizeof(hashentry_t));
if (!entry)
return -1;
entry->key = strophe_strdup(ctx, key);
if (!entry->key) {
strophe_free(ctx, entry);
return -1;
}
/* insert ourselves in the linked list */
entry->next = table->entries[table_index];
table->entries[table_index] = entry;
table->num_keys++;
} else {
if (table->free)
table->free(ctx, entry->value);
}
return 0;
entry->value = data;
return 0;
}
/** look up a key in a hash table */
void *hash_get(hash_t *table, const char *key)
{
hashentry_t *entry;
int index = _hash_key(table, key);
void *result = NULL;
hashentry_t *entry;
/* look up the hash entry */
entry = table->entries[index];
while (entry != NULL) {
/* traverse the linked list looking for the key */
if (!strcmp(key, entry->key)) {
/* match */
result = entry->value;
return result;
}
entry = entry->next;
}
/* no match */
return result;
entry = _hash_entry_find(table, key);
return entry == NULL ? NULL : entry->value;
}
/** delete a key from a hash table */
int hash_drop(hash_t *table, const char *key)
{
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry, *prev;
int index = _hash_key(table, key);
xmpp_ctx_t *ctx = table->ctx;
hashentry_t *entry, *prev;
int table_index = _hash_key(table, key);
/* look up the hash entry */
entry = table->entries[index];
prev = NULL;
while (entry != NULL) {
/* traverse the linked list looking for the key */
if (!strcmp(key, entry->key)) {
/* match, remove the entry */
xmpp_free(ctx, entry->key);
if (table->free) table->free(ctx, entry->value);
if (prev == NULL) {
table->entries[index] = entry->next;
} else {
prev->next = entry->next;
}
xmpp_free(ctx, entry);
table->num_keys--;
return 0;
}
prev = entry;
entry = entry->next;
}
/* no match */
return -1;
/* look up the hash entry */
entry = table->entries[table_index];
prev = NULL;
while (entry != NULL) {
/* traverse the linked list looking for the key */
if (!strcmp(key, entry->key)) {
/* match, remove the entry */
strophe_free(ctx, entry->key);
if (table->free)
table->free(ctx, entry->value);
if (prev == NULL) {
table->entries[table_index] = entry->next;
} else {
prev->next = entry->next;
}
strophe_free(ctx, entry);
table->num_keys--;
return 0;
}
prev = entry;
entry = entry->next;
}
/* no match */
return -1;
}
int hash_num_keys(hash_t *table)
@@ -219,18 +229,17 @@ 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);
iter->entry = NULL;
iter->index = -1;
iter->ref = 1;
iter->table = hash_clone(table);
iter->entry = NULL;
iter->index = -1;
}
return iter;
}
/** release an iterator that is no longer needed */
void hash_iter_release(hash_iterator_t *iter)
{
@@ -238,41 +247,42 @@ void hash_iter_release(hash_iterator_t *iter)
iter->ref--;
if (iter->ref <= 0) {
hash_release(iter->table);
xmpp_free(ctx, iter);
if (iter->ref == 0) { // ref is unsigned!!!
hash_release(iter->table);
strophe_free(ctx, iter);
}
}
/** return the next hash table key from the iterator.
the returned key should not be freed */
const char * hash_iter_next(hash_iterator_t *iter)
const char *hash_iter_next(hash_iterator_t *iter)
{
hash_t *table = iter->table;
hashentry_t *entry = iter->entry;
int i = iter->index + 1;
int i;
/* advance until we find the next entry */
if (entry != NULL) entry = entry->next;
if (entry != NULL)
entry = entry->next;
if (entry == NULL) {
/* we're off the end of list, search for a new entry */
while (i < iter->table->length) {
entry = table->entries[i];
if (entry != NULL) {
iter->index = i;
break;
}
i++;
}
/* we're off the end of list, search for a new entry */
i = iter->index + 1;
while (i < iter->table->length) {
entry = table->entries[i];
if (entry != NULL) {
iter->index = i;
break;
}
i++;
}
}
if ((entry == NULL) || (i >= table->length)) {
/* no more keys! */
return NULL;
if (entry == NULL) {
/* no more keys! */
return NULL;
}
/* remember our current match */
iter->entry = entry;
return entry->key;
}

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* hash.h
** strophe XMPP client library -- hash table interface
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -21,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);
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);
@@ -59,6 +56,6 @@ void hash_iter_release(hash_iterator_t *iter);
/** return the next hash table key from the iterator.
the returned key should not be freed */
const char * hash_iter_next(hash_iterator_t *iter);
const char *hash_iter_next(hash_iterator_t *iter);
#endif /* __LIBXMPPP_HASH_H__ */

158
src/jid.c
View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* jid.c
** strophe XMPP client library -- helper functions for parsing JIDs
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -31,15 +29,19 @@
* @return an allocated string with the full JID or NULL if no domain
* is specified
*/
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
const char *domain,
const char *resource)
char *xmpp_jid_new(xmpp_ctx_t *ctx,
const char *node,
const char *domain,
const char *resource)
{
char *result;
int len,nlen,dlen,rlen;
size_t len, nlen, dlen, rlen;
/* jid must at least have a domain */
if (domain == NULL) return NULL;
if (domain == NULL) {
strophe_error(ctx, "jid", "domainpart missing.");
return NULL;
}
/* accumulate lengths */
dlen = strlen(domain);
@@ -47,26 +49,46 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
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);
result[nlen-1] = '@';
}
if (node != NULL) {
memcpy(result, node, nlen - 1);
result[nlen - 1] = '@';
}
memcpy(result + nlen, domain, dlen);
if (resource != NULL) {
result[nlen+dlen] = '/';
memcpy(result+nlen+dlen+1, resource, rlen - 1);
}
result[nlen+dlen+rlen] = '\0';
if (resource != NULL) {
result[nlen + dlen] = '/';
memcpy(result + nlen + dlen + 1, resource, rlen - 1);
}
result[len] = '\0';
}
return result;
}
/** Create a bare JID from a JID.
*
*
* @param ctx the Strophe context object
* @param jid the JID
*
@@ -75,22 +97,20 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
{
char *result;
const char *c;
size_t len;
c = strchr(jid, '/');
if (c == NULL) return xmpp_strdup(ctx, jid);
result = xmpp_alloc(ctx, c-jid+1);
len = strcspn(jid, "/");
result = strophe_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, jid, c-jid);
result[c-jid] = '\0';
memcpy(result, jid, len);
result[len] = '\0';
}
return result;
}
/** Create a node string from a JID.
*
*
* @param ctx a Strophe context object
* @param jid the JID
*
@@ -99,18 +119,30 @@ 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, '@');
if (c != NULL) {
result = xmpp_alloc(ctx, (c-jid) + 1);
if (result != NULL) {
memcpy(result, jid, (c-jid));
result[c-jid] = '\0';
}
/* Apply the same parsing rules from rfc7622 Section 3.2
* 1. Strip resource
* 2. take part before the '@'
*/
char *resource = strchr(dup_jid, '/');
if (resource != NULL) {
*resource = '\0';
}
c = strchr(dup_jid, '@');
if (c != NULL) {
result = strophe_alloc(ctx, (c - dup_jid) + 1);
if (result != NULL) {
memcpy(result, dup_jid, (c - dup_jid));
result[c - dup_jid] = '\0';
}
}
strophe_free(ctx, dup_jid);
return result;
}
@@ -123,27 +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,*s;
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++;
}
s = strchr(c, '/');
if (s == NULL) {
/* no resource */
s = c + strlen(c);
}
result = xmpp_alloc(ctx, (s-c) + 1);
if (result != NULL) {
memcpy(result, c, (s-c));
result[s-c] = '\0';
result = strophe_strdup(ctx, dup_jid);
}
strophe_free(ctx, dup_jid);
return result;
}
@@ -153,25 +187,13 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
* @param ctx a Strophe context object
* @param jid the JID
*
* @return an allocated string with the resource or NULL if no resource
* @return an allocated string with the resource or NULL if no resource
* is found or an error occurs
*/
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
{
char *result = NULL;
const char *c;
int len;
c = strchr(jid, '/');
if (c != NULL) {
c++;
len = strlen(c);
result = xmpp_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, c, len);
result[len] = '\0';
}
}
return result;
return c != NULL ? strophe_strdup(ctx, c + 1) : NULL;
}

117
src/md5.c
View File

@@ -29,19 +29,21 @@
#include "md5.h"
/* little-endian word access macros */
#define GET_32BIT_LSB_FIRST(cp) \
(((uint32_t)(unsigned char)(cp)[0]) | \
((uint32_t)(unsigned char)(cp)[1] << 8 ) | \
#define GET_32BIT_LSB_FIRST(cp) \
(((uint32_t)(unsigned char)(cp)[0]) | \
((uint32_t)(unsigned char)(cp)[1] << 8) | \
((uint32_t)(unsigned char)(cp)[2] << 16) | \
((uint32_t)(unsigned char)(cp)[3] << 24))
#define PUT_32BIT_LSB_FIRST(cp, value) \
do { \
(cp)[0] = (value) & 0xFF; \
(cp)[1] = ((value) >> 8) & 0xFF; \
(cp)[2] = ((value) >> 16) & 0xFF; \
(cp)[3] = ((value) >> 24) & 0xFF; \
} while(0)
#define PUT_32BIT_LSB_FIRST(cp, value) \
do { \
(cp)[0] = (value)&0xFF; \
(cp)[1] = ((value) >> 8) & 0xFF; \
(cp)[2] = ((value) >> 16) & 0xFF; \
(cp)[3] = ((value) >> 24) & 0xFF; \
} while (0)
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64]);
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
@@ -57,7 +59,7 @@ void MD5Init(struct MD5Context *ctx)
ctx->bits[0] = 0;
ctx->bits[1] = 0;
memset(ctx->in, 0, 64);
memset(ctx->in, 0, 64);
}
/*
@@ -72,33 +74,33 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
t = ctx->bits[0];
if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = ctx->in + t;
unsigned char *p = ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
MD5Transform(ctx->buf, ctx->in, ctx);
buf += t;
len -= t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
MD5Transform(ctx->buf, ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
MD5Transform(ctx->buf, ctx->in, ctx);
buf += 64;
len -= 64;
memcpy(ctx->in, buf, 64);
MD5Transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
@@ -107,7 +109,7 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
@@ -128,31 +130,29 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
MD5Transform(ctx->buf, ctx->in, ctx);
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
MD5Transform(ctx->buf, ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
/* Append length in bits and transform */
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
MD5Transform(ctx->buf, ctx->in, ctx);
MD5Transform(ctx->buf, ctx->in);
PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
#ifndef ASM_MD5
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
@@ -165,28 +165,27 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
/* debugging version: */
/*
#define MD5STEP(f, w, x, y, z, data, s) \
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
w, x, y, z, data, s); \
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
w, x, y, z, data, s); \
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
*/
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
(w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
struct MD5Context *ctx)
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64])
{
register uint32_t a, b, c, d, i;
uint32_t in[16];
for (i = 0; i < 16; i++)
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
a = buf[0];
b = buf[1];
@@ -267,23 +266,3 @@ void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
buf[2] += c;
buf[3] += d;
}
#ifdef DEBUG_MD5
#include <stdio.h>
void MD5DumpBytes(unsigned char *b, int len)
{
int i;
for (i=0; i<len; i++) {
if (i%32==0 && i!=0) {
printf("\n");
}
printf("%02x", b[i]&0xff);
}
printf("\n");
}
#endif /* DEBUG_MD5 */
#endif /* !MD5_ASM */

View File

@@ -11,31 +11,19 @@
#ifndef MD5_H
#define MD5_H
/* we use the uint32_t type from stdint.h
* if it is not available, add a typedef here:
*/
/* make sure the stdint.h types are available */
#if defined(_MSC_VER) /* Microsoft Visual C++ */
typedef unsigned int uint32_t;
#else
#include <stdint.h>
#endif
#include "ostypes.h"
struct MD5Context {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
};
void MD5Init(struct MD5Context *context);
void MD5Update(struct MD5Context *context, unsigned char const *buf,
uint32_t len);
void MD5Update(struct MD5Context *context,
unsigned char const *buf,
uint32_t len);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
void MD5Transform(uint32_t buf[4], const unsigned char in[64],
struct MD5Context *ctx);
#ifdef DEBUG_MD5
void MD5DumpBytes(unsigned char *b, int len);
#endif
#endif /* !MD5_H */

View File

@@ -1,88 +0,0 @@
/* oocontext.cpp
** strophe XMPP client library -- C++ context implementation
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
#include <stdlib.h>
#include "strophe.h"
#include "strophepp.h"
XMPP::Context::Context()
{
m_mem.alloc = callAlloc;
m_mem.realloc = callRealloc;
m_mem.free = callFree;
m_mem.userdata = (void *)this;
m_log.handler = callLog;
m_log.userdata = (void *)this;
m_ctx = ::xmpp_ctx_new(&m_mem, &m_log);
}
XMPP::Context::~Context()
{
::xmpp_ctx_free(m_ctx);
}
void *XMPP::Context::alloc(const size_t size)
{
return ::malloc(size);
}
void *XMPP::Context::realloc(void *p, const size_t size)
{
return ::realloc(p, size);
}
void XMPP::Context::free(void *p)
{
::free(p);
}
void XMPP::Context::log(const xmpp_log_level_t level,
const char * const area,
const char * const msg)
{
/* do nothing by default */
}
xmpp_ctx_t *XMPP::Context::getContext()
{
return m_ctx;
}
void *XMPP::Context::callAlloc(const size_t size, void * const userdata)
{
return reinterpret_cast<Context *>(userdata)->alloc(size);
}
void *XMPP::Context::callRealloc(void *p, const size_t size,
void * const userdata)
{
return reinterpret_cast<Context *>(userdata)->realloc(p, size);
}
void XMPP::Context::callFree(void *p, void * const userdata)
{
reinterpret_cast<Context *>(userdata)->free(p);
}
void XMPP::Context::callLog(void * const userdata,
const xmpp_log_level_t level,
const char * const area,
const char * const msg)
{
reinterpret_cast<Context *>(userdata)->log(level, area, msg);
}

View File

@@ -1,82 +0,0 @@
/* oostanza.cpp
** strophe XMPP client library -- C++ context implementation
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
#include "strophe.h"
#include "strophepp.h"
using namespace XMPP;
void *Stanza::operator new(size_t size, Context *ctx)
{
void *p;
/* we must allocate extra room for the Context object so that the
destructor can access it to free the object. C++ does not allow
us to access normal members in the destructor, so we have to hide
it. This must be prepended as well, since C++ will add stuff to
the end in subclasses. */
p = ctx->alloc(size + sizeof(Context *));
if (!p) return p;
*reinterpret_cast<Context **>(p) = ctx;
p = reinterpret_cast<void *>(reinterpret_cast<char *>(p) +
sizeof(Context *));
return p;
}
void Stanza::operator delete(void *p)
{
Context *ctx;
ctx = *reinterpret_cast<Context **>(reinterpret_cast<char *>(p) - 4);
ctx->free(reinterpret_cast<char *>(p) - 4);
}
Stanza::Stanza(Context *ctx)
{
m_ctx = ctx;
m_stanza = ::xmpp_stanza_new(ctx->getContext());
// TODO: check for errors
}
Stanza::~Stanza()
{
}
Stanza *Stanza::create(Context *ctx)
{
return new (ctx) Stanza(ctx);
}
void Stanza::release()
{
if (::xmpp_stanza_release(m_stanza))
delete this;
}
Stanza *Stanza::clone()
{
::xmpp_stanza_clone(m_stanza);
return this;
}
Stanza *Stanza::copy()
{
// TODO
return NULL;
}

View File

@@ -1,16 +1,14 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* ostypes.h
** strophe XMPP client library -- type definitions for platforms
** strophe XMPP client library -- type definitions for platforms
** without stdint.h
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -20,8 +18,31 @@
#ifndef __LIBSTROPHE_OSTYPES_H__
#define __LIBSTROPHE_OSTYPES_H__
#ifdef _WIN32
typedef unsigned __int64 uint64_t;
#include <stddef.h> /* size_t */
#if defined(_MSC_VER) && _MSC_VER < 1600
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
typedef __int64 int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
#ifndef UINT16_MAX
#define UINT16_MAX ((uint16_t)0xffff)
#endif /* UINT16_MAX */
#ifndef UINT32_MAX
#define UINT32_MAX ((uint32_t)0xffffffff)
#endif /* UINT32_MAX */
#ifndef SIZE_MAX
#define SIZE_MAX UINT32_MAX
#endif /* SIZE_MAX */
#else
#include <stdint.h>
#endif
#endif /* __LIBSTROPHE_OSTYPES_H__ */

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.h
** strophe XMPP client library -- parser structures and functions
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -23,20 +21,17 @@
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_t *parser_new(xmpp_ctx_t *ctx,
parser_start_callback startcb,
parser_end_callback endcb,
parser_stanza_callback stanzacb,
void *userdata);
void parser_free(parser_t * const parser);
void parser_free(parser_t *parser);
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname);
int parser_reset(parser_t *parser);
int parser_feed(parser_t *parser, char *chunk, int len);

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.c
** strophe XMPP client library -- xml parser handlers and utility functions
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -22,10 +20,16 @@
#include <expat.h>
#include <strophe.h>
#include "strophe.h"
#include "common.h"
#include "parser.h"
/* Allocate inner text by this number bytes more. Expat splits string
* "new\nline" into 3 strings: "new" "\n" "line". Expecting this pattern,
* we can leave few bytes in the inner_text for "\n". It should reduce
* number of re-allocations in 2 times for multi-line texts. */
#define INNER_TEXT_PADDING 2
struct _parser_t {
xmpp_ctx_t *ctx;
XML_Parser expat;
@@ -35,65 +39,173 @@ struct _parser_t {
void *userdata;
int depth;
xmpp_stanza_t *stanza;
char *inner_text;
/* number of allocated bytes */
int inner_text_size;
/* excluding terminal '\0' */
int inner_text_used;
};
/* Use the Unit Separator to delimit namespace and name in our XML */
const XML_Char namespace_sep = '\x1F';
/*
* Cached strophe ctx. It is used for memory suite.
* Note, expat doesn't support userdata in memory suite, therefore,
* we can support only one strophe context. If user creates more than one
* context, this module will fallback to default library allocator for all
* contexts other than mem_ctx.
*/
static xmpp_ctx_t *mem_ctx = NULL;
static void *parser_mem_malloc(size_t size)
{
if (mem_ctx != NULL)
return strophe_alloc(mem_ctx, size);
else
return NULL;
}
static void *parser_mem_realloc(void *ptr, size_t size)
{
if (mem_ctx != NULL)
return strophe_realloc(mem_ctx, ptr, size);
else
return NULL;
}
static void parser_mem_free(void *ptr)
{
if (mem_ctx != NULL)
strophe_free(mem_ctx, ptr);
}
static const XML_Memory_Handling_Suite parser_mem_suite = {
.malloc_fcn = &parser_mem_malloc,
.realloc_fcn = &parser_mem_realloc,
.free_fcn = &parser_mem_free,
};
/* return allocated string with the name from a delimited
* namespace/name string */
static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
{
char *result = NULL;
const char *c;
size_t len;
c = strchr(nsname, namespace_sep);
if (c == NULL)
return strophe_strdup(ctx, nsname);
c++;
len = strlen(c);
result = strophe_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, c, len);
result[len] = '\0';
}
return result;
}
/* return allocated string with the namespace from a delimited string */
static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
{
char *result = NULL;
const char *c;
c = strchr(nsname, namespace_sep);
if (c != NULL) {
result = strophe_alloc(ctx, (c - nsname) + 1);
if (result != NULL) {
memcpy(result, nsname, (c - nsname));
result[c - nsname] = '\0';
}
}
return result;
}
static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
{
char *attr;
int i;
if (!attrs) return;
if (!attrs)
return;
for (i = 0; attrs[i]; i += 2) {
xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
/* 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]);
strophe_free(stanza->ctx, attr);
}
}
static void _start_element(void *userdata,
const XML_Char *name,
const XML_Char **attrs)
static void complete_inner_text(parser_t *parser)
{
xmpp_stanza_t *stanza;
if (parser->inner_text) {
/* create and populate stanza */
stanza = xmpp_stanza_new(parser->ctx);
/* FIXME: disconnect on allocation error */
if (stanza) {
xmpp_stanza_set_text(stanza, parser->inner_text);
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
}
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
parser->inner_text_size = 0;
parser->inner_text_used = 0;
}
}
static void
_start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
{
parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *child;
char *ns, *name;
ns = _xml_namespace(parser->ctx, nsname);
name = _xml_name(parser->ctx, nsname);
if (parser->depth == 0) {
/* notify the owner */
if (parser->startcb)
parser->startcb((char *)name, (char **)attrs,
parser->userdata);
parser->startcb(name, (char **)attrs, parser->userdata);
} else {
/* build stanzas at depth 1 */
if (!parser->stanza && parser->depth != 1) {
/* something terrible happened */
/* FIXME: shutdown disconnect */
xmpp_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);
if (!parser->stanza) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(parser->stanza, name);
_set_attributes(parser->stanza, attrs);
} else {
/* starting a child of parser->stanza */
child = xmpp_stanza_new(parser->ctx);
if (!child) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(child, name);
_set_attributes(child, attrs);
/* build stanzas at depth 1 */
if (!parser->stanza && parser->depth != 1) {
/* something terrible happened */
/* FIXME: shutdown disconnect */
strophe_error(parser->ctx, "parser",
"oops, where did our stanza go?");
} else {
child = xmpp_stanza_new(parser->ctx);
if (!child) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(child, name);
_set_attributes(child, attrs);
if (ns)
xmpp_stanza_set_ns(child, ns);
/* add child to parent */
xmpp_stanza_add_child(parser->stanza, child);
/* the child is owned by the toplevel stanza now */
xmpp_stanza_release(child);
/* make child the current stanza */
parser->stanza = child;
}
if (parser->stanza != NULL) {
complete_inner_text(parser);
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
}
parser->stanza = child;
}
}
if (ns)
strophe_free(parser->ctx, ns);
if (name)
strophe_free(parser->ctx, name);
parser->depth++;
}
@@ -108,36 +220,46 @@ static void _end_element(void *userdata, const XML_Char *name)
if (parser->endcb)
parser->endcb((char *)name, parser->userdata);
} else {
if (parser->stanza->parent) {
/* we're finishing a child stanza, so set current to the parent */
parser->stanza = parser->stanza->parent;
} else {
complete_inner_text(parser);
if (parser->stanza->parent) {
/* we're finishing a child stanza, so set current to the parent */
parser->stanza = parser->stanza->parent;
} else {
if (parser->stanzacb)
parser->stanzacb(parser->stanza,
parser->userdata);
xmpp_stanza_release(parser->stanza);
parser->stanza = NULL;
}
parser->stanzacb(parser->stanza, parser->userdata);
xmpp_stanza_release(parser->stanza);
parser->stanza = NULL;
}
}
}
static void _characters(void *userdata, const XML_Char *s, int len)
{
parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *stanza;
char *p;
if (parser->depth < 2) return;
if (parser->depth < 2)
return;
/* create and populate stanza */
stanza = xmpp_stanza_new(parser->ctx);
if (!stanza) {
/* FIXME: allocation error, disconnect */
return;
/* Join all parts to a single resulting string. Stanza is created in
* _start_element() and _end_element(). */
if (parser->inner_text_used + len >= parser->inner_text_size) {
parser->inner_text_size =
parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
p = strophe_realloc(parser->ctx, parser->inner_text,
parser->inner_text_size);
if (p == NULL) {
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
parser->inner_text_used = 0;
parser->inner_text_size = 0;
return;
}
parser->inner_text = p;
parser->inner_text[parser->inner_text_used] = '\0';
}
xmpp_stanza_set_text_with_size(stanza, s, len);
xmpp_stanza_add_child(parser->stanza, stanza);
xmpp_stanza_release(stanza);
parser->inner_text_used += len;
strncat(parser->inner_text, s, len);
}
parser_t *parser_new(xmpp_ctx_t *ctx,
@@ -148,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;
@@ -158,6 +280,9 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
parser->userdata = userdata;
parser->depth = 0;
parser->stanza = NULL;
parser->inner_text = NULL;
parser->inner_text_size = 0;
parser->inner_text_used = 0;
parser_reset(parser);
}
@@ -165,29 +290,73 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
return parser;
}
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);
xmpp_free(parser->ctx, parser);
if (parser->stanza) {
_free_parent_stanza(parser->stanza);
parser->stanza = NULL;
}
if (parser->inner_text) {
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}
strophe_free(parser->ctx, parser);
}
/* shuts down and restarts XML parser. true on success */
int parser_reset(parser_t *parser)
{
if (parser->expat)
XML_ParserFree(parser->expat);
XML_Bool ret;
const XML_Memory_Handling_Suite *mem = NULL;
if (parser->stanza)
xmpp_stanza_release(parser->stanza);
if (parser->expat) {
ret = XML_ParserReset(parser->expat, NULL);
if (ret != XML_TRUE) {
XML_ParserFree(parser->expat);
parser->expat = NULL;
}
} else {
if (mem_ctx == NULL)
mem_ctx = parser->ctx;
if (parser->ctx == mem_ctx)
mem = &parser_mem_suite;
parser->expat = XML_ParserCreate_MM(NULL, mem, &namespace_sep);
}
parser->expat = XML_ParserCreate(NULL);
if (!parser->expat) return 0;
if (parser->stanza) {
_free_parent_stanza(parser->stanza);
parser->stanza = NULL;
}
if (parser->inner_text) {
strophe_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}
if (!parser->expat)
return 0;
parser->depth = 0;
parser->stanza = NULL;
XML_SetUserData(parser->expat, parser);
XML_SetElementHandler(parser->expat, _start_element, _end_element);

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* parser.c
** strophe XMPP client library -- xml parser handlers and utility functions
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -23,7 +21,7 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <strophe.h>
#include "strophe.h"
#include "common.h"
#include "parser.h"
@@ -39,69 +37,154 @@ struct _parser_t {
xmpp_stanza_t *stanza;
};
static void _set_attributes(xmpp_stanza_t *stanza, const xmlChar **attrs)
static void
_set_attributes(xmpp_stanza_t *stanza, int nattrs, const xmlChar **attrs)
{
int i;
int i, len;
char *value;
if (!attrs) return;
if (!attrs)
return;
for (i = 0; attrs[i]; i += 2) {
xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], (const char *)attrs[i+1]);
/* 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 = 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);
strophe_free(stanza->ctx, value);
}
}
}
static void _start_element(void *userdata,
const xmlChar *name, const xmlChar **attrs)
/* SAX2 gives us the attrs in an incredibly inconvenient array,
* convert it to what the start callback is expecting */
static char **
_convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
{
int c, i, o, len;
char *value;
char **ret;
if (!attrs)
return NULL;
ret = strophe_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
if (!ret)
return NULL;
memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *));
for (c = 0; c < nattrs; c++) {
i = c * 5;
o = c * 2;
len = attrs[i + 4] - attrs[i + 3];
value = strophe_alloc(parser->ctx, len + 1);
if (value) {
memcpy(value, attrs[i + 3], len);
value[len] = '\0';
ret[o] = strophe_strdup(parser->ctx, (char *)attrs[i]);
ret[o + 1] = value;
}
}
return ret;
}
static void _free_cbattrs(parser_t *parser, char **attrs)
{
int i;
if (!attrs)
return;
for (i = 0; attrs[i]; i += 2) {
if (attrs[i])
strophe_free(parser->ctx, attrs[i]);
if (attrs[i + 1])
strophe_free(parser->ctx, attrs[i + 1]);
}
strophe_free(parser->ctx, attrs);
}
static void _start_element(void *userdata,
const xmlChar *name,
const xmlChar *prefix,
const xmlChar *uri,
int nnamespaces,
const xmlChar **namespaces,
int nattrs,
int ndefaulted,
const xmlChar **attrs)
{
parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *child;
char **cbattrs;
UNUSED(prefix);
UNUSED(nnamespaces);
UNUSED(namespaces);
UNUSED(ndefaulted);
if (parser->depth == 0) {
/* notify the owner */
if (parser->startcb)
parser->startcb((char *)name, (char **)attrs,
parser->userdata);
if (parser->startcb) {
cbattrs = _convert_attrs(parser, nattrs, attrs);
parser->startcb((char *)name, cbattrs, parser->userdata);
_free_cbattrs(parser, cbattrs);
}
} else {
/* build stanzas at depth 1 */
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?");
} else if (!parser->stanza) {
/* starting a new toplevel stanza */
parser->stanza = xmpp_stanza_new(parser->ctx);
if (!parser->stanza) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(parser->stanza, (char *)name);
_set_attributes(parser->stanza, attrs);
} else {
/* starting a child of conn->stanza */
child = xmpp_stanza_new(parser->ctx);
if (!child) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(child, (char *)name);
_set_attributes(child, attrs);
/* build stanzas at depth 1 */
if (!parser->stanza && parser->depth != 1) {
/* something terrible happened */
/* FIXME: we should probably trigger a disconnect */
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);
if (!parser->stanza) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(parser->stanza, (char *)name);
_set_attributes(parser->stanza, nattrs, attrs);
if (uri)
xmpp_stanza_set_ns(parser->stanza, (char *)uri);
} else {
/* starting a child of conn->stanza */
child = xmpp_stanza_new(parser->ctx);
if (!child) {
/* FIXME: can't allocate, disconnect */
}
xmpp_stanza_set_name(child, (char *)name);
_set_attributes(child, nattrs, attrs);
if (uri)
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);
/* add child to parent */
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
/* make child the current stanza */
parser->stanza = child;
}
/* make child the current stanza */
parser->stanza = child;
}
}
parser->depth++;
}
static void _end_element(void *userdata, const xmlChar *name)
static void _end_element(void *userdata,
const xmlChar *name,
const xmlChar *prefix,
const xmlChar *uri)
{
parser_t *parser = (parser_t *)userdata;
UNUSED(prefix);
UNUSED(uri);
parser->depth--;
if (parser->depth == 0) {
@@ -109,16 +192,15 @@ static void _end_element(void *userdata, const xmlChar *name)
if (parser->endcb)
parser->endcb((char *)name, parser->userdata);
} else {
if (parser->stanza->parent) {
/* we're finishing a child stanza, so set current to the parent */
parser->stanza = parser->stanza->parent;
} else {
if (parser->stanza->parent) {
/* we're finishing a child stanza, so set current to the parent */
parser->stanza = parser->stanza->parent;
} else {
if (parser->stanzacb)
parser->stanzacb(parser->stanza,
parser->userdata);
parser->stanzacb(parser->stanza, parser->userdata);
xmpp_stanza_release(parser->stanza);
parser->stanza = NULL;
}
}
}
}
@@ -128,18 +210,18 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
xmpp_stanza_t *stanza;
/* skip unimportant whitespace, etc */
if (parser->depth < 2) return;
if (parser->depth < 2)
return;
/* create and populate stanza */
stanza = xmpp_stanza_new(parser->ctx);
if (!stanza) {
/* FIXME: allocation error, disconnect */
return;
/* FIXME: allocation error, disconnect */
return;
}
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 */
@@ -151,13 +233,14 @@ 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;
memset(&parser->handlers, 0, sizeof(xmlSAXHandler));
parser->handlers.startElement = _start_element;
parser->handlers.endElement = _end_element;
parser->handlers.initialized = XML_SAX2_MAGIC;
parser->handlers.startElementNs = _start_element;
parser->handlers.endElementNs = _end_element;
parser->handlers.characters = _characters;
parser->startcb = startcb;
parser->endcb = endcb;
@@ -172,12 +255,28 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
return parser;
}
char *parser_attr_name(xmpp_ctx_t *ctx, char *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 */
void parser_free(parser_t *parser)
{
if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx);
xmpp_free(parser->ctx, parser);
if (parser->stanza)
_free_parent_stanza(parser->stanza);
strophe_free(parser->ctx, parser);
}
/* shuts down and restarts XML parser. true on success */
@@ -185,28 +284,22 @@ int parser_reset(parser_t *parser)
{
if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx);
if (parser->stanza)
_free_parent_stanza(parser->stanza);
if (parser->stanza)
xmpp_stanza_release(parser->stanza);
parser->xmlctx = xmlCreatePushParserCtxt(&parser->handlers,
parser, NULL, 0, NULL);
if (!parser->xmlctx) return 0;
parser->depth = 0;
parser->stanza = NULL;
parser->depth = 0;
return 1;
parser->xmlctx =
xmlCreatePushParserCtxt(&parser->handlers, parser, NULL, 0, NULL);
return parser->xmlctx ? 1 : 0;
}
/* feed a chunk of data to the parser */
int parser_feed(parser_t *parser, char *chunk, int len)
{
/* xmlParseChunk API returns 0 on success which is opposite logic to
/* xmlParseChunk API returns 0 on success which is opposite logic to
the status returned by parser_feed */
if(!xmlParseChunk(parser->xmlctx, chunk, len, 0)) {
return 1;
} else {
return 0;
}
return !xmlParseChunk(parser->xmlctx, chunk, len, 0);
}

388
src/rand.c Normal file
View File

@@ -0,0 +1,388 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* rand.c
* 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 or GPLv3 licenses.
*/
/** @file
* Pseudo-random number generator.
*
* Implemented Hash_DRBG mechanism according to NIST SP 800-90A.
* Hash function is SHA1.
*/
/** @defgroup Random Pseudo-random number generator
*/
#include <assert.h>
#include <string.h> /* memeset */
#include <time.h> /* clock, time */
#if !defined(_WIN32)
#include <unistd.h>
#endif
#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)
#define reseed_interval 0x7fffffff
/* maximum number of bytes that can be generated per call */
#define GENERATE_MAX (outlen * 10)
#define ENTROPY_MAX 128
#define NONCE_MAX 8
#define RESEED_NEEDED (-1)
struct Hash_DRBG_CTX_struc {
uint8_t V[seedlen];
uint8_t C[seedlen];
uint32_t reseed_counter;
};
typedef struct Hash_DRBG_CTX_struc Hash_DRBG_CTX;
struct _xmpp_rand_t {
int inited;
unsigned reseed_count;
Hash_DRBG_CTX ctx;
};
/* returns smallest number mupliple of y that not less than x */
#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))
/* adds two arrays as numbers in big-endian representation and stores
* result in the first one.
*/
static void
arr_add(uint8_t *arr1, size_t arr1_len, uint8_t *arr2, size_t arr2_len)
{
size_t i;
uint32_t acc;
uint32_t carry = 0;
assert(arr1_len >= arr2_len);
for (i = 1; (i <= arr2_len) || (carry != 0 && i <= arr1_len); ++i) {
acc = (uint32_t)arr1[arr1_len - i] + carry;
if (i <= arr2_len)
acc += (uint32_t)arr2[arr2_len - i];
carry = acc >> 8;
arr1[arr1_len - i] = (uint8_t)(acc & 0xff);
}
}
/* stores 32-bit number in big-endian representation */
static void store_be32(uint32_t val, uint8_t be[4])
{
be[0] = (uint8_t)((val >> 24) & 0xff);
be[1] = (uint8_t)((val >> 16) & 0xff);
be[2] = (uint8_t)((val >> 8) & 0xff);
be[3] = (uint8_t)(val & 0xff);
}
static void Hash_df(uint8_t *input_string,
size_t input_string_len,
uint8_t *output_string,
size_t no_of_bytes_to_return)
{
uint8_t counter;
uint8_t temp[round_up(seedlen, outlen)];
uint8_t conj[ENTROPY_MAX + NONCE_MAX + seedlen + 6];
size_t len;
size_t i;
size_t offset;
assert(no_of_bytes_to_return <= sizeof(temp));
assert(input_string_len + 5 <= sizeof(conj));
len = div_round_up(no_of_bytes_to_return, outlen);
for (i = 1; i <= len; ++i) {
offset = (i - 1) * outlen;
counter = (uint8_t)i;
conj[0] = counter;
store_be32((uint32_t)no_of_bytes_to_return * 8, conj + 1);
memcpy(conj + 5, input_string, input_string_len);
crypto_SHA1(conj, input_string_len + 5, temp + offset);
}
memcpy(output_string, temp, no_of_bytes_to_return);
}
/* assume personalization_string is zero length string */
static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
uint8_t *entropy_input,
size_t entropy_input_len,
uint8_t *nonce,
size_t nonce_len)
{
uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
uint8_t seed0[seedlen + 1];
uint8_t *seed = seed0 + 1;
assert(entropy_input_len <= ENTROPY_MAX);
assert(nonce_len <= NONCE_MAX);
assert(nonce != NULL || nonce_len == 0);
memcpy(seed_material, entropy_input, entropy_input_len);
if (nonce != NULL)
memcpy(seed_material + entropy_input_len, nonce, nonce_len);
Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
seed0[0] = 0;
memcpy(ctx->V, seed, seedlen);
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
ctx->reseed_counter = 1;
}
/* assume additional_input is zero length string */
static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
uint8_t *entropy_input,
size_t entropy_input_len)
{
uint8_t seed_material[1 + seedlen + ENTROPY_MAX];
uint8_t seed0[seedlen + 1];
uint8_t *seed = seed0 + 1;
assert(entropy_input_len <= ENTROPY_MAX);
seed_material[0] = 1;
memcpy(seed_material + 1, ctx->V, seedlen);
memcpy(seed_material + 1 + seedlen, entropy_input, entropy_input_len);
Hash_df(seed_material, entropy_input_len + seedlen + 1, seed, seedlen);
seed0[0] = 0;
memcpy(ctx->V, seed, seedlen);
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
ctx->reseed_counter = 1;
}
static void
Hashgen(uint8_t *V, uint8_t *output, size_t requested_number_of_bytes)
{
uint8_t data[seedlen];
uint8_t W[GENERATE_MAX];
uint8_t i1 = 1;
size_t m;
size_t i;
size_t offset;
assert(requested_number_of_bytes <= sizeof(W));
m = div_round_up(requested_number_of_bytes, outlen);
memcpy(data, V, seedlen);
for (i = 1; i <= m; ++i) {
offset = (i - 1) * outlen;
crypto_SHA1(data, seedlen, W + offset);
/* increase data by 1 */
arr_add(data, sizeof(data), &i1, 1);
}
memcpy(output, W, requested_number_of_bytes);
}
/* assume additional_input is zero length string */
static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx,
uint8_t *output,
size_t requested_number_of_bytes)
{
uint8_t H[outlen];
uint8_t V3[seedlen + 1];
uint8_t reseed_counter[4];
if (ctx->reseed_counter > reseed_interval || ctx->reseed_counter == 0)
return RESEED_NEEDED;
Hashgen(ctx->V, output, requested_number_of_bytes);
V3[0] = 3;
memcpy(V3 + 1, ctx->V, seedlen);
crypto_SHA1(V3, sizeof(V3), H);
arr_add(ctx->V, sizeof(ctx->V), ctx->C, sizeof(ctx->C));
arr_add(ctx->V, sizeof(ctx->V), H, sizeof(H));
store_be32(ctx->reseed_counter, reseed_counter);
arr_add(ctx->V, sizeof(ctx->V), reseed_counter, sizeof(reseed_counter));
++ctx->reseed_counter;
return 0;
}
#define ENTROPY_ACCUMULATE(ptr, last, type, arg) \
do { \
type __arg = (type)(arg); \
if ((char *)ptr + sizeof(__arg) < (char *)last) { \
*(type *)ptr = __arg; \
ptr = (void *)((char *)ptr + sizeof(__arg)); \
} \
} while (0)
static void xmpp_rand_reseed(xmpp_rand_t *rand)
{
uint8_t entropy[ENTROPY_MAX];
uint8_t *ptr = entropy;
const uint8_t *last = entropy + sizeof(entropy);
size_t len;
/* entropy:
* 1. time_stamp()
* 2. clock(3)
* 3. xmpp_rand_t address to make unique seed within one process
* 4. counter to make unique seed within one context
* 5. stack address
* 6. local ports of every connection in list (getsockname)
* 7. other non-constant info that can be retieved from socket
*
* rand(3) can't be used as it isn't thread-safe.
* XXX 6 and 7 are not implemented yet.
*/
ENTROPY_ACCUMULATE(ptr, last, uint64_t, time_stamp());
ENTROPY_ACCUMULATE(ptr, last, clock_t, clock());
ENTROPY_ACCUMULATE(ptr, last, void *, rand);
ENTROPY_ACCUMULATE(ptr, last, unsigned, ++rand->reseed_count);
ENTROPY_ACCUMULATE(ptr, last, void *, &entropy);
len = ptr - entropy;
if (rand->inited) {
Hash_DRBG_Reseed(&rand->ctx, entropy, len);
} else {
Hash_DRBG_Instantiate(&rand->ctx, entropy, len, NULL, 0);
rand->inited = 1;
}
}
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
{
xmpp_rand_t *out = strophe_alloc(ctx, sizeof(*out));
if (out != NULL) {
memset(out, 0, sizeof(*out));
}
return out;
}
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
{
strophe_free(ctx, rand);
}
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
{
int rc;
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;
xmpp_rand_bytes(rand, (unsigned char *)&result, sizeof(result));
return result;
}
static void rand_byte2hex(unsigned char byte, char *hex)
{
static const char hex_tbl[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
hex[0] = hex_tbl[(byte >> 4) & 0x0f];
hex[1] = hex_tbl[byte & 0x0f];
}
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len)
{
size_t i;
const size_t rand_len = len / 2;
/*
* We don't want to use any allocation here, because this function
* can't fail. Also we want to avoid VLA.
* Current implementation uses half of the output buffer for random buffer
* generation and then converts it to HEX representation.
*/
if (rand_len > 0) {
xmpp_rand_bytes(rand, (unsigned char *)output, rand_len);
for (i = rand_len; i > 0; --i)
rand_byte2hex(output[i - 1], &output[(i - 1) * 2]);
}
if (len > 0)
output[len - 1] = '\0';
}

1004
src/resolver.c Normal file

File diff suppressed because it is too large Load Diff

84
src/resolver.h Normal file
View File

@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* resolver.h
* strophe XMPP client library -- DNS resolver
*
* Copyright (C) 2015 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 or GPLv3 licenses.
*/
/** @file
* DNS resolver.
*/
#ifndef __LIBSTROPHE_RESOLVER_H__
#define __LIBSTROPHE_RESOLVER_H__
#include "ostypes.h"
#include "common.h"
typedef enum {
XMPP_DOMAIN_NOT_FOUND,
XMPP_DOMAIN_FOUND,
XMPP_DOMAIN_ALTDOMAIN
} xmpp_domain_state_t;
typedef struct resolver_srv_rr_struc {
uint16_t priority;
uint16_t weight;
uint16_t port;
char target[MAX_DOMAIN_LEN];
struct resolver_srv_rr_struc *next;
} resolver_srv_rr_t;
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.
*
* @param ctx a Strophe context object
* @param buf message in RFC1035 format
* @param len length of the message
* @param srv_rr_list is the result
*
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
*/
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
const unsigned char *buf,
size_t len,
resolver_srv_rr_t **srv_rr_list);
/** Resolve SRV record.
*
* @param ctx a Strophe context object
* @param service service of the SRV record
* @param proto protocol of the SRV record
* @param domain resolving domain
* @param srv_rr_list is the result
*
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
*/
int resolver_srv_lookup(xmpp_ctx_t *ctx,
const char *service,
const char *proto,
const char *domain,
resolver_srv_rr_t **srv_rr_list);
/** Release a list returned by resolver_srv_lookup() or
* resolver_srv_lookup_buf().
*
* @param ctx a Strophe context object
* @param srv_rr_list a list allocated by lookup functions
*/
void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list);
#endif /* __LIBSTROPHE_RESOLVER_H__ */

View File

@@ -1,63 +1,62 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sasl.c
** strophe XMPP client library -- SASL authentication helpers
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
* SASL authentication.
*/
#include <stdlib.h>
#include <string.h>
#include "strophe.h"
#include "common.h"
#include "ostypes.h"
#include "sasl.h"
#include "md5.h"
#include "scram.h"
#include "util.h"
/* make sure the stdint.h types are available */
#if defined(_MSC_VER) /* Microsoft Visual C++ */
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
typedef __int64 int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
/* no uint64_t */
/* strtok_s() has appeared in visual studio 2005.
Use own implementation for older versions. */
#ifdef _MSC_VER
#if (_MSC_VER >= 1400)
#define strtok_r strtok_s
#else
#include <stdint.h>
#define strtok_r xmpp_strtok_r
#endif
#endif /* _MSC_VER */
/** generate authentication string for the SASL PLAIN mechanism */
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
int idlen, passlen;
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
{
size_t idlen, passlen;
size_t msglen;
char *result = NULL;
char *msg;
/* our message is Base64(authzid,\0,authid,\0,password)
if there is no authzid, that field is left empty */
idlen = strlen(authid);
passlen = strlen(password);
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
msglen = 2 + idlen + passlen;
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 = base64_encode(ctx, (unsigned char *)msg, 2 + idlen + passlen);
xmpp_free(ctx, msg);
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);
strophe_free(ctx, msg);
}
return result;
@@ -66,14 +65,14 @@ 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';
memcpy(result, s, len);
result[len] = '\0';
}
return result;
}
@@ -82,14 +81,14 @@ static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
{
char *result;
int len = strlen(s);
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);
result[len+1] = '"';
result[len+2] = '\0';
result[0] = '"';
memcpy(result + 1, s, len);
result[len + 1] = '"';
result[len + 2] = '\0';
}
return result;
}
@@ -102,55 +101,60 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
char *key, *value;
unsigned char *s, *t;
text = base64_decode(ctx, msg, strlen(msg));
text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
if (text == NULL) {
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
return NULL;
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') {
/* skip any leading commas and spaces */
while ((*s == ',') || (*s == ' ')) s++;
/* accumulate a key ending at '=' */
t = s;
while ((*t != '=') && (*t != '\0')) t++;
if (*t == '\0') break; /* bad string */
key = _make_string(ctx, (char *)s, (t-s));
if (key == NULL) break;
s = text;
while (*s != '\0') {
/* skip any leading commas and spaces */
while ((*s == ',') || (*s == ' '))
s++;
/* accumulate a key ending at '=' */
t = s;
while ((*t != '=') && (*t != '\0'))
t++;
if (*t == '\0')
break; /* bad string */
key = _make_string(ctx, (char *)s, (t - s));
if (key == NULL)
break;
/* advance our start pointer past the key */
s = t + 1;
t = s;
/* if we see quotes, grab the string in between */
if ((*s == '\'') || (*s == '"')) {
t++;
while ((*t != *s) && (*t != '\0'))
t++;
value = _make_string(ctx, (char *)s+1, (t-s-1));
if (*t == *s) {
s = t + 1;
} else {
s = t;
}
/* otherwise, accumulate a value ending in ',' or '\0' */
} else {
while ((*t != ',') && (*t != '\0')) t++;
value = _make_string(ctx, (char *)s, (t-s));
s = t;
}
if (value == NULL) {
xmpp_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);
}
s = t + 1;
t = s;
/* if we see quotes, grab the string in between */
if ((*s == '\'') || (*s == '"')) {
t++;
while ((*t != *s) && (*t != '\0'))
t++;
value = _make_string(ctx, (char *)s + 1, (t - s - 1));
if (*t == *s) {
s = t + 1;
} else {
s = t;
}
/* otherwise, accumulate a value ending in ',' or '\0' */
} else {
while ((*t != ',') && (*t != '\0'))
t++;
value = _make_string(ctx, (char *)s, (t - s));
s = t;
}
if (value == NULL) {
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 */
strophe_free(ctx, key);
}
}
xmpp_free(ctx, text);
strophe_free(ctx, text);
return result;
}
@@ -162,89 +166,97 @@ static void _digest_to_hex(const char *digest, char *hex)
const char hexdigit[] = "0123456789abcdef";
for (i = 0; i < 16; i++) {
*hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
*hex++ = hexdigit[ digest[i] & 0x0F ];
*hex++ = hexdigit[(digest[i] >> 4) & 0x0F];
*hex++ = hexdigit[digest[i] & 0x0F];
}
}
/** append 'key="value"' to a buffer, growing as necessary */
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
char *buf, int *len, int quote)
static char *
_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
{
int olen,nlen;
int olen, nlen;
int keylen, valuelen;
const char *value, *qvalue;
char *c;
/* allocate a zero-length string if necessary */
if (buf == NULL) {
buf = xmpp_alloc(ctx, 1);
buf[0] = '\0';
buf = strophe_alloc(ctx, 1);
buf[0] = '\0';
}
if (buf == NULL) return NULL;
if (buf == NULL)
return NULL;
/* get current string length */
olen = strlen(buf);
value = hash_get(table, key);
if (value == NULL) {
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
value = "";
strophe_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
value = "";
}
if (quote) {
qvalue = _make_quoted(ctx, value);
qvalue = _make_quoted(ctx, value);
} else {
qvalue = value;
qvalue = value;
}
/* added length is key + '=' + value */
/* (+ ',' if we're not the first entry */
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;
if (olen) *c++ = ',';
memcpy(c, key, keylen); c += keylen;
*c++ = '=';
memcpy(c, qvalue, valuelen); c += valuelen;
*c++ = '\0';
c = buf + olen;
if (olen)
*c++ = ',';
memcpy(c, key, keylen);
c += keylen;
*c++ = '=';
memcpy(c, qvalue, valuelen);
c += valuelen;
*c++ = '\0';
}
if (quote) xmpp_free(ctx, (char *)qvalue);
if (quote)
strophe_free(ctx, (char *)qvalue);
return buf;
}
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
const char *jid, const char *password) {
char *sasl_digest_md5(xmpp_ctx_t *ctx,
const char *challenge,
const char *jid,
const char *password)
{
hash_t *table;
char *result = NULL;
char *node, *domain, *realm;
char *value;
char *response;
int rlen;
struct MD5Context MD5;
unsigned char digest[16], HA1[16], HA2[16];
char hex[32];
char cnonce[13];
/* our digest response is
Hex( KD( HEX(MD5(A1)),
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
))
/* our digest response is
Hex( KD( HEX(MD5(A1)),
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
))
where KD(k, s) = MD5(k ':' s),
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
A2 = "AUTHENTICATE" ':' "xmpp/" domain
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
A2 = "AUTHENTICATE" ':' "xmpp/" domain
If there is an authzid it is ':'-appended to A1 */
/* parse the challenge */
table = _parse_digest_challenge(ctx, challenge);
if (table == NULL) {
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
return NULL;
strophe_error(ctx, "SASL", "couldn't parse digest challenge");
return NULL;
}
node = xmpp_jid_node(ctx, jid);
@@ -254,22 +266,23 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
server */
realm = hash_get(table, "realm");
if (realm == NULL || strlen(realm) == 0) {
hash_add(table, "realm", xmpp_strdup(ctx, domain));
realm = hash_get(table, "realm");
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));
/* TODO: generate a random cnonce */
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
hash_add(table, "username", strophe_strdup(ctx, node));
xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
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", 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';
memcpy(value + 5, domain, strlen(domain));
value[5 + strlen(domain)] = '\0';
hash_add(table, "digest-uri", value);
/* generate response */
/* construct MD5(node : realm : password) */
@@ -302,8 +315,8 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
value = hash_get(table, "digest-uri");
MD5Update(&MD5, (unsigned char *)value, strlen(value));
if (strcmp(hash_get(table, "qop"), "auth") != 0) {
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
33);
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
33);
}
MD5Final(digest, &MD5);
@@ -330,7 +343,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
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';
@@ -338,278 +351,142 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
/* construct reply */
result = NULL;
rlen = 0;
result = _add_key(ctx, table, "username", result, &rlen, 1);
result = _add_key(ctx, table, "realm", result, &rlen, 1);
result = _add_key(ctx, table, "nonce", result, &rlen, 1);
result = _add_key(ctx, table, "cnonce", result, &rlen, 1);
result = _add_key(ctx, table, "nc", result, &rlen, 0);
result = _add_key(ctx, table, "qop", result, &rlen, 0);
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1);
result = _add_key(ctx, table, "response", result, &rlen, 0);
result = _add_key(ctx, table, "charset", result, &rlen, 0);
xmpp_free(ctx, node);
xmpp_free(ctx, domain);
result = _add_key(ctx, table, "username", result, 1);
result = _add_key(ctx, table, "realm", result, 1);
result = _add_key(ctx, table, "nonce", result, 1);
result = _add_key(ctx, table, "cnonce", result, 1);
result = _add_key(ctx, table, "nc", result, 0);
result = _add_key(ctx, table, "qop", result, 0);
result = _add_key(ctx, table, "digest-uri", result, 1);
result = _add_key(ctx, table, "response", result, 0);
result = _add_key(ctx, table, "charset", result, 0);
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 = base64_encode(ctx, (unsigned char *)result, strlen(result));
xmpp_free(ctx, result);
response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result));
strophe_free(ctx, result);
return response;
}
/** Base64 encoding routines. Implemented according to RFC 3548 */
/** map of all byte values to the base64 values, or to
'65' which indicates an invalid character. '=' is '64' */
static const char _base64_invcharmap[256] = {
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
};
/** map of all 6-bit values to their corresponding byte
in the base64 alphabet. Padding char is the value '64' */
static const char _base64_charmap[65] = {
'A','B','C','D', 'E','F','G','H',
'I','J','K','L', 'M','N','O','P',
'Q','R','S','T', 'U','V','W','X',
'Y','Z','a','b', 'c','d','e','f',
'g','h','i','j', 'k','l','m','n',
'o','p','q','r', 's','t','u','v',
'w','x','y','z', '0','1','2','3',
'4','5','6','7', '8','9','+','/',
'='
};
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len)
/** 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,
const char *password)
{
/* encoded steam is 4 bytes for every three, rounded up */
return ((len + 2)/3) << 2;
}
uint8_t key[SCRAM_DIGEST_SIZE];
uint8_t sign[SCRAM_DIGEST_SIZE];
char *r = NULL;
char *s = NULL;
char *i = NULL;
unsigned char *sval;
size_t sval_len;
long ival;
char *tmp;
char *ptr;
char *saveptr = NULL;
char *response;
char *auth;
char *response_b64;
char *sign_b64;
char *result = NULL;
size_t response_len;
size_t auth_len;
int l;
char *base64_encode(xmpp_ctx_t *ctx,
const unsigned char * const buffer, const unsigned len)
{
int clen;
char *cbuf, *c;
uint32_t word, hextet;
int i;
UNUSED(jid);
clen = base64_encoded_len(ctx, len);
cbuf = xmpp_alloc(ctx, clen + 1);
if (cbuf != NULL) {
c = cbuf;
/* loop over data, turning every 3 bytes into 4 characters */
for (i = 0; i < len - 2; i += 3) {
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
hextet = (word & 0x00FC0000) >> 18;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x0003F000) >> 12;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x00000FC0) >> 6;
*c++ = _base64_charmap[hextet];
hextet = (word & 0x000003F);
*c++ = _base64_charmap[hextet];
}
/* zero, one or two bytes left */
switch (len - i) {
case 0:
break;
case 1:
hextet = (buffer[len-1] & 0xFC) >> 2;
*c++ = _base64_charmap[hextet];
hextet = (buffer[len-1] & 0x03) << 4;
*c++ = _base64_charmap[hextet];
*c++ = _base64_charmap[64]; /* pad */
*c++ = _base64_charmap[64]; /* pad */
break;
case 2:
hextet = (buffer[len-2] & 0xFC) >> 2;
*c++ = _base64_charmap[hextet];
hextet = ((buffer[len-2] & 0x03) << 4) |
((buffer[len-1] & 0xF0) >> 4);
*c++ = _base64_charmap[hextet];
hextet = (buffer[len-1] & 0x0F) << 2;
*c++ = _base64_charmap[hextet];
*c++ = _base64_charmap[64]; /* pad */
break;
}
/* add a terminal null */
*c = '\0';
tmp = strophe_strdup(ctx, challenge);
if (!tmp) {
return NULL;
}
return cbuf;
}
int base64_decoded_len(xmpp_ctx_t *ctx,
const char * const buffer, const unsigned len)
{
int nudge;
int c;
/* count the padding characters for the remainder */
nudge = -1;
c = _base64_invcharmap[(int)buffer[len-1]];
if (c < 64) nudge = 0;
else if (c == 64) {
c = _base64_invcharmap[(int)buffer[len-2]];
if (c < 64) nudge = 1;
else if (c == 64) {
c = _base64_invcharmap[(int)buffer[len-3]];
if (c < 64) nudge = 2;
}
}
if (nudge < 0) return 0; /* reject bad coding */
/* decoded steam is 3 bytes for every four */
return 3 * (len >> 2) - nudge;
}
unsigned char *base64_decode(xmpp_ctx_t *ctx,
const char * const buffer, const unsigned len)
{
int dlen;
unsigned char *dbuf, *d;
uint32_t word, hextet;
int i;
/* len must be a multiple of 4 */
if (len & 0x03) return NULL;
dlen = base64_decoded_len(ctx, buffer, len);
dbuf = xmpp_alloc(ctx, dlen + 1);
if (dbuf != NULL) {
d = dbuf;
/* loop over each set of 4 characters, decoding 3 bytes */
for (i = 0; i < len - 3; i += 4) {
hextet = _base64_invcharmap[(int)buffer[i]];
if (hextet & 0xC0) break;
word = hextet << 18;
hextet = _base64_invcharmap[(int)buffer[i+1]];
if (hextet & 0xC0) break;
word |= hextet << 12;
hextet = _base64_invcharmap[(int)buffer[i+2]];
if (hextet & 0xC0) break;
word |= hextet << 6;
hextet = _base64_invcharmap[(int)buffer[i+3]];
if (hextet & 0xC0) break;
word |= hextet;
*d++ = (word & 0x00FF0000) >> 16;
*d++ = (word & 0x0000FF00) >> 8;
*d++ = (word & 0x000000FF);
}
if (hextet > 64) goto _base64_decode_error;
/* handle the remainder */
switch (dlen % 3) {
case 0:
/* nothing to do */
break;
case 1:
/* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(int)buffer[len-4]];
if (hextet & 0xC0) goto _base64_decode_error;
word = hextet << 2;
hextet = _base64_invcharmap[(int)buffer[len-3]];
if (hextet & 0xC0) goto _base64_decode_error;
word |= hextet >> 4;
*d++ = word & 0xFF;
hextet = _base64_invcharmap[(int)buffer[len-2]];
if (hextet != 64) goto _base64_decode_error;
hextet = _base64_invcharmap[(int)buffer[len-1]];
if (hextet != 64) goto _base64_decode_error;
break;
case 2:
/* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(int)buffer[len-4]];
if (hextet & 0xC0) goto _base64_decode_error;
word = hextet << 10;
hextet = _base64_invcharmap[(int)buffer[len-3]];
if (hextet & 0xC0) goto _base64_decode_error;
word |= hextet << 4;
hextet = _base64_invcharmap[(int)buffer[len-2]];
if (hextet & 0xC0) goto _base64_decode_error;
word |= hextet >> 2;
*d++ = (word & 0xFF00) >> 8;
*d++ = (word & 0x00FF);
hextet = _base64_invcharmap[(int)buffer[len-1]];
if (hextet != 64) goto _base64_decode_error;
break;
}
}
*d = '\0';
return dbuf;
_base64_decode_error:
/* invalid character; abort decoding! */
xmpp_free(ctx, dbuf);
return NULL;
}
/*** self tests ***/
#ifdef TEST
#include <stdio.h>
int test_charmap_identity(void)
{
int i, v, u;
for (i = 0; i < 65; i++) {
v = _base64_charmap[i];
if (v > 255) return 1;
u = _base64_invcharmap[v];
/* printf("map: %d -> %d -> %d\n", i, v, u); */
if (u != i) return 1;
ptr = strtok_r(tmp, ",", &saveptr);
while (ptr) {
if (strncmp(ptr, "r=", 2) == 0) {
r = ptr;
} else if (strncmp(ptr, "s=", 2) == 0) {
s = ptr + 2;
} else if (strncmp(ptr, "i=", 2) == 0) {
i = ptr + 2;
}
ptr = strtok_r(NULL, ",", &saveptr);
}
return 0;
}
int test_charmap_range(void)
{
int i, v;
for (i = 64; i < 256; i++) {
v = _base64_invcharmap[i];
if (i < 64) return 1;
if (!r || !s || !i) {
goto out;
}
return 0;
xmpp_base64_decode_bin(ctx, s, strlen(s), &sval, &sval_len);
if (!sval) {
goto out;
}
ival = strtol(i, &saveptr, 10);
/* "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;
}
auth_len = 3 + response_len + strlen(first_bare) + strlen(challenge);
auth = strophe_alloc(ctx, auth_len);
if (!auth) {
goto out_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, key, sign, sign);
sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size);
if (!sign_b64) {
goto out_auth;
}
/* Check for buffer overflow */
if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
strophe_free(ctx, sign_b64);
goto out_auth;
}
strcat(response, ",p=");
strcat(response, sign_b64);
strophe_free(ctx, sign_b64);
response_b64 =
xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response));
if (!response_b64) {
goto out_auth;
}
result = response_b64;
out_auth:
strophe_free(ctx, auth);
out_response:
strophe_free(ctx, response);
out_sval:
strophe_free(ctx, sval);
out:
strophe_free(ctx, tmp);
return result;
}
int main(int argc, char *argv[])
{
int ret = 0;
printf("testing charmap identity...");
ret = test_charmap_identity();
if (ret) return ret;
printf(" ok.\n");
printf("testing charmap range...");
ret = test_charmap_range();
if (ret) return ret;
printf(" ok.\n");
printf("no error\n");
return 0;
}
#endif /* TEST */

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sasl.h
** strophe XMPP client library -- SASL authentication helpers
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -20,25 +18,21 @@
#define __LIBSTROPHE_SASL_H__
#include "strophe.h"
#include "scram.h"
/** low-level sasl routines */
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
const char *jid, const char *password);
/** Base64 encoding routines. Implemented according to RFC 3548 */
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
char *base64_encode(xmpp_ctx_t *ctx,
const unsigned char * const buffer, const unsigned len);
int base64_decoded_len(xmpp_ctx_t *ctx,
const char * const buffer, const unsigned len);
unsigned char *base64_decode(xmpp_ctx_t *ctx,
const char * const buffer, const unsigned len);
char *sasl_digest_md5(xmpp_ctx_t *ctx,
const char *challenge,
const char *jid,
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,
const char *password);
#endif /* _LIBXMPP_SASL_H__ */

230
src/scram.c Normal file
View File

@@ -0,0 +1,230 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* scram.c
* strophe XMPP client library
*
* SCRAM-SHA1 helper functions according to RFC5802
* HMAC-SHA1 implementation according to RFC2104
*
* Copyright (C) 2013 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 or GPLv3 licenses.
*/
/** @file
* SCRAM-SHA1 helper functions.
*/
#include <assert.h>
#include <string.h>
#include "common.h"
#include "sha1.h"
#include "sha256.h"
#include "sha512.h"
#include "ostypes.h"
#include "scram.h"
#define HMAC_BLOCK_SIZE_MAX 128
static const uint8_t ipad = 0x36;
static const uint8_t opad = 0x5C;
const struct hash_alg scram_sha1 = {
"SCRAM-SHA-1",
SASL_MASK_SCRAMSHA1,
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_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,
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_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,
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_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;
sha512_context sha512;
};
static void crypto_HMAC(const struct hash_alg *alg,
const uint8_t *key,
size_t key_len,
const uint8_t *text,
size_t len,
uint8_t *digest)
{
uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
uint8_t sha_digest[SCRAM_DIGEST_SIZE];
size_t blocksize;
size_t i;
union common_hash_ctx ctx;
assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
blocksize = alg->digest_size < 48 ? 64 : 128;
memset(key_pad, 0, blocksize);
if (key_len <= blocksize) {
memcpy(key_pad, key, key_len);
} else {
/* according to RFC2104 */
alg->hash(key, key_len, key_pad);
}
for (i = 0; i < blocksize; i++) {
key_ipad[i] = key_pad[i] ^ ipad;
key_opad[i] = key_pad[i] ^ opad;
}
alg->init((void *)&ctx);
alg->update((void *)&ctx, key_ipad, blocksize);
alg->update((void *)&ctx, text, len);
alg->final((void *)&ctx, sha_digest);
alg->init((void *)&ctx);
alg->update((void *)&ctx, key_opad, blocksize);
alg->update((void *)&ctx, sha_digest, alg->digest_size);
alg->final((void *)&ctx, digest);
}
static void SCRAM_Hi(const struct hash_alg *alg,
const uint8_t *text,
size_t len,
const uint8_t *salt,
size_t salt_len,
uint32_t i,
uint8_t *digest)
{
size_t k;
uint32_t j;
uint8_t tmp[128];
static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
/* assume salt + INT(1) isn't longer than sizeof(tmp) */
assert(salt_len <= sizeof(tmp) - sizeof(int1));
memset(digest, 0, alg->digest_size);
if (i == 0) {
return;
}
memcpy(tmp, salt, salt_len);
memcpy(&tmp[salt_len], int1, sizeof(int1));
/* 'text' for Hi is a 'key' for HMAC */
crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
memcpy(tmp, digest, alg->digest_size);
for (j = 1; j < i; j++) {
crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
for (k = 0; k < alg->digest_size; k++) {
digest[k] ^= tmp[k];
}
}
}
void SCRAM_ClientKey(const struct hash_alg *alg,
const uint8_t *password,
size_t len,
const uint8_t *salt,
size_t salt_len,
uint32_t i,
uint8_t *key)
{
uint8_t salted[SCRAM_DIGEST_SIZE];
/* XXX: Normalize(password) is omitted */
SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
strlen("Client Key"), key);
}
void SCRAM_ClientSignature(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *AuthMessage,
size_t len,
uint8_t *sign)
{
uint8_t stored[SCRAM_DIGEST_SIZE];
alg->hash(ClientKey, alg->digest_size, stored);
crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
}
void SCRAM_ClientProof(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *ClientSignature,
uint8_t *proof)
{
size_t i;
for (i = 0; i < alg->digest_size; i++) {
proof[i] = ClientKey[i] ^ ClientSignature[i];
}
}

59
src/scram.h Normal file
View File

@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* scram.h
* strophe XMPP client library -- SCRAM helper functions
*
* Copyright (C) 2013 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 or GPLv3 licenses.
*/
/** @file
* SCRAM helper functions.
*/
#ifndef __LIBSTROPHE_SCRAM_H__
#define __LIBSTROPHE_SCRAM_H__
/* make sure the stdint.h types are available */
#include "ostypes.h"
/* Maximum possible digest size. Used for buffers allocation. */
#include "sha512.h"
#define SCRAM_DIGEST_SIZE SHA512_DIGEST_SIZE
struct hash_alg {
const char *scram_name;
int mask;
size_t digest_size;
void (*hash)(const uint8_t *, size_t, uint8_t *);
void (*init)(void *);
void (*update)(void *, const uint8_t *, size_t);
void (*final)(void *, uint8_t *);
};
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,
size_t len,
const uint8_t *salt,
size_t salt_len,
uint32_t i,
uint8_t *key);
void SCRAM_ClientSignature(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *AuthMessage,
size_t len,
uint8_t *sign);
void SCRAM_ClientProof(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *ClientSignature,
uint8_t *proof);
#endif /* __LIBSTROPHE_SCRAM_H__ */

112
src/sha.h Normal file
View File

@@ -0,0 +1,112 @@
/* public api for steve reid's public domain SHA-1 implementation */
/* this file is in the public domain */
/** @file
* SHA-1 hash API.
*/
#ifndef __LIBSTROPHE_SHA_H__
#define __LIBSTROPHE_SHA_H__
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/* make sure the stdint.h types are available */
#include "ostypes.h"
#if defined(__BIG_ENDIAN__) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define STORE32H(x, y) \
do { \
uint32_t __t = (x); \
memcpy(y, &__t, 4); \
} while (0)
#define LOAD32H(x, y) \
do { \
memcpy(&(x), y, 4); \
x &= 0xFFFFFFFF; \
} while (0)
#define STORE64H(x, y) \
do { \
uint64_t __t = (x); \
memcpy(y, &__t, 8); \
} while (0)
#define LOAD64H(x, y) \
do { \
memcpy(&(x), y, 8); \
} while (0)
#elif defined(__LITTLE_ENDIAN__) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define STORE32H(x, y) \
do { \
(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); \
} while (0)
#define LOAD32H(x, y) \
do { \
x = ((uint32_t)((y)[0] & 255) << 24) | \
((uint32_t)((y)[1] & 255) << 16) | \
((uint32_t)((y)[2] & 255) << 8) | ((uint32_t)((y)[3] & 255)); \
} while (0)
#define STORE64H(x, y) \
do { \
(y)[0] = (uint8_t)(((x) >> 56) & 255); \
(y)[1] = (uint8_t)(((x) >> 48) & 255); \
(y)[2] = (uint8_t)(((x) >> 40) & 255); \
(y)[3] = (uint8_t)(((x) >> 32) & 255); \
(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); \
} while (0)
#define LOAD64H(x, y) \
do { \
x = (((uint64_t)((y)[0] & 255)) << 56) | \
(((uint64_t)((y)[1] & 255)) << 48) | \
(((uint64_t)((y)[2] & 255)) << 40) | \
(((uint64_t)((y)[3] & 255)) << 32) | \
(((uint64_t)((y)[4] & 255)) << 24) | \
(((uint64_t)((y)[5] & 255)) << 16) | \
(((uint64_t)((y)[6] & 255)) << 8) | (((uint64_t)((y)[7] & 255))); \
} while (0)
#else
#error Unknown endianness not supported
#endif
#ifdef _MSC_VER
#define CONST64(n) n##ui64
#else
#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))) & \
0xFFFFFFFFUL)
#define ROR64c(x, y) \
(((((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y)&CONST64(63))) | \
((x) << (((uint64_t)64 - ((y)&63)) & 63))) & \
CONST64(0xFFFFFFFFFFFFFFFF))
#ifdef __cplusplus
}
#endif
#endif /* __LIBSTROPHE_SHA_H__ */

View File

@@ -8,16 +8,16 @@ By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
-----------------
Modified 7/98
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
@@ -30,7 +30,7 @@ Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
@@ -57,7 +57,7 @@ Still 100% public domain
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
Modified to run on Compaq Alpha hardware.
-----------------
Modified 07/2002
@@ -70,92 +70,82 @@ use SHA1_ prefix for public api
move public api to sha1.h
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define SHA1HANDSOFF */
#include <stdio.h>
#include <string.h>
/* make sure the stdint.h types are available */
#if defined(_MSC_VER) /* Microsoft Visual C++ */
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
typedef __int64 int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
/* no uint64_t */
#else
#include <stdint.h>
#endif
#include "ostypes.h"
#include "sha1.h"
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
static uint32_t host_to_be(uint32_t i);
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
/* FIXME: can we do this in an endian-proof way? */
#ifdef WORDS_BIGENDIAN
#define blk0(i) block->l[i]
#else
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
#define blk0(i) (block->l[i] = host_to_be(block->l[i]))
#define blk(i) \
(block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
block->l[(i + 2) & 15] ^ block->l[i & 15], \
1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
#define R0(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R1(v, w, x, y, z, i) \
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R2(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
w = rol(w, 30);
#define R3(v, w, x, y, z, i) \
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
w = rol(w, 30);
#define R4(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
w = rol(w, 30);
#ifdef VERBOSE /* SAK */
void SHAPrintContext(SHA1_CTX *context, char *msg){
printf("%s (%d,%d) %x %x %x %x %x\n",
msg,
context->count[0], context->count[1],
context->state[0],
context->state[1],
context->state[2],
context->state[3],
context->state[4]);
static uint32_t host_to_be(uint32_t i)
{
#define le_to_be(i) ((rol((i), 24) & 0xFF00FF00) | (rol((i), 8) & 0x00FF00FF))
#if defined(__BIG_ENDIAN__) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
return i;
#elif defined(__LITTLE_ENDIAN__) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return le_to_be(i);
#else /* fallback to run-time check */
static const union {
uint32_t u;
unsigned char c;
} check = {1};
return check.c ? le_to_be(i) : i;
#endif
#undef le_to_be
}
static void *(*volatile SHA1_explicit_memset)(void *, int, size_t) = &memset;
static void SHA1_cleanse(void *p, size_t len)
{
SHA1_explicit_memset(p, 0, len);
}
#endif /* VERBOSE */
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
typedef union {
uint8_t c[64];
uint32_t l[16];
} CHAR64LONG16;
CHAR64LONG16* block;
CHAR64LONG16 *block;
#ifdef SHA1HANDSOFF
static uint8_t workspace[64];
block = (CHAR64LONG16*)workspace;
block = (CHAR64LONG16 *)workspace;
memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
@@ -165,6 +155,7 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
/* clang-format off */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
@@ -185,6 +176,7 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* clang-format on */
/* Add the working vars back into context.state[] */
state[0] += a;
@@ -194,12 +186,15 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
SHA1_cleanse(&a, sizeof(a));
SHA1_cleanse(&b, sizeof(b));
SHA1_cleanse(&c, sizeof(c));
SHA1_cleanse(&d, sizeof(d));
SHA1_cleanse(&e, sizeof(e));
}
/* SHA1Init - Initialize new context */
void SHA1_Init(SHA1_CTX* context)
void crypto_SHA1_Init(SHA1_CTX *context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
@@ -210,180 +205,62 @@ void SHA1_Init(SHA1_CTX* context)
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void 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;
#ifdef VERBOSE
SHAPrintContext(context, "before");
#endif
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((context->count[0] += (uint32_t)len << 3) < ((uint32_t)len << 3))
context->count[1]++;
context->count[1] += (uint32_t)(len >> 29);
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
memcpy(&context->buffer[j], data, (i = 64 - j));
SHA1_Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
for (; i + 63 < len; i += 64) {
SHA1_Transform(context->state, data + i);
}
j = 0;
}
else i = 0;
} else
i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
SHAPrintContext(context, "after ");
#endif
}
/* Add padding and return the message digest. */
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest)
{
uint32_t i;
uint8_t finalcount[8];
uint8_t finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >>
((3 - (i & 3)) * 8)) &
255); /* Endian independent */
}
SHA1_Update(context, (uint8_t *)"\200", 1);
crypto_SHA1_Update(context, (uint8_t *)"\200", 1);
while ((context->count[0] & 504) != 448) {
SHA1_Update(context, (uint8_t *)"\0", 1);
crypto_SHA1_Update(context, (uint8_t *)"\0", 1);
}
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
/* Should cause a SHA1_Transform() */
crypto_SHA1_Update(context, finalcount, 8);
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
digest[i] = (uint8_t)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
digest[i] =
(uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
}
/* Wipe variables */
i = 0;
memset(context->buffer, 0, 64);
memset(context->state, 0, 20);
memset(context->count, 0, 8);
memset(finalcount, 0, 8); /* SWR */
SHA1_cleanse(context->buffer, 64);
SHA1_cleanse(context->state, 20);
SHA1_cleanse(context->count, 8);
SHA1_cleanse(finalcount, 8); /* SWR */
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
SHA1_Transform(context->state, context->buffer);
#endif
}
/*************************************************************/
#if 0
int main(int argc, char** argv)
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest)
{
int i, j;
SHA1_CTX context;
unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
FILE* file;
if (argc > 2) {
puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* JHB */
puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
return(0);
}
if (argc < 2) {
file = stdin;
}
else {
if (!(file = fopen(argv[1], "rb"))) {
fputs("Unable to open file.", stderr);
return(-1);
}
}
SHA1_Init(&context);
while (!feof(file)) { /* note: what if ferror(file) */
i = fread(buffer, 1, 16384, file);
SHA1_Update(&context, buffer, i);
}
SHA1_Final(&context, digest);
fclose(file);
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
for (j = 0; j < 4; j++) {
printf("%02X", digest[i*4+j]);
}
putchar(' ');
}
putchar('\n');
return(0); /* JHB */
SHA1_CTX ctx;
crypto_SHA1_Init(&ctx);
crypto_SHA1_Update(&ctx, data, len);
crypto_SHA1_Final(&ctx, digest);
}
#endif
/* self test */
#ifdef TEST
static char *test_data[] = {
"abc",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"A million repetitions of 'a'"};
static char *test_results[] = {
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
"84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
{
int i,j;
char *c = output;
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
for (j = 0; j < 4; j++) {
sprintf(c,"%02X", digest[i*4+j]);
c += 2;
}
sprintf(c, " ");
c += 1;
}
*(c - 1) = '\0';
}
int main(int argc, char** argv)
{
int k;
SHA1_CTX context;
uint8_t digest[20];
char output[80];
fprintf(stdout, "verifying SHA-1 implementation... ");
for (k = 0; k < 2; k++){
SHA1_Init(&context);
SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
SHA1_Final(&context, digest);
digest_to_hex(digest, output);
if (strcmp(output, test_results[k])) {
fprintf(stdout, "FAIL\n");
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
fprintf(stderr,"\t%s returned\n", output);
fprintf(stderr,"\t%s is correct\n", test_results[k]);
return (1);
}
}
/* million 'a' vector we feed separately */
SHA1_Init(&context);
for (k = 0; k < 1000000; k++)
SHA1_Update(&context, (uint8_t*)"a", 1);
SHA1_Final(&context, digest);
digest_to_hex(digest, output);
if (strcmp(output, test_results[2])) {
fprintf(stdout, "FAIL\n");
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
fprintf(stderr,"\t%s returned\n", output);
fprintf(stderr,"\t%s is correct\n", test_results[2]);
return (1);
}
/* success */
fprintf(stdout, "ok\n");
return(0);
}
#endif /* TEST */

View File

@@ -5,27 +5,31 @@
* SHA-1 hash API.
*/
#ifndef __SHA1_H
#define __SHA1_H
#ifndef __LIBSTROPHE_SHA1_H__
#define __LIBSTROPHE_SHA1_H__
#ifdef __cplusplus
extern "C" {
#endif
/* make sure the stdint.h types are available */
#include "ostypes.h"
typedef struct {
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
uint8_t buffer[64];
} SHA1_CTX;
#define SHA1_DIGEST_SIZE 20
void SHA1_Init(SHA1_CTX* context);
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
void crypto_SHA1_Init(SHA1_CTX *context);
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);
#ifdef __cplusplus
}
#endif
#endif /* __SHA1_H */
#endif /* __LIBSTROPHE_SHA1_H__ */

210
src/sha256.c Normal file
View File

@@ -0,0 +1,210 @@
/*
* Code originally from LibTomCrypt -- Licensed under the Public Domain/WTFPL2.0
*/
#include "sha256.h"
#include "sha.h"
/* Various logical functions */
#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 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))
#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
static void sha256_compress(sha256_context *md, const uint8_t *buf)
{
uint32_t S[8], W[64], t0, t1;
int i;
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = md->state[i];
}
/* copy the state into 512-bits into W[0..15] */
for (i = 0; i < 16; i++) {
LOAD32H(W[i], buf + (4 * i));
}
/* fill W[16..63] */
for (i = 16; i < 64; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
}
/* Compress */
#define RND(a, b, c, d, e, f, g, h, i, ki) \
t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 0, 0x428a2f98);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 1, 0x71374491);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 2, 0xb5c0fbcf);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 3, 0xe9b5dba5);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 4, 0x3956c25b);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 5, 0x59f111f1);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 6, 0x923f82a4);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 7, 0xab1c5ed5);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 8, 0xd807aa98);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 9, 0x12835b01);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 10, 0x243185be);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 11, 0x550c7dc3);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 12, 0x72be5d74);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 13, 0x80deb1fe);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 14, 0x9bdc06a7);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 15, 0xc19bf174);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 16, 0xe49b69c1);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 17, 0xefbe4786);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 18, 0x0fc19dc6);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 19, 0x240ca1cc);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 20, 0x2de92c6f);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 21, 0x4a7484aa);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 22, 0x5cb0a9dc);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 23, 0x76f988da);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 24, 0x983e5152);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 25, 0xa831c66d);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 26, 0xb00327c8);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 27, 0xbf597fc7);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 28, 0xc6e00bf3);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 29, 0xd5a79147);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 30, 0x06ca6351);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 31, 0x14292967);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 32, 0x27b70a85);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 33, 0x2e1b2138);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 34, 0x4d2c6dfc);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 35, 0x53380d13);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 36, 0x650a7354);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 37, 0x766a0abb);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 38, 0x81c2c92e);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 39, 0x92722c85);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 40, 0xa2bfe8a1);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 41, 0xa81a664b);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 42, 0xc24b8b70);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 43, 0xc76c51a3);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 44, 0xd192e819);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 45, 0xd6990624);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 46, 0xf40e3585);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 47, 0x106aa070);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 48, 0x19a4c116);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 49, 0x1e376c08);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 50, 0x2748774c);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 51, 0x34b0bcb5);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 52, 0x391c0cb3);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 53, 0x4ed8aa4a);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 54, 0x5b9cca4f);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 55, 0x682e6ff3);
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 56, 0x748f82ee);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 57, 0x78a5636f);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 58, 0x84c87814);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 59, 0x8cc70208);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 60, 0x90befffa);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 61, 0xa4506ceb);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 62, 0xbef9a3f7);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 63, 0xc67178f2);
#undef RND
/* feedback */
for (i = 0; i < 8; i++) {
md->state[i] = md->state[i] + S[i];
}
}
void sha256_init(sha256_context *md)
{
md->curlen = 0;
md->length = 0;
md->state[0] = 0x6A09E667UL;
md->state[1] = 0xBB67AE85UL;
md->state[2] = 0x3C6EF372UL;
md->state[3] = 0xA54FF53AUL;
md->state[4] = 0x510E527FUL;
md->state[5] = 0x9B05688CUL;
md->state[6] = 0x1F83D9ABUL;
md->state[7] = 0x5BE0CD19UL;
}
void sha256_process(sha256_context *md, const uint8_t *in, size_t inlen)
{
size_t n;
if (md->curlen > sizeof(md->buf)) {
return;
}
if ((md->length + inlen) < md->length) {
return;
}
while (inlen > 0) {
if (md->curlen == 0 && inlen >= 64) {
sha256_compress(md, in);
md->length += 64 * 8;
in += 64;
inlen -= 64;
} else {
n = (((inlen) < ((64 - md->curlen))) ? (inlen)
: ((64 - md->curlen)));
memcpy(md->buf + md->curlen, in, (size_t)n);
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == 64) {
sha256_compress(md, md->buf);
md->length += 8 * 64;
md->curlen = 0;
}
}
}
}
void sha256_done(sha256_context *md, uint8_t *out)
{
int i;
if (md->curlen >= sizeof(md->buf)) {
return;
}
/* increase the length of the message */
md->length += md->curlen * 8;
/* append the '1' bit */
md->buf[md->curlen++] = (uint8_t)0x80;
/* if the length is currently above 56 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (md->curlen > 56) {
while (md->curlen < 64) {
md->buf[md->curlen++] = (uint8_t)0;
}
sha256_compress(md, md->buf);
md->curlen = 0;
}
/* pad upto 56 bytes of zeroes */
while (md->curlen < 56) {
md->buf[md->curlen++] = (uint8_t)0;
}
/* store length */
STORE64H(md->length, md->buf + 56);
sha256_compress(md, md->buf);
/* copy output */
for (i = 0; i < 8; i++) {
STORE32H(md->state[i], out + (4 * i));
}
}
void sha256_hash(const uint8_t *data, size_t len, uint8_t *digest)
{
sha256_context md;
sha256_init(&md);
sha256_process(&md, data, len);
sha256_done(&md, digest);
}

34
src/sha256.h Normal file
View File

@@ -0,0 +1,34 @@
/* public api for LibTomCrypt SHA-256 implementation */
/** @file
* SHA-256 hash API.
*/
#ifndef __LIBSTROPHE_SHA256_H__
#define __LIBSTROPHE_SHA256_H__
#ifdef __cplusplus
extern "C" {
#endif
/* make sure the stdint.h types are available */
#include "ostypes.h"
typedef struct {
uint64_t length;
uint32_t state[8], curlen;
uint8_t buf[64];
} sha256_context;
#define SHA256_DIGEST_SIZE 32
void sha256_init(sha256_context *md);
void sha256_process(sha256_context *md, const uint8_t *in, size_t inlen);
void sha256_done(sha256_context *md, uint8_t *out);
void sha256_hash(const uint8_t *data, size_t len, uint8_t *digest);
#ifdef __cplusplus
}
#endif
#endif /* __LIBSTROPHE_SHA256_H__ */

201
src/sha512.c Normal file
View File

@@ -0,0 +1,201 @@
/*
* Code originally from LibTomCrypt -- Licensed under the Public Domain/WTFPL2.0
*/
#include "sha512.h"
#include "sha.h"
/* the K array */
static const uint64_t K[80] = {
CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)};
/* Various logical functions */
#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 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))
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
/* compress 1024-bits */
static void sha512_compress(sha512_context *md, const uint8_t *buf)
{
uint64_t S[8], W[80], t0, t1;
int i;
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = md->state[i];
}
/* copy the state into 1024-bits into W[0..15] */
for (i = 0; i < 16; i++) {
LOAD64H(W[i], buf + (8 * i));
}
/* fill W[16..79] */
for (i = 16; i < 80; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
}
/* Compress */
#define RND(a, b, c, d, e, f, g, h, i) \
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
for (i = 0; i < 80; i += 8) {
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i + 0);
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], i + 1);
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], i + 2);
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], i + 3);
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], i + 4);
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], i + 5);
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], i + 6);
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], i + 7);
}
/* feedback */
for (i = 0; i < 8; i++) {
md->state[i] = md->state[i] + S[i];
}
}
void sha512_init(sha512_context *md)
{
md->curlen = 0;
md->length = 0;
md->state[0] = CONST64(0x6a09e667f3bcc908);
md->state[1] = CONST64(0xbb67ae8584caa73b);
md->state[2] = CONST64(0x3c6ef372fe94f82b);
md->state[3] = CONST64(0xa54ff53a5f1d36f1);
md->state[4] = CONST64(0x510e527fade682d1);
md->state[5] = CONST64(0x9b05688c2b3e6c1f);
md->state[6] = CONST64(0x1f83d9abfb41bd6b);
md->state[7] = CONST64(0x5be0cd19137e2179);
}
void sha512_process(sha512_context *md, const uint8_t *in, size_t inlen)
{
size_t n;
if (md->curlen > sizeof(md->buf)) {
return;
}
if ((md->length + inlen) < md->length) {
return;
}
while (inlen > 0) {
if (md->curlen == 0 && inlen >= 128) {
sha512_compress(md, in);
md->length += 128 * 8;
in += 128;
inlen -= 128;
} else {
n = (((inlen) < ((128u - md->curlen))) ? (inlen)
: ((128u - md->curlen)));
memcpy(md->buf + md->curlen, in, (size_t)n);
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == 128) {
sha512_compress(md, md->buf);
md->length += 8 * 128;
md->curlen = 0;
}
}
}
}
void sha512_done(sha512_context *md, uint8_t *out)
{
int i;
if (md->curlen >= sizeof(md->buf)) {
return;
}
/* increase the length of the message */
md->length += md->curlen * CONST64(8);
/* append the '1' bit */
md->buf[md->curlen++] = (uint8_t)0x80;
/* if the length is currently above 112 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (md->curlen > 112) {
while (md->curlen < 128) {
md->buf[md->curlen++] = (uint8_t)0;
}
sha512_compress(md, md->buf);
md->curlen = 0;
}
/* pad upto 120 bytes of zeroes
* note: that from 112 to 120 is the 64 MSB of the length. We assume that
* you won't hash > 2^64 bits of data... :-)
*/
while (md->curlen < 120) {
md->buf[md->curlen++] = (uint8_t)0;
}
/* store length */
STORE64H(md->length, md->buf + 120);
sha512_compress(md, md->buf);
/* copy output */
for (i = 0; i < 8; i++) {
STORE64H(md->state[i], out + (8 * i));
}
}
void sha512_hash(const uint8_t *data, size_t len, uint8_t *digest)
{
sha512_context md;
sha512_init(&md);
sha512_process(&md, data, len);
sha512_done(&md, digest);
}

34
src/sha512.h Normal file
View File

@@ -0,0 +1,34 @@
/* public api for LibTomCrypt SHA-512 implementation */
/** @file
* SHA-512 hash API.
*/
#ifndef __LIBSTROPHE_SHA512_H__
#define __LIBSTROPHE_SHA512_H__
#ifdef __cplusplus
extern "C" {
#endif
/* make sure the stdint.h types are available */
#include "ostypes.h"
typedef struct {
uint64_t length, state[8];
uint8_t curlen;
uint8_t buf[128];
} sha512_context;
#define SHA512_DIGEST_SIZE 64
void sha512_init(sha512_context *cc);
void sha512_process(sha512_context *cc, const uint8_t *data, size_t len);
void sha512_done(sha512_context *cc, uint8_t *dst);
void sha512_hash(const uint8_t *data, size_t len, uint8_t *digest);
#ifdef __cplusplus
}
#endif
#endif /* __LIBSTROPHE_SHA512_H__ */

File diff suppressed because it is too large Load Diff

34
src/snprintf.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright Patrick Powell 1995
* This code is based on code written by Patrick Powell (papowell@astart.com)
* It may be used for any purpose as long as this notice remains intact
* on all source code distributions
*/
/** @file
* Compatibility wrappers for OSes lacking snprintf(3) and/or vsnprintf(3).
*/
#ifndef __LIBSTROPHE_SNPRINTF_H__
#define __LIBSTROPHE_SNPRINTF_H__
#include <stddef.h>
#include <stdarg.h>
#if defined(HAVE_SNPRINTF) || defined(HAVE_VSNPRINTF)
#include <stdio.h>
#endif
#ifdef HAVE_SNPRINTF
#define strophe_snprintf snprintf
#else
int strophe_snprintf(char *str, size_t count, const char *fmt, ...);
#endif
#ifdef HAVE_VSNPRINTF
#define strophe_vsnprintf vsnprintf
#else
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
#endif
#endif /* __LIBSTROPHE_SNPRINTF_H__ */

1124
src/sock.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* sock.h
** strophe XMPP client library -- socket abstraction header
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -23,29 +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 int 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_srv_lookup(const char *service, const char *proto,
const char *domain, char *resulttarget,
int resulttargetlength, int *resultport);
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__ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
/* thread.c
** strophe XMPP client library -- thread abstraction
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
/** @file
* Thread absraction.
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <pthread.h>
#endif
#include "strophe.h"
#include "common.h"
#include "thread.h"
struct _mutex_t {
const xmpp_ctx_t *ctx;
#ifdef _WIN32
HANDLE mutex;
#else
pthread_mutex_t *mutex;
#endif
};
/* mutex functions */
mutex_t *mutex_create(const xmpp_ctx_t * ctx)
{
mutex_t *mutex;
mutex = xmpp_alloc(ctx, sizeof(mutex_t));
if (mutex) {
mutex->ctx = ctx;
#ifdef _WIN32
mutex->mutex = CreateMutex(NULL, FALSE, NULL);
#else
mutex->mutex = xmpp_alloc(ctx, sizeof(pthread_mutex_t));
if (mutex->mutex)
if (pthread_mutex_init(mutex->mutex, NULL) != 0) {
xmpp_free(ctx, mutex->mutex);
mutex->mutex = NULL;
}
#endif
if (!mutex->mutex) {
xmpp_free(ctx, mutex);
mutex = NULL;
}
}
return mutex;
}
int mutex_destroy(mutex_t *mutex)
{
int ret = 1;
const xmpp_ctx_t *ctx;
#ifdef _WIN32
if (mutex->mutex)
ret = CloseHandle(mutex->mutex);
#else
if (mutex->mutex)
ret = pthread_mutex_destroy(mutex->mutex) == 0;
#endif
ctx = mutex->ctx;
xmpp_free(ctx, mutex);
return ret;
}
int mutex_lock(mutex_t *mutex)
{
int ret;
#ifdef _WIN32
ret = WaitForSingleObject(mutex->mutex, INFINITE) == 0;
#else
ret = pthread_mutex_lock(mutex->mutex) == 0;
#endif
return ret;
}
int mutex_trylock(mutex_t *mutex)
{
/* TODO */
return 0;
}
int mutex_unlock(mutex_t *mutex)
{
int ret;
#ifdef _WIN32
ret = ReleaseMutex(mutex->mutex);
#else
ret = pthread_mutex_unlock(mutex->mutex) == 0;
#endif
return ret;
}

View File

@@ -1,43 +0,0 @@
/* thread.h
** strophe XMPP client library -- thread abstraction header
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
/** @file
* Threading abstraction API.
*/
#ifndef __LIBSTROPHE_THREAD_H__
#define __LIBSTROPHE_THREAD_H__
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include "strophe.h"
typedef struct _mutex_t mutex_t;
/* mutex functions */
mutex_t *mutex_create(const xmpp_ctx_t *ctx);
int mutex_destroy(mutex_t *mutex);
int mutex_lock(mutex_t *mutex);
int mutex_trylock(mutex_t *mutex);
int mutex_unlock(mutex_t *mutex);
#endif /* __LIBSTROPHE_THREAD_H__ */

266
src/tls.c Normal file
View File

@@ -0,0 +1,266 @@
/* 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 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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.h
** strophe XMPP client library -- TLS abstraction header
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -24,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_ctx_t *ctx, sock_t sock);
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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls_dummy.c
** strophe XMPP client library -- TLS abstraction dummy impl.
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -36,59 +34,113 @@ void tls_shutdown(void)
return;
}
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
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);
/* always fail */
return NULL;
}
void tls_free(tls_t *tls)
{
UNUSED(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);
UNUSED(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);
return -1;
}
int tls_stop(tls_t *tls)
{
UNUSED(tls);
return -1;
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
/* 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(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(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(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(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,32 +1,51 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls.c
** strophe XMPP client library -- TLS abstraction header
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** 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"
#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;
};
void tls_initialize(void)
@@ -45,88 +64,615 @@ void tls_shutdown(void)
gnutls_global_deinit();
}
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
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)
{
tls_t *tls = xmpp_alloc(ctx, sizeof(tls_t));
const int cert_type_priority[3] = { GNUTLS_CRT_X509,
GNUTLS_CRT_OPENPGP, 0 };
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;
}
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 = strophe_alloc(conn->ctx, sizeof(tls_t));
if (tls) {
tls->ctx = ctx;
tls->sock = sock;
gnutls_init(&tls->session, GNUTLS_CLIENT);
memset(tls, 0, sizeof(*tls));
tls->ctx = conn->ctx;
tls->conn = conn;
gnutls_init(&tls->session, GNUTLS_CLIENT);
gnutls_certificate_allocate_credentials(&tls->cred);
gnutls_certificate_allocate_credentials(&tls->cred);
tls_set_credentials(tls, NULL);
gnutls_set_default_priority(tls->session);
gnutls_certificate_type_set_priority(tls->session, cert_type_priority);
if (conn->password_callback)
gnutls_certificate_set_pin_function(tls->cred,
_tls_password_callback, conn);
/* fixme: this may require setting a callback on win32? */
gnutls_transport_set_ptr(tls->session,
(gnutls_transport_ptr_t)sock);
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);
if (err < 0) return err;
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);
}
tls->lasterror = err;
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
tls->cred);
return err == GNUTLS_E_SUCCESS;
}
return err;
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)
{
return gnutls_handshake(tls->session);
sock_set_blocking(tls->conn->sock);
tls->lasterror = gnutls_handshake(tls->session);
sock_set_nonblocking(tls->conn->sock);
return tls->lasterror == GNUTLS_E_SUCCESS;
}
int tls_stop(tls_t *tls)
{
return gnutls_bye(tls->session, GNUTLS_SHUT_RDWR);
tls->lasterror = gnutls_bye(tls->session, GNUTLS_SHUT_RDWR);
return tls->lasterror == GNUTLS_E_SUCCESS;
}
int tls_error(tls_t *tls)
int tls_error(struct conn_interface *intf)
{
/* todo: some kind of error polling/dump */
return 0;
return intf->conn->tls->lasterror;
}
int tls_pending(tls_t *tls)
int tls_is_recoverable(struct conn_interface *intf, int error)
{
return gnutls_record_check_pending (tls->session);
UNUSED(intf);
return !gnutls_error_is_fatal(error);
}
int tls_read(tls_t *tls, void * const buff, const size_t len)
int tls_pending(struct conn_interface *intf)
{
return gnutls_record_check_pending(intf->conn->tls->session);
}
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;
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;
return ret;
}
int tls_clear_pending_write(struct conn_interface *intf)
{
UNUSED(intf);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* tls_schannel.c
** strophe XMPP client library -- TLS abstraction schannel impl.
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -26,6 +24,7 @@
struct _tls {
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
sock_t sock;
HANDLE hsec32;
@@ -63,8 +62,25 @@ void tls_shutdown(void)
return;
}
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
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;
sock_t sock = conn->sock;
tls_t *tls;
PSecurityFunctionTable (*pInitSecurityInterface)(void);
SCHANNEL_CRED scred;
@@ -75,99 +91,105 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
SecPkgCred_CipherStrengths spc_cs;
SecPkgCred_SupportedProtocols spc_sp;
OSVERSIONINFO osvi;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
OSVERSIONINFO osvi;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
/* no TLS support on win9x/me, despite what anyone says */
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
return NULL;
return NULL;
}
tls = xmpp_alloc(ctx, sizeof(*tls));
tls = strophe_alloc(ctx, sizeof(*tls));
if (!tls) {
return NULL;
return NULL;
}
memset(tls, 0, sizeof(*tls));
tls->ctx = ctx;
tls->conn = conn;
tls->sock = sock;
if (!(tls->hsec32 = LoadLibrary ("secur32.dll"))) {
tls_free(tls);
return NULL;
if (!(tls->hsec32 = LoadLibrary("secur32.dll"))) {
tls_free(tls);
return NULL;
}
if (!(pInitSecurityInterface =
(void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) {
tls_free(tls);
return NULL;
if (!(pInitSecurityInterface =
(void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) {
tls_free(tls);
return NULL;
}
tls->sft = pInitSecurityInterface();
if (!tls->sft) {
tls_free(tls);
return NULL;
tls_free(tls);
return NULL;
}
ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi));
if (ret != SEC_E_OK)
{
tls_free(tls);
return NULL;
if (ret != SEC_E_OK) {
tls_free(tls);
return NULL;
}
xmpp_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
strophe_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
memset(&scred, 0, sizeof(scred));
scred.dwVersion = SCHANNEL_CRED_VERSION;
/*scred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;*/
/* Remote server closes connection with forced RC4.
The below lines are commented to leave default system configuration */
#if 0
/* Something down the line doesn't like AES, so force it to RC4 */
algs[0] = CALG_RC4;
scred.cSupportedAlgs = 1;
scred.palgSupportedAlgs = algs;
#else
(void)algs;
#endif
ret = tls->sft->AcquireCredentialsHandleA(NULL, UNISP_NAME,
SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL, &(tls->hcred), NULL);
ret = tls->sft->AcquireCredentialsHandleA(
NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL,
&(tls->hcred), NULL);
if (ret != SEC_E_OK)
{
tls_free(tls);
return NULL;
if (ret != SEC_E_OK) {
tls_free(tls);
return NULL;
}
xmpp_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
strophe_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
tls->init = 1;
/* This bunch of queries should trip up wine until someone fixes
* schannel support there */
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa);
if (ret != SEC_E_OK)
{
tls_free(tls);
return NULL;
}
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs);
if (ret != SEC_E_OK)
{
tls_free(tls);
return NULL;
}
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp);
if (ret != SEC_E_OK)
{
tls_free(tls);
return NULL;
}
ret = tls->sft->QueryCredentialsAttributes(
&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa);
if (ret != SEC_E_OK) {
tls_free(tls);
return NULL;
}
ret = tls->sft->QueryCredentialsAttributes(
&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs);
if (ret != SEC_E_OK) {
tls_free(tls);
return NULL;
}
ret = tls->sft->QueryCredentialsAttributes(
&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp);
if (ret != SEC_E_OK) {
tls_free(tls);
return NULL;
}
return tls;
}
@@ -175,37 +197,63 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
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) {
tls->sft->FreeCredentialsHandle(&(tls->hcred));
tls->sft->FreeCredentialsHandle(&(tls->hcred));
}
tls->sft = NULL;
if (tls->hsec32) {
FreeLibrary(tls->hsec32);
tls->hsec32 = NULL;
FreeLibrary(tls->hsec32);
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);
UNUSED(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;
@@ -213,29 +261,17 @@ int tls_start(tls_t *tls)
SecBuffer sbin[2], sbout[1];
SECURITY_STATUS ret;
int sent;
char *name = NULL;
char *name;
struct conn_interface *intf;
/* search the ctx's conns for our sock, and use the domain there as our
* name */
{
xmpp_connlist_t *listentry = tls->ctx->connlist;
/* use the domain there as our name */
name = tls->conn->domain;
intf = tls->conn->intf;
while (listentry) {
xmpp_conn_t *conn = listentry->conn;
if (conn->sock == tls->sock) {
name = strdup(conn->domain);
listentry = NULL;
} else {
listentry = listentry->next;
}
}
}
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT
| ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR
| ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM
| ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM |
ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
memset(&(sbout[0]), 0, sizeof(sbout[0]));
sbout[0].BufferType = SECBUFFER_TOKEN;
@@ -247,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]));
@@ -258,279 +294,284 @@ int tls_start(tls_t *tls)
sbdin.cBuffers = 2;
sbdin.pBuffers = sbin;
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), NULL, name, ctxtreq, 0, 0,
NULL, 0, &(tls->hctxt), &sbdout,
&ctxtattr, NULL);
ret = tls->sft->InitializeSecurityContextA(
&(tls->hcred), NULL, name, ctxtreq, 0, 0, NULL, 0, &(tls->hctxt),
&sbdout, &ctxtattr, NULL);
while (ret == SEC_I_CONTINUE_NEEDED
|| ret == SEC_I_INCOMPLETE_CREDENTIALS) {
unsigned char *p = sbin[0].pvBuffer;
int len = 0, inbytes = 0;
unsigned char *p = sbin[0].pvBuffer;
int len = 0;
if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
while (ret == SEC_I_CONTINUE_NEEDED ||
ret == SEC_I_INCOMPLETE_CREDENTIALS ||
ret == SEC_E_INCOMPLETE_MESSAGE) {
int inbytes = 0;
sent = sock_write(tls->sock, writebuff, writelen);
if (sent == -1) {
tls->lasterror = sock_error();
}
else
{
writebuff += sent;
writelen -= sent;
}
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
sbdout.pBuffers[0].pvBuffer = NULL;
sbdout.pBuffers[0].cbBuffer = 0;
}
if (ret != SEC_E_INCOMPLETE_MESSAGE) {
len = 0;
p = sbin[0].pvBuffer;
}
/* poll for a bit until the remote server stops sending data, ie it
* finishes sending the token */
inbytes = 1;
{
fd_set fds;
struct timeval tv;
if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
tv.tv_sec = 2;
tv.tv_usec = 0;
sent = sock_write(intf, writebuff, writelen);
if (sent == -1) {
tls->lasterror = sock_error(intf);
} else {
writebuff += sent;
writelen -= sent;
}
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
sbdout.pBuffers[0].pvBuffer = NULL;
sbdout.pBuffers[0].cbBuffer = 0;
}
FD_ZERO(&fds);
FD_SET(tls->sock, &fds);
select(tls->sock, &fds, NULL, NULL, &tv);
}
/* poll for a bit until the remote server stops sending data, ie it
* finishes sending the token */
inbytes = 1;
{
fd_set fds;
struct timeval tv;
while (inbytes > 0) {
fd_set fds;
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
tv.tv_sec = 0;
tv.tv_usec = 1000;
FD_ZERO(&fds);
FD_SET(tls->sock, &fds);
FD_ZERO(&fds);
FD_SET(tls->sock, &fds);
select(tls->sock, &fds, NULL, NULL, &tv);
select(tls->sock, &fds, NULL, NULL, &tv);
}
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len);
while (inbytes > 0) {
fd_set fds;
struct timeval tv;
if (inbytes > 0) {
len += inbytes;
p += inbytes;
}
else
{
tls->lasterror = sock_error();
}
tv.tv_sec = 0;
tv.tv_usec = 1000;
}
FD_ZERO(&fds);
FD_SET(tls->sock, &fds);
sbin[0].cbBuffer = len;
select(tls->sock, &fds, NULL, NULL, &tv);
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), &(tls->hctxt), name,
ctxtreq, 0, 0, &sbdin, 0,
&(tls->hctxt), &sbdout,
&ctxtattr, NULL);
inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
if (inbytes > 0) {
len += inbytes;
p += inbytes;
} else {
tls->lasterror = sock_error(intf);
}
}
sbin[0].cbBuffer = len;
ret = tls->sft->InitializeSecurityContextA(
&(tls->hcred), &(tls->hctxt), name, ctxtreq, 0, 0, &sbdin, 0,
&(tls->hctxt), &sbdout, &ctxtattr, NULL);
}
if (ret == SEC_E_OK) {
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);
if (sent == -1) {
tls->lasterror = sock_error();
}
else
{
writebuff += sent;
writelen -= sent;
}
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
sbdout.pBuffers[0].pvBuffer = NULL;
sbdout.pBuffers[0].cbBuffer = 0;
}
if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
sent = sock_write(intf, writebuff, writelen);
if (sent == -1) {
tls->lasterror = sock_error(intf);
} else {
writebuff += sent;
writelen -= sent;
}
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
sbdout.pBuffers[0].pvBuffer = NULL;
sbdout.pBuffers[0].cbBuffer = 0;
}
}
xmpp_free(tls->ctx, sbin[0].pvBuffer);
strophe_free(tls->ctx, sbin[0].pvBuffer);
if (ret != SEC_E_OK) {
tls->lasterror = ret;
return 0;
tls->lasterror = ret;
strophe_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
(unsigned long)ret);
return 0;
}
tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES,
&(tls->spcss));
tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES,
&(tls->spcss));
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage
+ tls->spcss.cbTrailer;
tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen);
tls->recvbufferpos = 0;
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
tls->spcss.cbTrailer;
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->sendbufferpos = 0;
tls->sendbufferlen = 0;
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
tls->spcss.cbTrailer;
tls->sendbuffer = strophe_alloc(tls->ctx, tls->sendbuffermaxlen);
tls->sendbufferpos = 0;
tls->sendbufferlen = 0;
tls->readybuffer = xmpp_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
tls->readybufferpos = 0;
tls->readybufferlen = 0;
tls->readybuffer = strophe_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
tls->readybufferpos = 0;
tls->readybufferlen = 0;
return 1;
}
int tls_stop(tls_t *tls)
{
UNUSED(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)
{
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE
|| error == WSAEWOULDBLOCK || error == WSAEMSGSIZE
|| error == WSAEINPROGRESS);
UNUSED(intf);
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
error == WSAEINPROGRESS);
}
int tls_pending(tls_t *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
// was incomplete. This should be processed again only if there is data
// on the physical connection
// - there is data on the physical connection. This case is treated
// outside the tls (in event.c)
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
// was incomplete. This should be processed again only if there is data
// on the physical connection
// - there is data on the physical connection. This case is treated
// outside the tls (in event.c)
if (tls->readybufferpos < tls->readybufferlen) {
return tls->readybufferlen - tls->readybufferpos;
}
return tls->readybufferlen - tls->readybufferpos;
}
return 0;
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)
{
if (len < tls->readybufferlen - tls->readybufferpos) {
bytes = len;
} else {
bytes = tls->readybufferlen - tls->readybufferpos;
}
if (tls->readybufferpos < tls->readybufferlen) {
if (len < tls->readybufferlen - tls->readybufferpos) {
bytes = len;
} else {
bytes = tls->readybufferlen - tls->readybufferpos;
}
memcpy(buff, tls->readybuffer + tls->readybufferpos, bytes);
memcpy(buff, tls->readybuffer + tls->readybufferpos, bytes);
if (len < tls->readybufferlen - tls->readybufferpos) {
tls->readybufferpos += bytes;
return bytes;
} else {
unsigned char *newbuff = buff;
int read;
tls->readybufferpos += bytes;
newbuff += bytes;
read = tls_read(tls, newbuff, len - bytes);
if (len < tls->readybufferlen - tls->readybufferpos) {
tls->readybufferpos += bytes;
return bytes;
} else {
unsigned char *newbuff = buff;
int read;
tls->readybufferpos += bytes;
newbuff += bytes;
read = tls_read(tls, newbuff, len - bytes);
if (read == -1) {
if (tls_is_recoverable(tls->lasterror)) {
return bytes;
}
if (read == -1) {
if (tls_is_recoverable(intf, tls->lasterror)) {
return bytes;
}
return -1;
}
return -1;
}
return bytes + read;
}
return bytes + read;
}
}
/* next, top up our recv buffer */
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos,
tls->recvbuffermaxlen - tls->recvbufferpos);
bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
tls->recvbuffermaxlen - tls->recvbufferpos);
if (bytes == 0) {
tls->lasterror = WSAECONNRESET;
return -1;
}
}
if (bytes == -1) {
if (!tls_is_recoverable(sock_error())) {
tls->lasterror = sock_error();
return -1;
}
if (!tls_is_recoverable(intf, sock_error(intf))) {
tls->lasterror = sock_error(intf);
return -1;
}
}
if (bytes > 0) {
tls->recvbufferpos += bytes;
tls->recvbufferpos += bytes;
}
/* next, try to decrypt the recv buffer */
if (tls->recvbufferpos > 0) {
SecBufferDesc sbddec;
SecBuffer sbdec[4];
int ret;
SecBufferDesc sbddec;
SecBuffer sbdec[4];
int ret;
memset(&sbddec, 0, sizeof(sbddec));
sbddec.ulVersion = SECBUFFER_VERSION;
sbddec.cBuffers = 4;
sbddec.pBuffers = sbdec;
memset(&sbddec, 0, sizeof(sbddec));
sbddec.ulVersion = SECBUFFER_VERSION;
sbddec.cBuffers = 4;
sbddec.pBuffers = sbdec;
memset(&(sbdec[0]), 0, sizeof(sbdec[0]));
sbdec[0].BufferType = SECBUFFER_DATA;
sbdec[0].pvBuffer = tls->recvbuffer;
sbdec[0].cbBuffer = tls->recvbufferpos;
memset(&(sbdec[0]), 0, sizeof(sbdec[0]));
sbdec[0].BufferType = SECBUFFER_DATA;
sbdec[0].pvBuffer = tls->recvbuffer;
sbdec[0].cbBuffer = tls->recvbufferpos;
memset(&(sbdec[1]), 0, sizeof(sbdec[1]));
sbdec[1].BufferType = SECBUFFER_EMPTY;
memset(&(sbdec[1]), 0, sizeof(sbdec[1]));
sbdec[1].BufferType = SECBUFFER_EMPTY;
memset(&(sbdec[2]), 0, sizeof(sbdec[2]));
sbdec[2].BufferType = SECBUFFER_EMPTY;
memset(&(sbdec[2]), 0, sizeof(sbdec[2]));
sbdec[2].BufferType = SECBUFFER_EMPTY;
memset(&(sbdec[3]), 0, sizeof(sbdec[3]));
sbdec[3].BufferType = SECBUFFER_EMPTY;
memset(&(sbdec[3]), 0, sizeof(sbdec[3]));
sbdec[3].BufferType = SECBUFFER_EMPTY;
ret = tls->sft->DecryptMessage(&(tls->hctxt), &sbddec, 0, NULL);
ret = tls->sft->DecryptMessage(&(tls->hctxt), &sbddec, 0, NULL);
if (ret == SEC_E_OK) {
memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer);
tls->readybufferpos = 0;
tls->readybufferlen = sbdec[1].cbBuffer;
/* have we got some data left over? If so, copy it to the start
* of the recv buffer */
if (sbdec[3].BufferType == SECBUFFER_EXTRA) {
memcpy(tls->recvbuffer, sbdec[3].pvBuffer, sbdec[3].cbBuffer);
tls->recvbufferpos = sbdec[3].cbBuffer;
} else {
tls->recvbufferpos = 0;
}
if (ret == SEC_E_OK) {
memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer);
tls->readybufferpos = 0;
tls->readybufferlen = sbdec[1].cbBuffer;
/* have we got some data left over? If so, copy it to the start
* of the recv buffer */
if (sbdec[3].BufferType == SECBUFFER_EXTRA) {
memcpy(tls->recvbuffer, sbdec[3].pvBuffer, sbdec[3].cbBuffer);
tls->recvbufferpos = sbdec[3].cbBuffer;
} else {
tls->recvbufferpos = 0;
}
return tls_read(tls, buff, len);
} else if (ret == SEC_E_INCOMPLETE_MESSAGE) {
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
return -1;
} else if (ret == SEC_I_RENEGOTIATE) {
ret = tls_start(tls);
if (!ret)
{
return -1;
}
return tls_read(tls, buff, len);
} else if (ret == SEC_E_INCOMPLETE_MESSAGE) {
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
return -1;
} else if (ret == SEC_I_RENEGOTIATE) {
ret = tls_start(tls);
if (!ret) {
return -1;
}
/* fake an incomplete message so we're called again */
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
return -1;
}
/* fake an incomplete message so we're called again */
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
return -1;
}
/* something bad happened, so we bail */
tls->lasterror = ret;
/* something bad happened, so we bail */
tls->lasterror = ret;
return -1;
return -1;
}
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
@@ -538,41 +579,41 @@ 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)
{
if (tls->sendbufferpos < tls->sendbufferlen)
{
int bytes;
tls_t *tls = intf->conn->tls;
if (tls->sendbufferpos < tls->sendbufferlen) {
int bytes;
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
tls->sendbufferlen - tls->sendbufferpos);
bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
tls->sendbufferlen - tls->sendbufferpos);
if (bytes == -1) {
tls->lasterror = sock_error();
return -1;
} else if (bytes > 0) {
tls->sendbufferpos += bytes;
}
if (bytes == -1) {
tls->lasterror = sock_error(intf);
return -1;
} else if (bytes > 0) {
tls->sendbufferpos += bytes;
}
if (tls->sendbufferpos < tls->sendbufferlen) {
return 0;
}
if (tls->sendbufferpos < tls->sendbufferlen) {
return 0;
}
}
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];
unsigned char *sendbuffer;
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) {
return ret;
return ret;
}
tls->sendbufferpos = 0;
@@ -600,51 +641,50 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
sbenc[1].pvBuffer = tls->sendbuffer + tls->spcss.cbHeader;
while (remain > 0)
{
if (remain > tls->spcss.cbMaximumMessage) {
sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage;
} else {
sbenc[1].cbBuffer = remain;
}
while (remain > 0) {
if (remain > tls->spcss.cbMaximumMessage) {
sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage;
} else {
sbenc[1].cbBuffer = remain;
}
sbenc[2].pvBuffer = (unsigned char *)sbenc[1].pvBuffer
+ sbenc[1].cbBuffer;
sbenc[2].pvBuffer =
(unsigned char *)sbenc[1].pvBuffer + sbenc[1].cbBuffer;
sbenc[2].cbBuffer = tls->spcss.cbTrailer;
memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer);
p += tls->spcss.cbMaximumMessage;
memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer);
p += tls->spcss.cbMaximumMessage;
tls->sendbufferlen = sbenc[0].cbBuffer + sbenc[1].cbBuffer
+ sbenc[2].cbBuffer;
tls->sendbufferlen =
sbenc[0].cbBuffer + sbenc[1].cbBuffer + sbenc[2].cbBuffer;
ret = tls->sft->EncryptMessage(&(tls->hctxt), 0, &sbdenc, 0);
ret = tls->sft->EncryptMessage(&(tls->hctxt), 0, &sbdenc, 0);
if (ret != SEC_E_OK) {
tls->lasterror = ret;
return -1;
}
if (ret != SEC_E_OK) {
tls->lasterror = ret;
return -1;
}
tls->sendbufferpos = 0;
tls->sendbufferpos = 0;
ret = tls_clear_pending_write(tls);
ret = tls_clear_pending_write(tls);
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) {
return -1;
}
if (remain > tls->spcss.cbMaximumMessage) {
sent += tls->spcss.cbMaximumMessage;
remain -= tls->spcss.cbMaximumMessage;
} else {
sent += remain;
remain = 0;
}
if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
return -1;
}
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
return sent;
}
if (remain > tls->spcss.cbMaximumMessage) {
sent += tls->spcss.cbMaximumMessage;
remain -= tls->spcss.cbMaximumMessage;
} else {
sent += remain;
remain = 0;
}
if (ret == 0 ||
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
return sent;
}
}
return sent;

View File

@@ -1,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* util.c
** strophe XMPP client library -- various utility functions
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -18,17 +16,18 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/time.h>
#include <time.h>
#include <stdint.h>
#endif
#include "strophe.h"
#include "common.h"
#include "ostypes.h"
#include "util.h"
/** implement our own strdup that uses the ctx allocator */
@@ -39,25 +38,67 @@
* @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;
}
/** 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().
*/
char *strophe_strtok_r(char *s, const char *delim, char **saveptr)
{
size_t len;
s = s ? s : *saveptr;
len = strspn(s, delim);
s += len;
if (*s == '\0')
return NULL;
len = strcspn(s, delim);
*saveptr = s[len] == '\0' ? &s[len] : &s[len + 1];
s[len] = '\0';
return s;
}
/** Return an integer based time stamp.
* This function uses gettimeofday or timeGetTime (on Win32 platforms) to
* compute an integer based time stamp. This is used internally by the
@@ -67,8 +108,24 @@ char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s)
*/
uint64_t time_stamp(void)
{
#ifdef _WIN32
return timeGetTime();
#if defined(_WIN32) || defined(_XBOX_ONE)
#ifndef __GNUC__
#define EPOCHFILETIME (116444736000000000i64)
#else
#define EPOCHFILETIME (116444736000000000LL)
#endif
FILETIME ft;
LARGE_INTEGER li;
__int64 t;
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
t = li.QuadPart; /* In 100-nanosecond intervals */
t -= EPOCHFILETIME; /* Offset to the Epoch time */
return (uint64_t)(t / 10000); /* Convert to milliseconds */
#else
struct timeval tv;
@@ -95,13 +152,29 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
/** Disconnect the stream with a memory error.
* This is a convenience function used internally by various parts of
* the Strophe library for terminating the connection because of a
* the Strophe library for terminating the connection because of a
* memory error.
*
* @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,15 +1,13 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* util.h
** strophe XMPP client library -- various utility functions
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -19,14 +17,24 @@
#ifndef __LIBSTROPHE_UTIL_H__
#define __LIBSTROPHE_UTIL_H__
#ifndef _WIN32
#include <stdint.h>
#else
#include "ostypes.h"
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif /* ARRAY_SIZE */
/* TODO evaluate x and y only once */
#define xmpp_min(x, y) ((x) < (y) ? (x) : (y))
/* string functions */
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__ */

73
src/uuid.c Normal file
View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* uuid.c
* strophe XMPP client library -- UUID generation
*
* Copyright (C) 2015 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 or GPLv3 licenses.
*/
/** @file
* Generation of UUID version 4 according to RFC4122.
*/
#include "strophe.h"
#include "common.h"
/** @def XMPP_UUID_LEN
* UUID length in string representation excluding '\0'.
*/
#define XMPP_UUID_LEN 36
/** Generate UUID version 4 in pre-allocated buffer.
*
* @param ctx a Strophe context object
* @param uuid pre-allocated buffer of size (XMPP_UUID_LEN + 1)
*/
static void crypto_uuid_gen(xmpp_ctx_t *ctx, char *uuid)
{
unsigned char buf[16];
int i = 0; /* uuid iterator */
int j = 0; /* buf iterator */
static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
xmpp_rand_bytes(ctx->rand, buf, sizeof(buf));
buf[8] &= 0x3f;
buf[8] |= 0x80;
buf[6] &= 0x0f;
buf[6] |= 0x40;
while (i < XMPP_UUID_LEN) {
if (i == 8 || i == 13 || i == 18 || i == 23)
uuid[i++] = '-';
else {
uuid[i++] = hex[buf[j] >> 4];
uuid[i++] = hex[buf[j] & 0x0f];
++j;
}
}
uuid[XMPP_UUID_LEN] = '\0';
}
/** Generate UUID version 4.
* This function allocates memory for the resulting string and must be freed
* with xmpp_free().
*
* @param ctx a Strophe context object
*
* @return ASCIIZ string
*/
char *xmpp_uuid_gen(xmpp_ctx_t *ctx)
{
char *uuid;
uuid = strophe_alloc(ctx, XMPP_UUID_LEN + 1);
if (uuid != NULL) {
crypto_uuid_gen(ctx, uuid);
}
return uuid;
}

692
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,10 +7,7 @@
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This software is dual licensed under the MIT or GPLv3 licenses.
*/
/** @file
@@ -19,12 +17,12 @@
#ifndef __LIBSTROPHE_STROPHE_H__
#define __LIBSTROPHE_STROPHE_H__
#include <stddef.h> /* size_t */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* namespace defines */
/** @def XMPP_NS_CLIENT
* Namespace definition for 'jabber:client'.
@@ -42,6 +40,10 @@ extern "C" {
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'.
*/
#define XMPP_NS_STREAMS_IETF "urn:ietf:params:xml:ns:xmpp-streams"
/** @def XMPP_NS_STANZAS_IETF
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-stanzas'.
*/
#define XMPP_NS_STANZAS_IETF "urn:ietf:params:xml:ns:xmpp-stanzas"
/** @def XMPP_NS_TLS
* Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'.
*/
@@ -74,6 +76,22 @@ extern "C" {
* Namespace definition for 'jabber:iq:roster'.
*/
#define XMPP_NS_ROSTER "jabber:iq:roster"
/** @def XMPP_NS_REGISTER
* 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
@@ -82,7 +100,7 @@ extern "C" {
#define XMPP_EOK 0
/** @def XMPP_EMEM
* Memory related failure error code.
*
*
* This is returned on allocation errors and signals that the host may
* be out of memory.
*/
@@ -117,14 +135,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 *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;
};
@@ -135,21 +161,16 @@ typedef enum {
XMPP_LEVEL_ERROR
} xmpp_log_level_t;
typedef enum {
XMPP_UNKNOWN,
XMPP_CLIENT,
XMPP_COMPONENT
} xmpp_conn_type_t;
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;
void *userdata;
/* mutex_t lock; */
};
/* return a default logger filtering at a given level */
@@ -160,10 +181,38 @@ 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)
#define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1)
#define XMPP_CONN_FLAG_LEGACY_SSL (1UL << 2)
/** @def XMPP_CONN_FLAG_TRUST_TLS
* Trust server's certificate even if it is invalid.
*/
#define XMPP_CONN_FLAG_TRUST_TLS (1UL << 3)
/** @def XMPP_CONN_FLAG_LEGACY_AUTH
* 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 {
XMPP_CONN_CONNECT,
XMPP_CONN_RAW_CONNECT,
XMPP_CONN_DISCONNECT,
XMPP_CONN_FAIL
} xmpp_conn_event_t;
@@ -195,177 +244,530 @@ 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);
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);
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);
/** 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,
unsigned short altport,
xmpp_conn_handler callback,
void * const userdata);
/** 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);
/*
int xmpp_connect_component(conn, name)
*/
void xmpp_disconnect(xmpp_conn_t * const conn);
/* an example callback that sets basic keepalive parameters */
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
void xmpp_send(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza);
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);
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);
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);
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state);
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 *userdata);
int xmpp_connect_component(xmpp_conn_t *conn,
const char *server,
unsigned short port,
xmpp_conn_handler callback,
void *userdata);
int xmpp_connect_raw(xmpp_conn_t *conn,
const char *altdomain,
unsigned short altport,
xmpp_conn_handler callback,
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 *conn);
void xmpp_disconnect(xmpp_conn_t *conn);
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
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 handle returns false it is removed */
typedef int (*xmpp_timed_handler)(xmpp_conn_t * const conn,
void * const userdata);
void xmpp_timed_handler_add(xmpp_conn_t * const 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);
/* if the handler returns false it is removed */
typedef int (*xmpp_timed_handler)(xmpp_conn_t *conn, void *userdata);
void xmpp_timed_handler_add(xmpp_conn_t *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_handler)(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata);
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *ctx, void *userdata);
void xmpp_handler_add(xmpp_conn_t * const 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);
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler,
unsigned long period,
void *userdata);
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
xmpp_global_timed_handler handler);
void xmpp_id_handler_add(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id,
void * const userdata);
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
xmpp_handler handler,
const char * const id);
/* if the handler returns false it is removed */
typedef int (*xmpp_handler)(xmpp_conn_t *conn,
xmpp_stanza_t *stanza,
void *userdata);
void xmpp_handler_add(xmpp_conn_t *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 *conn,
xmpp_handler handler,
const char *id,
void *userdata);
void xmpp_id_handler_delete(xmpp_conn_t *conn,
xmpp_handler handler,
const char *id);
/*
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
*/
/** stanzas **/
/* stanzas */
/** allocate an initialize a blank stanza */
/* allocate and initialize a blank stanza */
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);
/* clone a 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);
/* copies a stanza and all children */
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);
/* free a stanza object and it's contents */
int xmpp_stanza_release(xmpp_stanza_t *stanza);
/** 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);
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);
/* marshall a stanza into text for transmission or display */
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_next(xmpp_stanza_t * const stanza);
char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
const char * const name);
char * xmpp_stanza_get_ns(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 *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);
char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza);
char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
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);
const char *text,
size_t size);
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name);
/* common stanza helpers */
char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
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);
/* unimplemented
int xmpp_stanza_set_to();
int xmpp_stanza_set_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 */
/* unimplemented
xmpp_stanza_t *xmpp_stanza_reply(const xmpp_stanza_t *stanza);
*/
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 */
/* unimplemented
void xmpp_message_new();
void xmpp_message_get_body();
void xmpp_message_set_body();
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
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 *text);
void xmpp_iq_new();
void xmpp_presence_new();
*/
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 type, const char *text);
/** event loop **/
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
/* jid */
/* these return new strings that must be xmpp_free()'d */
char *xmpp_jid_new(xmpp_ctx_t *ctx,
const char *node,
const char *domain,
const char *resource);
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
/* event loop */
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 *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);
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 */
char *xmpp_uuid_gen(xmpp_ctx_t *ctx);
/* SHA1 */
/** @def XMPP_SHA1_DIGEST_SIZE
* Size of the SHA1 message digest.
*/
#define XMPP_SHA1_DIGEST_SIZE 20
typedef struct _xmpp_sha1_t xmpp_sha1_t;
char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
void xmpp_sha1_digest(const unsigned char *data,
size_t len,
unsigned char *digest);
xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx);
void xmpp_sha1_free(xmpp_sha1_t *sha1);
void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len);
void xmpp_sha1_final(xmpp_sha1_t *sha1);
char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen);
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
/* Base64 */
char *
xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len);
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
const char *base64,
size_t len,
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
}

View File

@@ -1,136 +0,0 @@
/* strophepp.h
** strophe XMPP client library C++ API
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
/** @file
* Strophe public C++ API definitions.
*/
#ifndef __LIBSTROPHE_STROPHEPP_H__
#define __LIBSTROPHE_STROPHEPP_H__
#include "strophe.h"
namespace XMPP {
class Context {
private:
xmpp_mem_t m_mem;
xmpp_log_t m_log;
xmpp_ctx_t *m_ctx;
public:
Context();
virtual ~Context();
virtual void *alloc(const size_t size);
virtual void *realloc(void *p, const size_t size);
virtual void free(void *p);
virtual void log(const xmpp_log_level_t level,
const char * const area,
const char * const msg);
xmpp_ctx_t *getContext();
private:
static void *callAlloc(const size_t size, void * const userdata);
static void *callRealloc(void *p, const size_t size,
void * const userdata);
static void callFree(void *p, void * const userdata);
static void callLog(void * const userdata,
const xmpp_log_level_t level,
const char * const area,
const char * const msg);
};
class Stanza {
private:
Context *m_ctx;
xmpp_stanza_t *m_stanza;
void *operator new(size_t size, Context *ctx);
void operator delete(void *p);
Stanza(Context *ctx);
virtual ~Stanza();
public:
static Stanza *create(Context *ctx);
void release();
Stanza *clone();
Stanza *copy();
int toText(const char ** const buf, size_t * const buflen);
Stanza *getChildren();
Stanza *getChildByName(const char * const name);
Stanza *getNext();
char *getAttribute(const char * const name);
char *getNamespace();
char *getText();
char *getName();
void addChild(Stanza *child);
void setNamespace(const char * const ns);
void setAttribute(const char * const key, const char * const value);
void setName(const char * const name);
void setText(const char * const text);
void setText(const char * const text, const size_t size);
char *getType();
char *getId();
char *getTo();
char *getFrom();
void setType(const char * const type);
void setId(const char * const id);
void setTo(const char * const to);
void setFrom(const char * const from);
};
class Connection {
private:
Context *m_ctx;
xmpp_conn_t *conn;
void *operator new(size_t size, Context *ctx);
Connection(Context *ctx);
public:
static Connection *create(Context *ctx);
virtual ~Connection();
Connection *clone();
void operator delete(void *p);
const char *getJID();
void setJID(const char * const jid);
const char *getPass();
void setPass(const char * const pass);
bool connectClient(const char * const domain,
xmpp_conn_handler callback,
void * const userdata);
void disconnect();
void send(Stanza *stanza);
void addTimedHandler(xmpp_timed_handler handler,
const unsigned long perdio,
void * const userdata);
void deleteTimedHandler(xmpp_timed_handler handler);
void addHandler(xmpp_handler handler,
const char * const ns,
const char * const name,
const char * const type,
void * const userdata);
void deleteHandler(xmpp_handler handler);
void addIdHandler(xmpp_handler handler,
const char * const id,
void * const userdata);
void deleteIdHandler(xmpp_handler handler);
};
}
#endif /* __LIBSTROPHE_STROPHEPP_H__ */

13
testbuild.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
logfile="../../testbuild.log"
err_out() {
tail $logfile
exit 1
}
./bootstrap.sh
./configure >> $logfile || err_out
make -j$(( `nproc` * 2 + 1 )) >> $logfile || err_out
make check >> $logfile || 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,75 +1,90 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* check_parser.h
** strophe XMPP client library -- parser tests
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express or
** implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** This program is dual licensed under the MIT or GPLv3 licenses.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <check.h>
#include <strophe.h>
#include "strophe.h"
#include "parser.h"
#include "test.h"
START_TEST(create_destroy)
#define fail_unless(expr) \
do { \
int result = (expr); \
if (!result) { \
printf("%s:%d: Assertion failed: %s\n", __FILE__, __LINE__, \
#expr); \
exit(1); \
} \
} while (0)
static void create_destroy(void)
{
xmpp_ctx_t *ctx;
parser_t *parser;
ctx = xmpp_ctx_new(NULL, NULL);
parser = parser_new(ctx, NULL, NULL, NULL, NULL);
fail_unless(parser != NULL, "Parser creation failed.");
fail_unless(parser != NULL);
parser_free(parser);
xmpp_ctx_free(ctx);
}
END_TEST
int cbtest_got_start = 0;
void cbtest_handle_start(char *name, char **attrs, void *userdata)
{
if (strcmp(name, "stream:stream") == 0)
(void)attrs;
(void)userdata;
if (strcmp(name, "stream") == 0)
cbtest_got_start = 1;
}
int cbtest_got_end = 0;
void cbtest_handle_end(char *name, void *userdata)
{
if (strcmp(name, "stream:stream") == 0)
(void)userdata;
if (strcmp(name, "stream") == 0)
cbtest_got_end = 1;
}
int cbtest_got_stanza = 0;
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
{
(void)userdata;
if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0)
cbtest_got_stanza = 1;
}
START_TEST(callbacks)
static void callbacks(void)
{
xmpp_ctx_t *ctx;
parser_t *parser;
int ret;
ctx = xmpp_ctx_new(NULL, NULL);
parser = parser_new(ctx,
cbtest_handle_start,
cbtest_handle_end,
parser = parser_new(ctx, cbtest_handle_start, cbtest_handle_end,
cbtest_handle_stanza, NULL);
ret = parser_feed(parser, "<stream:stream>", 15);
ret = parser_feed(parser, "<stream>", 8);
fail_unless(ret != 0);
ret = parser_feed(parser, "<message/>", 10);
parser_feed(parser, "</stream:stream>", 16);
fail_unless(ret != 0);
ret = parser_feed(parser, "</stream>", 9);
fail_unless(ret != 0);
fail_unless(cbtest_got_start == 1);
fail_unless(cbtest_got_end == 1);
@@ -78,16 +93,18 @@ START_TEST(callbacks)
parser_free(parser);
xmpp_ctx_free(ctx);
}
END_TEST
Suite *parser_suite(void)
int main()
{
Suite *s = suite_create("Parser");
TCase *tc_core = tcase_create("Core");
tcase_add_test(tc_core, create_destroy);
tcase_add_test(tc_core, callbacks);
suite_add_tcase(s, tc_core);
return s;
}
printf("XML parser tests.\n");
TEST_MAIN
printf("create-destroy: ");
create_destroy();
printf("ok\n");
printf("callbacks: ");
callbacks();
printf("ok\n");
return 0;
}

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

91
tests/res_query_dump.c Normal file
View File

@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* res_query_dump.c
* Simple program to dump res_query(3) response
*
* 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 or GPLv3 licenses.
*/
/* Linux and OSX:
* gcc -o res_query_dump tests/res_query_dump.c -lresolv
* *BSD:
* gcc -o res_query_dump tests/res_query_dump.c
* QNX:
* gcc -o res_query_dump tests/res_query_dump.c -lsocket
* Solaris:
* gcc -o res_query_dump tests/res_query_dump.c -lresolv -lsocket -lnsl
*/
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifndef T_SRV
#define T_SRV 33
#endif /* T_SRV */
#ifndef C_IN
#define C_IN 1
#endif /* C_IN */
#define STEP 10
int main(int argc, char **argv)
{
unsigned char buf[65536];
char fulldomain[2048];
char *service = "xmpp-client";
char *proto = "tcp";
char *domain = NULL;
int len;
int i;
int j;
if (argc < 2) {
fprintf(stderr, "%s: argument missed\n", argc > 0 ? argv[0] : "$0");
fprintf(stderr, "Usage: %s <domain>\n", argc > 0 ? argv[0] : "$0");
return 1;
}
domain = argv[1];
snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service, proto,
domain);
errno = 0;
len = res_query(fulldomain, C_IN, T_SRV, buf, sizeof(buf));
if (len < 0) {
fprintf(stderr, "res_query(): Error occurred (errno=%d)\n", errno);
}
if (len == 0) {
fprintf(stderr, "res_query(): Empty result\n");
}
if (len > 0) {
printf("/* res_query(\"%s\", C_IN, T_SRV, ...) */\n", fulldomain);
printf("static const unsigned char data[] = {\n");
for (i = 0; i < len; i += STEP) {
printf(" ");
for (j = i; j < len && j < i + STEP; ++j) {
printf(" 0x%02x,", buf[j]);
}
for (j = len; j < i + STEP; ++j) {
printf(" ");
}
printf(" // ");
for (j = i; j < len && j < i + STEP; ++j) {
printf("%c", isprint(buf[j]) ? buf[j] : '.');
}
printf("\n");
}
printf("};\n");
}
return len <= 0;
}

55
tests/test.c Normal file
View File

@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test.c
* strophe XMPP client library -- common routines for tests
*
* 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 or GPLv3 licenses.
*/
#include <assert.h>
#include <string.h>
#include "test.h"
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);
}
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len)
{
size_t len = strlen(hex);
size_t i;
assert(len % 2 == 0);
for (i = 0; i < len / 2; ++i) {
bin[i] = char_to_bin(hex[i * 2]) * 16 + char_to_bin(hex[i * 2 + 1]);
}
*bin_len = len / 2;
}
const char *test_bin_to_hex(const uint8_t *bin, size_t len)
{
static char buf[2048];
size_t i;
static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
assert(ARRAY_SIZE(buf) > len * 2);
for (i = 0; i < len; ++i) {
buf[i * 2] = hex[(bin[i] >> 4) & 0x0f];
buf[i * 2 + 1] = hex[bin[i] & 0x0f];
}
buf[len * 2] = '\0';
return buf;
}

View File

@@ -1,16 +1,85 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test.h
** libstrophe XMPP client library -- common routines for tests
**
** 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.
*/
#ifndef __LIBSTROPHE_TEST_H__
#define __LIBSTROPHE_TEST_H__
#define TEST_MAIN \
int main(int argc, char **argv) {\
int num_failed;\
Suite *s = parser_suite();\
SRunner *sr = srunner_create(s);\
srunner_run_all(sr, CK_NORMAL);\
num_failed = srunner_ntests_failed(sr);\
srunner_free(sr);\
return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;\
}\
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ostypes.h"
#define TEST_MAIN \
int main(int argc, char **argv) \
{ \
int num_failed; \
Suite *s = parser_suite(); \
SRunner *sr = srunner_create(s); \
srunner_run_all(sr, CK_NORMAL); \
num_failed = srunner_ntests_failed(sr); \
srunner_free(sr); \
return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; \
}
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
#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(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);
const char *test_bin_to_hex(const uint8_t *bin, size_t len);
#endif /* __LIBSTROPHE_TEST_H__ */

View File

@@ -1,151 +1,164 @@
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
/* test_base64.c
** libstrophe XMPP client library -- test routines for the base64 codec
**
** Copyright (C) 2005-2009 Collecta, Inc.
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
** 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 "sasl.h"
static const unsigned char text_1[] = "How now brown cow?";
static const char base64_1[] = "SG93IG5vdyBicm93biBjb3c/";
static const unsigned char text_2[] = {0x14,0xfb,0x9c,0x03,0xd9,0x7e,0x00};
static const char base64_2[] = "FPucA9l+";
static const unsigned char text_3[] =
"From rest and sleep, which but thy pictures be, "
"Much pleasure; then from thee much more must flow, "
"And soonest our best men with thee do go, "
"Rest of their bones, and soul's delivery.";
static const char base64_3[] =
"RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
"dWwncyBkZWxpdmVyeS4=";
static const unsigned char text_4[] =
{0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd, 0xf3, 0xd5,
0x41, 0xbc, 0x1b, 0xe9, 0xdf, 0xe9, 0xb3, 0x08, 0x00};
static const char base64_4[] = "1i8nSX7d89VBvBvp3+mzCA==";
static const char text_5[] =
"realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
static const char base64_5[] =
"cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz";
#include "test.h"
int test_encode(xmpp_ctx_t *ctx)
{
char *result;
result = base64_encode(ctx, text_1, strlen(text_1));
if (result == NULL) return 2;
if (strlen(result) != strlen(base64_1)) return 1;
if (strncmp(base64_1, result, strlen(base64_1))) return 1;
xmpp_free(ctx,result);
static const unsigned char test_2_raw[] = {0x14, 0xfb, 0x9c, 0x03,
0xd9, 0x7e, 0x00};
static const unsigned char test_4_raw[] = {0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd,
0xf3, 0xd5, 0x41, 0xbc, 0x1b, 0xe9,
0xdf, 0xe9, 0xb3, 0x08, 0x00};
result = base64_encode(ctx, text_2, strlen(text_2));
if (result == NULL) return 2;
if (strlen(result) != strlen(base64_2)) return 1;
if (strncmp(base64_2, result, strlen(base64_2))) return 1;
xmpp_free(ctx,result);
static const struct {
char *raw;
char *base64;
} tests[] = {
{
.raw = "How now brown cow?",
.base64 = "SG93IG5vdyBicm93biBjb3c/",
},
{
.raw = (char *)test_2_raw,
.base64 = "FPucA9l+",
},
{
.raw = "From rest and sleep, which but thy pictures be, "
"Much pleasure; then from thee much more must flow, "
"And soonest our best men with thee do go, "
"Rest of their bones, and soul's delivery.",
.base64 = "RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
"dWwncyBkZWxpdmVyeS4=",
},
{
.raw = (char *)test_4_raw,
.base64 = "1i8nSX7d89VBvBvp3+mzCA==",
},
{
.raw = "realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83"
"a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
.base64 = "cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz",
},
result = base64_encode(ctx, text_3, strlen(text_3));
if (result == NULL) return 2;
if (strlen(result) != strlen(base64_3)) return 1;
if (strncmp(base64_3, result, strlen(base64_3))) return 1;
xmpp_free(ctx,result);
/* RFC4648 test vectors */
{
.raw = "",
.base64 = "",
},
{
.raw = "f",
.base64 = "Zg==",
},
{
.raw = "fo",
.base64 = "Zm8=",
},
{
.raw = "foo",
.base64 = "Zm9v",
},
{
.raw = "foob",
.base64 = "Zm9vYg==",
},
{
.raw = "fooba",
.base64 = "Zm9vYmE=",
},
{
.raw = "foobar",
.base64 = "Zm9vYmFy",
},
};
result = base64_encode(ctx, text_4, strlen(text_4));
if (result == NULL) return 2;
if (strlen(result) != strlen(base64_4)) return 1;
if (strncmp(base64_4, result, strlen(base64_4))) return 1;
xmpp_free(ctx,result);
static const unsigned char bin_data[] = {
0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x6d, 0x61, 0x69, 0x6c,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00,
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70,
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c,
0x74, 0x34, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00,
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x04, 0x78, 0x6d, 0x70,
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x43, 0x00, 0x19, 0x00, 0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d,
0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61,
0x6c, 0x74, 0x33, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
};
result = base64_encode(ctx, text_5, strlen(text_5));
if (result == NULL) return 2;
if (strlen(result) != strlen(base64_5)) return 1;
if (strncmp(base64_5, result, strlen(base64_5))) return 1;
xmpp_free(ctx,result);
return 0;
}
int test_decode(xmpp_ctx_t *ctx)
{
unsigned char *result;
result = base64_decode(ctx, base64_1, strlen(base64_1));
if (result == NULL) return 2;
if (strlen(result) != strlen(text_1)) return 1;
if (strncmp(text_1, result, strlen(text_1))) return 1;
xmpp_free(ctx,result);
result = base64_decode(ctx, base64_2, strlen(base64_2));
if (result == NULL) return 2;
if (strlen(result) != strlen(text_2)) return 1;
if (strncmp(text_2, result, strlen(text_2))) return 1;
xmpp_free(ctx,result);
result = base64_decode(ctx, base64_3, strlen(base64_3));
if (result == NULL) return 2;
if (strlen(result) != strlen(text_3)) return 1;
if (strncmp(text_3, result, strlen(text_3))) return 1;
xmpp_free(ctx,result);
result = base64_decode(ctx, base64_4, strlen(base64_4));
if (result == NULL) return 2;
if (strlen(result) != strlen(text_4)) return 1;
if (strncmp(text_4, result, strlen(text_4))) return 1;
xmpp_free(ctx,result);
result = base64_decode(ctx, base64_5, strlen(base64_5));
if (result == NULL) return 2;
if (strlen(result) != strlen(text_5)) return 1;
if (strncmp(text_5, result, strlen(text_5))) return 1;
xmpp_free(ctx,result);
return 0;
}
int main(int argc, char *argv[])
int main()
{
xmpp_ctx_t *ctx;
int ret;
unsigned char *udec;
char *dec;
char *enc;
size_t len, i;
printf("BASE64 tests.\n");
printf("allocating context... ");
ctx = xmpp_ctx_new(NULL, NULL);
if (ctx == NULL) printf("failed to create context\n");
if (ctx == NULL) return -1;
printf("ok.\n");
if (ctx == NULL) {
fprintf(stderr, "failed to create context\n");
return 1;
}
printf("testing encode... ");
ret = test_encode(ctx);
if (ret) printf("base64_encode failed!\n");
if (ret) return ret;
printf("ok.\n");
for (i = 0; i < ARRAY_SIZE(tests); ++i) {
printf("Test #%d: ", (int)i + 1);
enc = xmpp_base64_encode(ctx, (unsigned char *)tests[i].raw,
strlen(tests[i].raw));
assert(enc != NULL);
COMPARE(tests[i].base64, enc);
xmpp_free(ctx, enc);
printf("testing decode... ");
ret = test_decode(ctx);
if (ret) printf("base64_decode failed!\n");
if (ret) return ret;
printf("ok.\n");
dec = xmpp_base64_decode_str(ctx, tests[i].base64,
strlen(tests[i].base64));
assert(dec != NULL);
COMPARE_BUF(tests[i].raw, strlen(tests[i].raw), dec, strlen(dec));
xmpp_free(ctx, dec);
printf("ok\n");
}
printf("Test with binary data: ");
enc = xmpp_base64_encode(ctx, bin_data, sizeof(bin_data));
assert(enc != NULL);
xmpp_base64_decode_bin(ctx, enc, strlen(enc), &udec, &len);
assert(udec != NULL);
assert(len != 0);
assert(len == sizeof(bin_data));
COMPARE_BUF(bin_data, sizeof(bin_data), udec, len);
xmpp_free(ctx, udec);
xmpp_free(ctx, enc);
printf("ok\n");
printf("freeing context... ");
xmpp_ctx_free(ctx);
printf("ok.\n");
return ret;
return 0;
}

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