186 Commits

Author SHA1 Message Date
Dmitry Podgorny
070a64d9d5 WIP Unify error codes returned to the connection handler
Error argument in the connection handler is not always set and in some
cases provides meaningless values. For example, every TLS module returns
its specific error codes.

Current solution unifies error codes and allows to distinguish between
different types of disconnection.

Fixes #123.
2020-09-16 02:28:37 +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
87 changed files with 8354 additions and 5212 deletions

11
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.swp *.swp
*.orig
Makefile Makefile
test-driver test-driver
.libs .libs
@@ -34,8 +35,10 @@ examples/active
examples/basic examples/basic
examples/bot examples/bot
examples/component examples/component
examples/register
examples/roster examples/roster
examples/uuid examples/uuid
examples/vcard
test_stamp test_stamp
test-suite.log test-suite.log
tests/*.o tests/*.o
@@ -52,11 +55,17 @@ tests/test_resolver
tests/test_sasl tests/test_sasl
tests/test_scram tests/test_scram
tests/test_sha1 tests/test_sha1
tests/test_sha256
tests/test_sha512
tests/test_snprintf tests/test_snprintf
tests/test_string
tests/test_sock tests/test_sock
tests/test_stanza
tests/test_string
m4/ m4/
libstrophe.project libstrophe.project
libs/ libs/
obj/ obj/
expat/ expat/
.settings/
.project
.cproject

View File

@@ -1,11 +1,24 @@
language: c language: c
install: install:
- sudo apt-get update - sudo apt-get update
- sudo apt-get -y install libtool pkg-config libexpat1-dev libxml2-dev libssl-dev check - sudo apt-get -y install libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libc-ares-dev dos2unix
script: stages:
- style
- test
before_script:
- ./bootstrap.sh - ./bootstrap.sh
- ./configure --without-libxml2 && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS env:
- make clean - CONFIGURE_OPT="--without-libxml2"
- ./configure --with-libxml2 && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS - CONFIGURE_OPT="--with-libxml2"
- make clean - CONFIGURE_OPT="--disable-tls"
- ./configure --disable-tls && make CFLAGS="-Wall -Werror" && make CFLAGS="-Wall -Werror" check-TESTS - CONFIGURE_OPT="--enable-cares"
script: ./configure ${CONFIGURE_OPT} CFLAGS="-Werror" && make && make check
jobs:
include:
- stage: style
name: "Check code style"
script: ./configure && make format && git diff --exit-code
env:
- CONFIGURE_OPT=""
matrix:
fast_finish: true

View File

@@ -1,3 +1,58 @@
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 0.9.0
- IPv6 support - IPv6 support
- Legacy SSL support - Legacy SSL support

View File

@@ -1,7 +1,6 @@
AUTOMAKE_OPTIONS = subdir-objects AUTOMAKE_OPTIONS = subdir-objects
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
AM_CFLAGS = -g -Wall
PARSER_CFLAGS=@PARSER_CFLAGS@ PARSER_CFLAGS=@PARSER_CFLAGS@
PARSER_LIBS=@PARSER_LIBS@ PARSER_LIBS=@PARSER_LIBS@
@@ -9,26 +8,61 @@ PARSER_LIBS=@PARSER_LIBS@
SSL_CFLAGS = @openssl_CFLAGS@ SSL_CFLAGS = @openssl_CFLAGS@
SSL_LIBS = @openssl_LIBS@ SSL_LIBS = @openssl_LIBS@
RESOLV_CFLAGS = @RESOLV_CFLAGS@
RESOLV_LIBS = @RESOLV_LIBS@ RESOLV_LIBS = @RESOLV_LIBS@
STROPHE_FLAGS = -I$(top_srcdir) WARNING_FLAGS = @WARNING_FLAGS@
STROPHE_FLAGS = -I$(top_srcdir) $(WARNING_FLAGS)
STROPHE_LIBS = libstrophe.la STROPHE_LIBS = libstrophe.la
## Main build targets ## Main build targets
lib_LTLIBRARIES = libstrophe.la lib_LTLIBRARIES = libstrophe.la
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS)
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) -no-undefined libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) -no-undefined
# Export only public API # Export only public API
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_'
libstrophe_la_SOURCES = 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 \ libstrophe_la_SOURCES = \
src/resolver.c src/sasl.c src/scram.c src/sha1.c \ src/auth.c \
src/snprintf.c src/sock.c src/stanza.c src/util.c \ src/conn.c \
src/rand.c src/uuid.c \ src/crypto.c \
src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \ src/ctx.c \
src/resolver.h src/sasl.h src/scram.h src/sha1.h src/snprintf.h \ src/event.c \
src/sock.h src/tls.h src/util.h src/rand.h 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/snprintf.c \
src/sock.c \
src/stanza.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/rand.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 DISABLE_TLS if DISABLE_TLS
libstrophe_la_SOURCES += src/tls_dummy.c libstrophe_la_SOURCES += src/tls_dummy.c
@@ -43,23 +77,39 @@ libstrophe_la_SOURCES += src/parser_libxml2.c
endif endif
include_HEADERS = strophe.h include_HEADERS = strophe.h
noinst_HEADERS = strophepp.h
pkgconfig_DATA = libstrophe.pc pkgconfig_DATA = libstrophe.pc
EXTRA_DIST = docs rpm Doxyfile LICENSE.txt GPL-LICENSE.txt MIT-LICENSE.txt \ EXTRA_DIST = \
src/tls_dummy.c src/tls_gnutls.c src/tls_schannel.c \ _clang-format \
examples/README.md Doxyfile \
GPL-LICENSE.txt \
LICENSE.txt \
MIT-LICENSE.txt \
bootstrap.sh \
build-android.sh \
docs/footer.html \
examples/README.md \
jni/Android.mk \
jni/Application.mk \
src/tls_gnutls.c \
src/tls_schannel.c \
tests/res_query_dump.c
## Examples ## Examples
noinst_PROGRAMS = examples/active examples/roster examples/basic examples/bot \ noinst_PROGRAMS = \
examples/component examples/uuid examples/active \
examples/basic \
examples/bot \
examples/component \
examples/roster \
examples/uuid \
examples/vcard \
examples/register
examples_active_SOURCES = examples/active.c examples_active_SOURCES = examples/active.c
examples_active_CFLAGS = $(STROPHE_FLAGS) examples_active_CFLAGS = $(STROPHE_FLAGS)
examples_active_LDADD = $(STROPHE_LIBS) examples_active_LDADD = $(STROPHE_LIBS)
examples_roster_SOURCES = examples/roster.c
examples_roster_CFLAGS = $(STROPHE_FLAGS)
examples_roster_LDADD = $(STROPHE_LIBS)
examples_basic_SOURCES = examples/basic.c examples_basic_SOURCES = examples/basic.c
examples_basic_CFLAGS = $(STROPHE_FLAGS) examples_basic_CFLAGS = $(STROPHE_FLAGS)
examples_basic_LDADD = $(STROPHE_LIBS) examples_basic_LDADD = $(STROPHE_LIBS)
@@ -69,21 +119,44 @@ examples_bot_LDADD = $(STROPHE_LIBS)
examples_component_SOURCES = examples/component.c examples_component_SOURCES = examples/component.c
examples_component_CFLAGS = $(STROPHE_FLAGS) examples_component_CFLAGS = $(STROPHE_FLAGS)
examples_component_LDADD = $(STROPHE_LIBS) examples_component_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_SOURCES = examples/uuid.c
examples_uuid_CFLAGS = $(STROPHE_FLAGS) examples_uuid_CFLAGS = $(STROPHE_FLAGS)
examples_uuid_LDADD = $(STROPHE_LIBS) examples_uuid_LDADD = $(STROPHE_LIBS)
examples_vcard_SOURCES = examples/vcard.c
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
examples_vcard_LDADD = $(STROPHE_LIBS)
examples_register_SOURCES = examples/register.c
examples_register_CFLAGS = $(STROPHE_FLAGS)
examples_register_LDADD = $(STROPHE_LIBS)
## Tests ## Tests
TESTS = tests/check_parser tests/test_sha1 tests/test_md5 tests/test_rand \ TESTS = \
tests/test_scram tests/test_ctx tests/test_base64 tests/test_jid \ tests/check_parser \
tests/test_snprintf tests/test_string tests/test_resolver tests/test_sha1 \
tests/test_sha256 \
tests/test_sha512 \
tests/test_md5 \
tests/test_rand \
tests/test_scram \
tests/test_ctx \
tests/test_base64 \
tests/test_hash \
tests/test_jid \
tests/test_snprintf \
tests/test_string \
tests/test_stanza \
tests/test_resolver
check_PROGRAMS = $(TESTS) check_PROGRAMS = $(TESTS)
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
tests_check_parser_CFLAGS = @check_CFLAGS@ $(PARSER_CFLAGS) $(STROPHE_FLAGS) \ tests_check_parser_CFLAGS = $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/src
tests_check_parser_LDADD = @check_LIBS@ $(STROPHE_LIBS) tests_check_parser_LDADD = $(STROPHE_LIBS)
tests_check_parser_LDFLAGS = -static tests_check_parser_LDFLAGS = -static
tests_test_ctx_SOURCES = tests/test_ctx.c tests_test_ctx_SOURCES = tests/test_ctx.c
@@ -96,6 +169,11 @@ tests_test_base64_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_base64_LDADD = $(STROPHE_LIBS) tests_test_base64_LDADD = $(STROPHE_LIBS)
tests_test_base64_LDFLAGS = -static 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_SOURCES = tests/test_jid.c
tests_test_jid_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src tests_test_jid_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_jid_LDADD = $(STROPHE_LIBS) tests_test_jid_LDADD = $(STROPHE_LIBS)
@@ -109,12 +187,19 @@ tests_test_resolver_LDFLAGS = -static
tests_test_rand_SOURCES = tests/test_rand.c tests/test.c src/sha1.c 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_rand_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_scram_SOURCES = tests/test_scram.c tests/test.c src/sha1.c 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_scram_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_sha1_SOURCES = tests/test_sha1.c src/sha1.c tests_test_sha1_SOURCES = tests/test_sha1.c src/sha1.c
tests_test_sha1_CFLAGS = -I$(top_srcdir)/src 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_SOURCES = tests/test_md5.c tests/test.c src/md5.c
tests_test_md5_CFLAGS = -I$(top_srcdir)/src tests_test_md5_CFLAGS = -I$(top_srcdir)/src
@@ -125,3 +210,13 @@ tests_test_string_SOURCES = tests/test_string.c tests/test.h
tests_test_string_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src tests_test_string_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
tests_test_string_LDADD = $(STROPHE_LIBS) tests_test_string_LDADD = $(STROPHE_LIBS)
tests_test_string_LDFLAGS = -static tests_test_string_LDFLAGS = -static
tests_test_stanza_SOURCES = tests/test_stanza.c
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS)
tests_test_stanza_LDADD = $(STROPHE_LIBS)
tests_test_stanza_LDFLAGS = -static
format:
@echo " * run clang-format on all sources"
@dos2unix -q src/*.[ch] *.h tests/*.[ch] examples/*.c
@clang-format -i src/*.[ch] *.h tests/*.[ch] examples/*.c

View File

@@ -5,11 +5,7 @@ libstrophe is a lightweight XMPP client library written in C. It has
minimal dependencies and is configurable for various environments. It minimal dependencies and is configurable for various environments. It
runs well on Linux, Unix and Windows based platforms. runs well on Linux, Unix and Windows based platforms.
Its goals are: libstrophe is dual licensed under MIT and GPLv3.
- usable quickly
- well documented
- reliable
Build Instructions Build Instructions
------------------ ------------------
@@ -58,9 +54,6 @@ libstrophe requires:
To build libstrophe using autotools you will need autoconf, automake, To build libstrophe using autotools you will need autoconf, automake,
libtool and pkg-config. libtool and pkg-config.
In addition, if you wish to run the unit tests, you will need the
check package.
Installation Installation
------------ ------------

17
_clang-format Normal file
View File

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

View File

@@ -1,4 +1,4 @@
AC_INIT([libstrophe], [0.9.0], [jack@metajack.im]) AC_INIT([libstrophe], [0.10.0], [jack@metajack.im])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign]) AM_INIT_AUTOMAKE([foreign])
LT_INIT([dlopen]) LT_INIT([dlopen])
@@ -15,15 +15,25 @@ AS_CASE([$host_os],
[*nto*|*qnx*], [PLATFORM="qnx"], [*nto*|*qnx*], [PLATFORM="qnx"],
[*solaris*], [PLATFORM="solaris"], [*solaris*], [PLATFORM="solaris"],
[*android*], [PLATFORM="android"], [*android*], [PLATFORM="android"],
[*haiku*], [PLATFORM="haiku"],
[PLATFORM="nix"]) [PLATFORM="nix"])
WARNING_FLAGS="-Wall"
AS_CASE([$PLATFORM],
[haiku], [],
[WARNING_FLAGS="$WARNING_FLAGS -Wextra"])
AC_ARG_WITH([libxml2], AC_ARG_WITH([libxml2],
[AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing, expat is the default])]) [AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing, expat is the default])])
AC_ARG_ENABLE([tls], AC_ARG_ENABLE([tls],
[AS_HELP_STRING([--disable-tls], [disable TLS support])]) [AS_HELP_STRING([--disable-tls], [disable TLS support])])
AC_ARG_ENABLE([cares],
[AS_HELP_STRING([--enable-cares], [use c-ares for DNS resolution])])
PKG_CHECK_MODULES([check], [check >= 0.9.4], AC_SEARCH_LIBS([socket], [network socket])
[], [AC_MSG_WARN([libcheck not found; unit tests will not be compilable])]) AC_CHECK_FUNCS([snprintf vsnprintf])
AC_CHECK_DECLS([va_copy], [], [], [#include <stdarg.h>])
if test "x$enable_tls" != xno; then if test "x$enable_tls" != xno; then
PKG_CHECK_MODULES([openssl], [openssl], PKG_CHECK_MODULES([openssl], [openssl],
@@ -65,7 +75,7 @@ if test "x$with_libxml2" != xno -a "x$with_parser" = x; then
[ [
with_parser="libxml2" with_parser="libxml2"
libxml2_CFLAGS="-I${includedir}/libxml2" libxml2_CFLAGS="-I${includedir}/libxml2"
libxml2_LIBS="-lxml2" libxml2_LIBS="-lxml2 -lm"
PC_LIBS="${libxml2_LIBS} ${PC_LIBS}" PC_LIBS="${libxml2_LIBS} ${PC_LIBS}"
PC_CFLAGS="${libxml2_CFLAGS} ${PC_CFLAGS}" PC_CFLAGS="${libxml2_CFLAGS} ${PC_CFLAGS}"
]) ])
@@ -85,13 +95,24 @@ fi
AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser]) AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
AC_SEARCH_LIBS([socket], [socket]) 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
AS_CASE([$PLATFORM], AS_CASE([$PLATFORM],
[bsd], [RESOLV_LIBS=""], [bsd], [RESOLV_LIBS=""],
[qnx], [RESOLV_LIBS="-lsocket"], [qnx], [RESOLV_LIBS="-lsocket"],
[solaris], [RESOLV_LIBS="-lresolv -lsocket -lnsl"], [solaris], [RESOLV_LIBS="-lresolv -lsocket -lnsl"],
[android], [RESOLV_LIBS=""], [android], [RESOLV_LIBS=""],
[haiku], [RESOLV_LIBS="-lnetwork"],
[RESOLV_LIBS="-lresolv"]) [RESOLV_LIBS="-lresolv"])
LIBS_TMP="${LIBS}" LIBS_TMP="${LIBS}"
@@ -113,6 +134,8 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
LIBS="${LIBS_TMP}" LIBS="${LIBS_TMP}"
PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}" PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}"
fi
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR], m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
[AC_ARG_WITH([pkgconfigdir], [AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir],
@@ -129,6 +152,8 @@ AC_SUBST([PC_LIBS], [${PC_LIBS}])
AC_SUBST(PARSER_CFLAGS) AC_SUBST(PARSER_CFLAGS)
AC_SUBST(PARSER_LIBS) AC_SUBST(PARSER_LIBS)
AC_SUBST(RESOLV_CFLAGS)
AC_SUBST(RESOLV_LIBS) AC_SUBST(RESOLV_LIBS)
AC_SUBST(WARNING_FLAGS)
AC_CONFIG_FILES([Makefile libstrophe.pc]) AC_CONFIG_FILES([Makefile libstrophe.pc])
AC_OUTPUT AC_OUTPUT

View File

@@ -26,6 +26,8 @@ int handle_reply(xmpp_conn_t * const conn,
xmpp_stanza_t *query, *item; xmpp_stanza_t *query, *item;
const char *type; const char *type;
(void)userdata;
type = xmpp_stanza_get_type(stanza); type = xmpp_stanza_get_type(stanza);
if (strcmp(type, "error") == 0) if (strcmp(type, "error") == 0)
fprintf(stderr, "ERROR: query failed\n"); fprintf(stderr, "ERROR: query failed\n");
@@ -44,21 +46,23 @@ int handle_reply(xmpp_conn_t * const conn,
return 0; return 0;
} }
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, void conn_handler(xmpp_conn_t *const conn,
const int error, xmpp_stream_error_t * const stream_error, const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata) void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *iq, *query; xmpp_stanza_t *iq, *query;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) { if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n"); fprintf(stderr, "DEBUG: connected\n");
/* create iq stanza for request */ /* create iq stanza for request */
iq = xmpp_stanza_new(ctx); iq = xmpp_iq_new(ctx, "get", "active1");
xmpp_stanza_set_name(iq, "iq");
xmpp_stanza_set_type(iq, "get");
xmpp_stanza_set_id(iq, "active1");
xmpp_stanza_set_to(iq, "xxxxxxxxx.com"); xmpp_stanza_set_to(iq, "xxxxxxxxx.com");
query = xmpp_stanza_new(ctx); query = xmpp_stanza_new(ctx);

View File

@@ -19,22 +19,44 @@
#define KA_INTERVAL 1 #define KA_INTERVAL 1
/* define a handler for connection events */ /* define a handler for connection events */
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, void conn_handler(xmpp_conn_t *const conn,
const int error, xmpp_stream_error_t * const stream_error, const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata) void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
int secured; int secured;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) { if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n"); fprintf(stderr, "DEBUG: connected\n");
secured = xmpp_conn_is_secured(conn); secured = xmpp_conn_is_secured(conn);
fprintf(stderr, "DEBUG: connection is %s.\n", fprintf(stderr, "DEBUG: connection is %s.\n",
secured ? "secured" : "NOT secured"); secured ? "secured" : "NOT secured");
xmpp_disconnect(conn); xmpp_disconnect(conn);
} else {
switch (error) {
case XMPP_EOK:
fprintf(stderr, "DEBUG: disconnected successfully.\n");
break;
case XMPP_EINVOP:
/*
* This may happen when the requested functionality is not
* supported or some data is not provided when it is required (e.g.
* resource part).
*/
fprintf(stderr, "DEBUG: disconnected, invalid operation.\n");
break;
case XMPP_ECERT:
/*
* In this case, application can reconnect with
* XMPP_CONN_FLAG_TRUST_TLS if user permits.
*/
fprintf(stderr, "DEBUG: disconnected, invalid TLS certificate.\n");
} }
else {
fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx); xmpp_stop(ctx);
} }
} }
@@ -55,8 +77,12 @@ int main(int argc, char **argv)
flags |= XMPP_CONN_FLAG_DISABLE_TLS; flags |= XMPP_CONN_FLAG_DISABLE_TLS;
else if (strcmp(argv[i], "--mandatory-tls") == 0) else if (strcmp(argv[i], "--mandatory-tls") == 0)
flags |= XMPP_CONN_FLAG_MANDATORY_TLS; 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) else if (strcmp(argv[i], "--legacy-ssl") == 0)
flags |= XMPP_CONN_FLAG_LEGACY_SSL; flags |= XMPP_CONN_FLAG_LEGACY_SSL;
else if (strcmp(argv[i], "--legacy-auth") == 0)
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
else if (strcmp(argv[i], "--tcp-keepalive") == 0) else if (strcmp(argv[i], "--tcp-keepalive") == 0)
tcp_keepalive = 1; tcp_keepalive = 1;
else else
@@ -67,7 +93,9 @@ int main(int argc, char **argv)
"Options:\n" "Options:\n"
" --disable-tls Disable TLS.\n" " --disable-tls Disable TLS.\n"
" --mandatory-tls Deny plaintext connection.\n" " --mandatory-tls Deny plaintext connection.\n"
" --trust-tls Trust TLS certificate.\n"
" --legacy-ssl Use old style SSL.\n" " --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n"
" --tcp-keepalive Configure TCP keepalive.\n\n" " --tcp-keepalive Configure TCP keepalive.\n\n"
"Note: --disable-tls conflicts with --mandatory-tls or " "Note: --disable-tls conflicts with --mandatory-tls or "
"--legacy-ssl\n"); "--legacy-ssl\n");
@@ -87,8 +115,9 @@ int main(int argc, char **argv)
/* init library */ /* init library */
xmpp_initialize(); xmpp_initialize();
/* pass NULL instead to silence output */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */ /* create a context */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log); ctx = xmpp_ctx_new(NULL, log);
/* create a connection */ /* create a connection */
@@ -97,7 +126,8 @@ int main(int argc, char **argv)
/* configure connection properties (optional) */ /* configure connection properties (optional) */
xmpp_conn_set_flags(conn, flags); xmpp_conn_set_flags(conn, flags);
/* configure TCP keepalive (optional) */ /* configure TCP keepalive (optional) */
if (tcp_keepalive) xmpp_conn_set_keepalive(conn, KA_TIMEOUT, KA_INTERVAL); if (tcp_keepalive)
xmpp_conn_set_keepalive(conn, KA_TIMEOUT, KA_INTERVAL);
/* setup authentication information */ /* setup authentication information */
xmpp_conn_set_jid(conn, jid); xmpp_conn_set_jid(conn, jid);

View File

@@ -22,8 +22,9 @@
#include <strophe.h> #include <strophe.h>
int version_handler(xmpp_conn_t *const conn,
int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) xmpp_stanza_t *const stanza,
void *const userdata)
{ {
xmpp_stanza_t *reply, *query, *name, *version, *text; xmpp_stanza_t *reply, *query, *name, *version, *text;
const char *ns; const char *ns;
@@ -69,57 +70,77 @@ int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void
return 1; return 1;
} }
int message_handler(xmpp_conn_t *const conn,
int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) xmpp_stanza_t *const stanza,
void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *reply; xmpp_stanza_t *body, *reply;
const char *type;
char *intext, *replytext; char *intext, *replytext;
int quit = 0;
if (!xmpp_stanza_get_child_by_name(stanza, "body")) body = xmpp_stanza_get_child_by_name(stanza, "body");
if (body == NULL)
return 1; return 1;
if (xmpp_stanza_get_type(stanza) != NULL && !strcmp(xmpp_stanza_get_type(stanza), "error")) type = xmpp_stanza_get_type(stanza);
if (type != NULL && strcmp(type, "error") == 0)
return 1; return 1;
intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body")); intext = xmpp_stanza_get_text(body);
printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza), intext); printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza),
intext);
reply = xmpp_stanza_reply(stanza); reply = xmpp_stanza_reply(stanza);
if (xmpp_stanza_get_type(reply) == NULL) if (xmpp_stanza_get_type(reply) == NULL)
xmpp_stanza_set_type(reply, "chat"); xmpp_stanza_set_type(reply, "chat");
if (strcmp(intext, "quit") == 0) {
replytext = strdup("bye!");
quit = 1;
} else {
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1); replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
strcpy(replytext, intext); strcpy(replytext, intext);
strcat(replytext, " to you too!"); strcat(replytext, " to you too!");
}
xmpp_free(ctx, intext); xmpp_free(ctx, intext);
xmpp_message_set_body(reply, replytext); xmpp_message_set_body(reply, replytext);
xmpp_send(conn, reply); xmpp_send(conn, reply);
xmpp_stanza_release(reply); xmpp_stanza_release(reply);
free(replytext); free(replytext);
if (quit)
xmpp_disconnect(conn);
return 1; return 1;
} }
/* define a handler for connection events */ /* define a handler for connection events */
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, void conn_handler(xmpp_conn_t *const conn,
const int error, xmpp_stream_error_t * const stream_error, const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata) void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) { if (status == XMPP_CONN_CONNECT) {
xmpp_stanza_t *pres; xmpp_stanza_t *pres;
fprintf(stderr, "DEBUG: connected\n"); fprintf(stderr, "DEBUG: connected\n");
xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL, ctx); xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL,
ctx);
xmpp_handler_add(conn, message_handler, NULL, "message", NULL, ctx); xmpp_handler_add(conn, message_handler, NULL, "message", NULL, ctx);
/* Send initial <presence/> so that we appear online to contacts */ /* Send initial <presence/> so that we appear online to contacts */
pres = xmpp_presence_new(ctx); pres = xmpp_presence_new(ctx);
xmpp_send(conn, pres); xmpp_send(conn, pres);
xmpp_stanza_release(pres); xmpp_stanza_release(pres);
} } else {
else {
fprintf(stderr, "DEBUG: disconnected\n"); fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx); xmpp_stop(ctx);
} }
@@ -144,8 +165,9 @@ int main(int argc, char **argv)
/* init library */ /* init library */
xmpp_initialize(); xmpp_initialize();
/* pass NULL instead to silence output */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */ /* create a context */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log); ctx = xmpp_ctx_new(NULL, log);
/* create a connection */ /* create a connection */

View File

@@ -19,19 +19,22 @@
#include <strophe.h> #include <strophe.h>
/* define a handler for connection events */ /* define a handler for connection events */
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, void conn_handler(xmpp_conn_t *const conn,
const int error, xmpp_stream_error_t * const stream_error, const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata) void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) { if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n"); fprintf(stderr, "DEBUG: connected\n");
xmpp_disconnect(conn); xmpp_disconnect(conn);
} } else {
else {
fprintf(stderr, "DEBUG: disconnected\n"); fprintf(stderr, "DEBUG: disconnected\n");
xmpp_stop(ctx); xmpp_stop(ctx);
} }
@@ -67,8 +70,9 @@ int main(int argc, char **argv)
/* init library */ /* init library */
xmpp_initialize(); xmpp_initialize();
/* pass NULL instead to silence output */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
/* create a context */ /* create a context */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log); ctx = xmpp_ctx_new(NULL, log);
/* create a connection */ /* create a connection */
@@ -94,4 +98,3 @@ int main(int argc, char **argv)
return 0; return 0;
} }

370
examples/register.c Normal file
View File

@@ -0,0 +1,370 @@
/* 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 and 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 *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
void *const userdata)
{
(void)userdata;
fprintf(stderr, "DEBUG: timeout\n");
xmpp_disconnect(conn);
return 0;
}
static int _handle_features(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const 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

@@ -25,6 +25,8 @@ int handle_reply(xmpp_conn_t * const conn,
xmpp_stanza_t *query, *item; xmpp_stanza_t *query, *item;
const char *type, *name; const char *type, *name;
(void)userdata;
type = xmpp_stanza_get_type(stanza); type = xmpp_stanza_get_type(stanza);
if (strcmp(type, "error") == 0) if (strcmp(type, "error") == 0)
fprintf(stderr, "ERROR: query failed\n"); fprintf(stderr, "ERROR: query failed\n");
@@ -34,13 +36,11 @@ int handle_reply(xmpp_conn_t * const conn,
for (item = xmpp_stanza_get_children(query); item; for (item = xmpp_stanza_get_children(query); item;
item = xmpp_stanza_get_next(item)) item = xmpp_stanza_get_next(item))
if ((name = xmpp_stanza_get_attribute(item, "name"))) if ((name = xmpp_stanza_get_attribute(item, "name")))
printf("\t %s (%s) sub=%s\n", printf("\t %s (%s) sub=%s\n", name,
name,
xmpp_stanza_get_attribute(item, "jid"), xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription")); xmpp_stanza_get_attribute(item, "subscription"));
else else
printf("\t %s sub=%s\n", printf("\t %s sub=%s\n", xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "jid"),
xmpp_stanza_get_attribute(item, "subscription")); xmpp_stanza_get_attribute(item, "subscription"));
printf("END OF LIST\n"); printf("END OF LIST\n");
} }
@@ -51,21 +51,23 @@ int handle_reply(xmpp_conn_t * const conn,
return 0; return 0;
} }
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, void conn_handler(xmpp_conn_t *const conn,
const int error, xmpp_stream_error_t * const stream_error, const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const userdata) void *const userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *iq, *query; xmpp_stanza_t *iq, *query;
(void)error;
(void)stream_error;
if (status == XMPP_CONN_CONNECT) { if (status == XMPP_CONN_CONNECT) {
fprintf(stderr, "DEBUG: connected\n"); fprintf(stderr, "DEBUG: connected\n");
/* create iq stanza for request */ /* create iq stanza for request */
iq = xmpp_stanza_new(ctx); iq = xmpp_iq_new(ctx, "get", "roster1");
xmpp_stanza_set_name(iq, "iq");
xmpp_stanza_set_type(iq, "get");
xmpp_stanza_set_id(iq, "roster1");
query = xmpp_stanza_new(ctx); query = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(query, "query"); xmpp_stanza_set_name(query, "query");

View File

@@ -1,7 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <strophe.h> #include <strophe.h>
int main(int argc, char **argv) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
char *uuid; char *uuid;

283
examples/vcard.c Normal file
View File

@@ -0,0 +1,283 @@
/* 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 and 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 *const conn, void *const userdata)
{
(void)userdata;
fprintf(stderr, "Timeout reached.\n");
xmpp_disconnect(conn);
return 0;
}
static int recv_vcard(xmpp_conn_t *const conn,
xmpp_stanza_t *const stanza,
void *const 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 *const conn,
const xmpp_conn_event_t status,
const int error,
xmpp_stream_error_t *const stream_error,
void *const 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;
}

View File

@@ -23,7 +23,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := libstrophe LOCAL_MODULE := libstrophe
LOCAL_CFLAGS := LOCAL_CFLAGS := -DHAVE_DECL_VA_COPY
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \ $(LOCAL_PATH)/.. \
$(LOCAL_PATH)/../src \ $(LOCAL_PATH)/../src \
@@ -45,6 +45,8 @@ LOCAL_SRC_FILES := \
../src/sasl.c \ ../src/sasl.c \
../src/scram.c \ ../src/scram.c \
../src/sha1.c \ ../src/sha1.c \
../src/sha256.c \
../src/sha512.c \
../src/snprintf.c \ ../src/snprintf.c \
../src/sock.c \ ../src/sock.c \
../src/stanza.c \ ../src/stanza.c \
@@ -62,7 +64,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := libexpat LOCAL_MODULE := libexpat
LOCAL_CFLAGS := -DHAVE_MEMMOVE LOCAL_CFLAGS := -DHAVE_MEMMOVE -DXML_DEV_URANDOM
#LOCAL_C_INCLUDES := \ #LOCAL_C_INCLUDES := \
# $(LOCAL_PATH)/expat # $(LOCAL_PATH)/expat

View File

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

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,57 +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: automake
BuildRequires: libtool
BuildRequires: openssl-devel
BuildRequires: expat-devel
Requires: expat
%description
XMPP library in C
%package devel
Summary: Headers and libraries for building apps that use libstrophe
Group: Development/Libraries
Requires: %{name} = %{version}-%{release}
%description devel
This package contains headers and libraries required to build applications that
use the strophe XMPP library.
%prep
%setup -n libstrophe
./bootstrap.sh
%build
%configure
make %{?_smp_mflags}
%install
make install DESTDIR=%{buildroot}
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%files
%defattr(-,root,root,-)
%{_libdir}/libstrophe.so*
%doc
%files devel
%defattr(-,root,root,-)
%{_libdir}/libstrophe.a
%{_libdir}/libstrophe.la
%{_libdir}/pkgconfig/libstrophe.pc
%{_includedir}/strophe.h
%doc
%changelog

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include "strophe.h" #include "strophe.h"
#include "ostypes.h" #include "ostypes.h"
#include "sock.h" #include "sock.h"
@@ -30,6 +29,38 @@
#include "rand.h" #include "rand.h"
#include "snprintf.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 **/ /** run-time context **/
typedef enum { typedef enum {
@@ -50,13 +81,14 @@ struct _xmpp_ctx_t {
xmpp_rand_t *rand; xmpp_rand_t *rand;
xmpp_loop_status_t loop_status; xmpp_loop_status_t loop_status;
xmpp_connlist_t *connlist; xmpp_connlist_t *connlist;
}; xmpp_handlist_t *timed_handlers;
unsigned long timeout;
};
/* convenience functions for accessing the context */ /* convenience functions for accessing the context */
void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size); void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size);
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p, void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size);
const size_t size);
char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s); char *xmpp_strdup(const xmpp_ctx_t *const ctx, const char *const s);
void xmpp_log(const xmpp_ctx_t *const ctx, void xmpp_log(const xmpp_ctx_t *const ctx,
@@ -101,35 +133,7 @@ struct _xmpp_send_queue_t {
xmpp_send_queue_t *next; xmpp_send_queue_t *next;
}; };
typedef struct _xmpp_handlist_t xmpp_handlist_t; #define UNUSED(x) ((void)(x))
struct _xmpp_handlist_t {
/* common members */
int user_handler;
void *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;
};
};
};
#define MAX_DOMAIN_LEN 256 #define MAX_DOMAIN_LEN 256
@@ -137,6 +141,11 @@ struct _xmpp_handlist_t {
#define SASL_MASK_DIGESTMD5 (1 << 1) #define SASL_MASK_DIGESTMD5 (1 << 1)
#define SASL_MASK_ANONYMOUS (1 << 2) #define SASL_MASK_ANONYMOUS (1 << 2)
#define SASL_MASK_SCRAMSHA1 (1 << 3) #define SASL_MASK_SCRAMSHA1 (1 << 3)
#define SASL_MASK_SCRAMSHA256 (1 << 4)
#define SASL_MASK_SCRAMSHA512 (1 << 5)
#define SASL_MASK_SCRAM \
(SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512)
enum { enum {
XMPP_PORT_CLIENT = 5222, XMPP_PORT_CLIENT = 5222,
@@ -166,9 +175,11 @@ struct _xmpp_conn_t {
int tls_disabled; int tls_disabled;
int tls_mandatory; int tls_mandatory;
int tls_legacy_ssl; int tls_legacy_ssl;
int tls_trust;
int tls_failed; /* set when tls fails, so we don't try again */ int tls_failed; /* set when tls fails, so we don't try again */
int sasl_support; /* if true, field is a bitfield of supported int sasl_support; /* if true, field is a bitfield of supported
mechanisms */ mechanisms */
int auth_legacy_enabled;
int secured; /* set when stream is secured with TLS */ int secured; /* set when stream is secured with TLS */
/* if server returns <bind/> or <session/> we must do them */ /* if server returns <bind/> or <session/> we must do them */
@@ -222,7 +233,6 @@ int conn_tls_start(xmpp_conn_t * const conn);
void conn_prepare_reset(xmpp_conn_t *const conn, xmpp_open_handler handler); void conn_prepare_reset(xmpp_conn_t *const conn, xmpp_open_handler handler);
void conn_parser_reset(xmpp_conn_t *const conn); void conn_parser_reset(xmpp_conn_t *const conn);
typedef enum { typedef enum {
XMPP_STANZA_UNKNOWN, XMPP_STANZA_UNKNOWN,
XMPP_STANZA_TEXT, XMPP_STANZA_TEXT,
@@ -246,8 +256,7 @@ struct _xmpp_stanza_t {
}; };
/* handler management */ /* handler management */
void handler_fire_stanza(xmpp_conn_t * const conn, void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
xmpp_stanza_t * const stanza);
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx); uint64_t handler_fire_timed(xmpp_ctx_t *const ctx);
void handler_reset_timed(xmpp_conn_t *conn, int user_only); void handler_reset_timed(xmpp_conn_t *conn, int user_only);
void handler_add_timed(xmpp_conn_t *const conn, void handler_add_timed(xmpp_conn_t *const conn,
@@ -264,8 +273,10 @@ void handler_add(xmpp_conn_t * const conn,
const char *const name, const char *const name,
const char *const type, const char *const type,
void *const userdata); void *const userdata);
void handler_system_delete_all(xmpp_conn_t *conn);
/* utility functions */ /* utility functions */
void disconnect_with_error(xmpp_conn_t *const conn, int error);
void disconnect_mem_error(xmpp_conn_t *const conn); void disconnect_mem_error(xmpp_conn_t *const conn);
/* auth functions */ /* auth functions */

View File

@@ -48,20 +48,21 @@
#define CONNECT_TIMEOUT 5000 /* 5 seconds */ #define CONNECT_TIMEOUT 5000 /* 5 seconds */
#endif #endif
static int _disconnect_cleanup(xmpp_conn_t * const conn, static int _disconnect_cleanup(xmpp_conn_t *const conn, void *const userdata);
void * const userdata);
static char *_conn_build_stream_tag(xmpp_conn_t *const conn, static char *_conn_build_stream_tag(xmpp_conn_t *const conn,
char **attributes, size_t attributes_len); char **attributes,
static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs,
char ***attributes, size_t *attributes_len);
static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes,
size_t attributes_len); size_t attributes_len);
static void _handle_stream_start(char *name, char **attrs, static void _conn_attributes_new(xmpp_conn_t *conn,
void * const userdata); char **attrs,
static void _handle_stream_end(char *name, char ***attributes,
void * const userdata); size_t *attributes_len);
static void _handle_stream_stanza(xmpp_stanza_t *stanza, static void _conn_attributes_destroy(xmpp_conn_t *conn,
void * const userdata); char **attributes,
size_t attributes_len);
static void
_handle_stream_start(char *name, char **attrs, void *const userdata);
static void _handle_stream_end(char *name, void *const userdata);
static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *const userdata);
static unsigned short _conn_default_port(xmpp_conn_t *const conn, static unsigned short _conn_default_port(xmpp_conn_t *const conn,
xmpp_conn_type_t type); xmpp_conn_type_t type);
static void _conn_reset(xmpp_conn_t *const conn); static void _conn_reset(xmpp_conn_t *const conn);
@@ -73,6 +74,17 @@ static int _conn_connect(xmpp_conn_t * const conn,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *const userdata); void *const userdata);
void xmpp_send_error(xmpp_conn_t *const conn,
xmpp_error_type_t const type,
char *const text)
{
xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
xmpp_send(conn, error);
xmpp_stanza_release(error);
}
/** Create a new Strophe connection object. /** Create a new Strophe connection object.
* *
* @param ctx a Strophe context object * @param ctx a Strophe context object
@@ -86,7 +98,8 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
xmpp_conn_t *conn = NULL; xmpp_conn_t *conn = NULL;
xmpp_connlist_t *tail, *item; xmpp_connlist_t *tail, *item;
if (ctx == NULL) return NULL; if (ctx == NULL)
return NULL;
conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t)); conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
if (conn != NULL) { if (conn != NULL) {
@@ -128,18 +141,18 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
conn->tls_disabled = 0; conn->tls_disabled = 0;
conn->tls_mandatory = 0; conn->tls_mandatory = 0;
conn->tls_legacy_ssl = 0; conn->tls_legacy_ssl = 0;
conn->tls_trust = 0;
conn->tls_failed = 0; conn->tls_failed = 0;
conn->sasl_support = 0; conn->sasl_support = 0;
conn->auth_legacy_enabled = 0;
conn->secured = 0; conn->secured = 0;
conn->bind_required = 0; conn->bind_required = 0;
conn->session_required = 0; conn->session_required = 0;
conn->parser = parser_new(conn->ctx, conn->parser =
_handle_stream_start, parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
_handle_stream_end, _handle_stream_stanza, conn);
_handle_stream_stanza,
conn);
conn->reset_parser = 0; conn->reset_parser = 0;
conn->authenticated = 0; conn->authenticated = 0;
@@ -155,7 +168,8 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
/* add connection to ctx->connlist */ /* add connection to ctx->connlist */
tail = conn->ctx->connlist; tail = conn->ctx->connlist;
while (tail && tail->next) tail = tail->next; while (tail && tail->next)
tail = tail->next;
item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t)); item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t));
if (!item) { if (!item) {
@@ -168,8 +182,10 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
item->conn = conn; item->conn = conn;
item->next = NULL; item->next = NULL;
if (tail) tail->next = item; if (tail)
else conn->ctx->connlist = item; tail->next = item;
else
conn->ctx->connlist = item;
} }
} }
@@ -243,6 +259,11 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
else { else {
ctx = conn->ctx; ctx = conn->ctx;
if (conn->state == XMPP_STATE_CONNECTING ||
conn->state == XMPP_STATE_CONNECTED) {
conn_disconnect(conn);
}
/* remove connection from context's connlist */ /* remove connection from context's connlist */
if (ctx->connlist->conn == conn) { if (ctx->connlist->conn == conn) {
item = ctx->connlist; item = ctx->connlist;
@@ -264,6 +285,8 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
} }
} }
_conn_reset(conn);
/* free handler stuff /* free handler stuff
* note that userdata is the responsibility of the client * note that userdata is the responsibility of the client
* and the handler pointers don't need to be freed since they * and the handler pointers don't need to be freed since they
@@ -286,7 +309,7 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
while (hlitem) { while (hlitem) {
thli = hlitem; thli = hlitem;
hlitem = hlitem->next; hlitem = hlitem->next;
xmpp_free(conn->ctx, thli->id); xmpp_free(conn->ctx, thli->u.id);
xmpp_free(conn->ctx, thli); xmpp_free(conn->ctx, thli);
} }
} }
@@ -298,18 +321,23 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
thli = hlitem; thli = hlitem;
hlitem = hlitem->next; hlitem = hlitem->next;
if (thli->ns) xmpp_free(ctx, thli->ns); if (thli->u.ns)
if (thli->name) xmpp_free(ctx, thli->name); xmpp_free(ctx, thli->u.ns);
if (thli->type) xmpp_free(ctx, thli->type); if (thli->u.name)
xmpp_free(ctx, thli->u.name);
if (thli->u.type)
xmpp_free(ctx, thli->u.type);
xmpp_free(ctx, thli); xmpp_free(ctx, thli);
} }
parser_free(conn->parser); parser_free(conn->parser);
_conn_reset(conn);
if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->jid)
if (conn->pass) xmpp_free(ctx, conn->pass); xmpp_free(ctx, conn->jid);
if (conn->lang) xmpp_free(ctx, conn->lang); if (conn->pass)
xmpp_free(ctx, conn->pass);
if (conn->lang)
xmpp_free(ctx, conn->lang);
xmpp_free(ctx, conn); xmpp_free(ctx, conn);
released = 1; released = 1;
} }
@@ -361,7 +389,8 @@ 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) void xmpp_conn_set_jid(xmpp_conn_t *const conn, const char *const jid)
{ {
if (conn->jid) xmpp_free(conn->ctx, conn->jid); if (conn->jid)
xmpp_free(conn->ctx, conn->jid);
conn->jid = xmpp_strdup(conn->ctx, jid); conn->jid = xmpp_strdup(conn->ctx, jid);
} }
@@ -389,7 +418,8 @@ 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) void xmpp_conn_set_pass(xmpp_conn_t *const conn, const char *const pass)
{ {
if (conn->pass) xmpp_free(conn->ctx, conn->pass); if (conn->pass)
xmpp_free(conn->ctx, conn->pass);
conn->pass = xmpp_strdup(conn->ctx, pass); conn->pass = xmpp_strdup(conn->ctx, pass);
} }
@@ -441,7 +471,8 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
int rc; int rc;
domain = xmpp_jid_domain(conn->ctx, conn->jid); domain = xmpp_jid_domain(conn->ctx, conn->jid);
if (!domain) return XMPP_EMEM; if (!domain)
return XMPP_EMEM;
if (altdomain != NULL) { if (altdomain != NULL) {
xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain."); xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
@@ -457,7 +488,8 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
} }
if (XMPP_DOMAIN_NOT_FOUND == found) { if (XMPP_DOMAIN_NOT_FOUND == found) {
xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, " xmpp_debug(conn->ctx, "xmpp",
"SRV lookup failed, "
"connecting via domain."); "connecting via domain.");
host = domain; host = domain;
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT); port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
@@ -471,8 +503,8 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
port = rr->port; port = rr->port;
rr = rr->next; rr = rr->next;
} }
rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, callback,
callback, userdata); userdata);
} while (rc != 0 && rr != NULL); } while (rc != 0 && rr != NULL);
xmpp_free(conn->ctx, domain); xmpp_free(conn->ctx, domain);
@@ -504,17 +536,21 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
* *
* @ingroup Connections * @ingroup Connections
*/ */
int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, int xmpp_connect_component(xmpp_conn_t *const conn,
unsigned short port, xmpp_conn_handler callback, const char *const server,
unsigned short port,
xmpp_conn_handler callback,
void *const userdata) void *const userdata)
{ {
/* The server domain, jid and password MUST be specified. */ /* The server domain, jid and password MUST be specified. */
if (!(server && conn->jid && conn->pass)) return XMPP_EINVOP; if (!(server && conn->jid && conn->pass))
return XMPP_EINVOP;
/* XEP-0114 does not support TLS */ /* XEP-0114 does not support TLS */
xmpp_conn_disable_tls(conn); xmpp_conn_disable_tls(conn);
if (!conn->tls_disabled) { if (!conn->tls_disabled) {
xmpp_error(conn->ctx, "conn", "Failed to disable TLS. " xmpp_error(conn->ctx, "conn",
"Failed to disable TLS. "
"XEP-0114 does not support TLS"); "XEP-0114 does not support TLS");
return XMPP_EINT; return XMPP_EINT;
} }
@@ -573,7 +609,8 @@ void conn_established(xmpp_conn_t * const conn)
/* we skip authentication for a "raw" connection, but the event loop /* we skip authentication for a "raw" connection, but the event loop
ignores user's handlers when conn->authenticated is not set. */ ignores user's handlers when conn->authenticated is not set. */
conn->authenticated = 1; conn->authenticated = 1;
conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL, conn->userdata); conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
conn->userdata);
} else { } else {
/* send stream init */ /* send stream init */
conn_open_stream(conn); conn_open_stream(conn);
@@ -618,7 +655,8 @@ int xmpp_conn_open_stream_default(xmpp_conn_t * const conn)
* *
* @ingroup Connections * @ingroup Connections
*/ */
int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes, int xmpp_conn_open_stream(xmpp_conn_t *const conn,
char **attributes,
size_t attributes_len) size_t attributes_len)
{ {
char *tag; char *tag;
@@ -702,7 +740,7 @@ void conn_parser_reset(xmpp_conn_t * const conn)
/** Initiate termination of the connection to the XMPP server. /** Initiate termination of the connection to the XMPP server.
* This function starts the disconnection sequence by sending * This function starts the disconnection sequence by sending
* </stream:stream> to the XMPP server. This function will do nothing * </stream:stream> to the XMPP server. This function will do nothing
* if the connection state is CONNECTING or CONNECTED. * if the connection state is different from CONNECTING or CONNECTED.
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
* *
@@ -718,8 +756,7 @@ void xmpp_disconnect(xmpp_conn_t * const conn)
xmpp_send_raw_string(conn, "</stream:stream>"); xmpp_send_raw_string(conn, "</stream:stream>");
/* setup timed handler in case disconnect takes too long */ /* setup timed handler in case disconnect takes too long */
handler_add_timed(conn, _disconnect_cleanup, handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
DISCONNECT_TIMEOUT, NULL);
} }
/** Send a raw string to the XMPP server. /** Send a raw string to the XMPP server.
@@ -735,8 +772,7 @@ void xmpp_disconnect(xmpp_conn_t * const conn)
* *
* @ingroup Connections * @ingroup Connections
*/ */
void xmpp_send_raw_string(xmpp_conn_t * const conn, void xmpp_send_raw_string(xmpp_conn_t *const conn, const char *const fmt, ...)
const char * const fmt, ...)
{ {
va_list ap; va_list ap;
size_t len; size_t len;
@@ -744,16 +780,17 @@ void xmpp_send_raw_string(xmpp_conn_t * const conn,
char *bigbuf; char *bigbuf;
va_start(ap, fmt); va_start(ap, fmt);
len = xmpp_vsnprintf(buf, 1024, fmt, ap); len = xmpp_vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap); va_end(ap);
if (len >= 1024) { if (len >= sizeof(buf)) {
/* we need more space for this data, so we allocate a big /* we need more space for this data, so we allocate a big
* enough buffer and print to that */ * enough buffer and print to that */
len++; /* account for trailing \0 */ len++; /* account for trailing \0 */
bigbuf = xmpp_alloc(conn->ctx, len); bigbuf = xmpp_alloc(conn->ctx, len);
if (!bigbuf) { if (!bigbuf) {
xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string"); xmpp_debug(conn->ctx, "xmpp",
"Could not allocate memory for send_raw_string");
return; return;
} }
va_start(ap, fmt); va_start(ap, fmt);
@@ -768,7 +805,6 @@ void xmpp_send_raw_string(xmpp_conn_t * const conn,
xmpp_free(conn->ctx, bigbuf); xmpp_free(conn->ctx, bigbuf);
} else { } else {
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf); xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
xmpp_send_raw(conn, buf, len); xmpp_send_raw(conn, buf, len);
} }
} }
@@ -786,15 +822,18 @@ void xmpp_send_raw_string(xmpp_conn_t * const conn,
* @ingroup Connections * @ingroup Connections
*/ */
void xmpp_send_raw(xmpp_conn_t *const conn, void xmpp_send_raw(xmpp_conn_t *const conn,
const char * const data, const size_t len) const char *const data,
const size_t len)
{ {
xmpp_send_queue_t *item; xmpp_send_queue_t *item;
if (conn->state != XMPP_STATE_CONNECTED) return; if (conn->state != XMPP_STATE_CONNECTED)
return;
/* create send queue item for queue */ /* create send queue item for queue */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t)); item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
if (!item) return; if (!item)
return;
item->data = xmpp_alloc(conn->ctx, len); item->data = xmpp_alloc(conn->ctx, len);
if (!item->data) { if (!item->data) {
@@ -828,15 +867,13 @@ void xmpp_send_raw(xmpp_conn_t * const conn,
* *
* @ingroup Connections * @ingroup Connections
*/ */
void xmpp_send(xmpp_conn_t * const conn, void xmpp_send(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
xmpp_stanza_t * const stanza)
{ {
char *buf; char *buf;
size_t len; size_t len;
int ret;
if (conn->state == XMPP_STATE_CONNECTED) { if (conn->state == XMPP_STATE_CONNECTED) {
if ((ret = xmpp_stanza_to_text(stanza, &buf, &len)) == 0) { if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
xmpp_send_raw(conn, buf, len); xmpp_send_raw(conn, buf, len);
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf); xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
xmpp_free(conn->ctx, buf); xmpp_free(conn->ctx, buf);
@@ -853,16 +890,15 @@ void xmpp_send(xmpp_conn_t * const conn,
void conn_open_stream(xmpp_conn_t *const conn) void conn_open_stream(xmpp_conn_t *const conn)
{ {
xmpp_send_raw_string(conn, xmpp_send_raw_string(conn,
"<?xml version=\"1.0\"?>" \ "<?xml version=\"1.0\"?>"
"<stream:stream to=\"%s\" " \ "<stream:stream to=\"%s\" "
"xml:lang=\"%s\" " \ "xml:lang=\"%s\" "
"version=\"1.0\" " \ "version=\"1.0\" "
"xmlns=\"%s\" " \ "xmlns=\"%s\" "
"xmlns:stream=\"%s\">", "xmlns:stream=\"%s\">",
conn->domain, conn->domain, conn->lang,
conn->lang, conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT
conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : : XMPP_NS_COMPONENT,
XMPP_NS_COMPONENT,
XMPP_NS_STREAMS); XMPP_NS_STREAMS);
} }
@@ -874,7 +910,7 @@ int conn_tls_start(xmpp_conn_t * const conn)
conn->tls = NULL; conn->tls = NULL;
rc = XMPP_EINVOP; rc = XMPP_EINVOP;
} else { } else {
conn->tls = tls_new(conn->ctx, conn->sock); conn->tls = tls_new(conn);
rc = conn->tls == NULL ? XMPP_EMEM : 0; rc = conn->tls == NULL ? XMPP_EMEM : 0;
} }
@@ -882,16 +918,18 @@ int conn_tls_start(xmpp_conn_t * const conn)
if (tls_start(conn->tls)) { if (tls_start(conn->tls)) {
conn->secured = 1; conn->secured = 1;
} else { } else {
rc = XMPP_EINT; /* TODO: distinguish invalid certificates and other errors */
conn->error = tls_error(conn->tls); rc = XMPP_ECERT;
tls_free(conn->tls); tls_free(conn->tls);
conn->tls = NULL; conn->tls = NULL;
conn->tls_failed = 1; conn->tls_failed = 1;
} }
} }
if (rc != 0) { if (rc != 0) {
xmpp_debug(conn->ctx, "conn", "Couldn't start TLS! " xmpp_debug(conn->ctx, "conn",
"error %d tls_error %d", rc, conn->error); "Couldn't start TLS! error %d tls_error %d", rc,
conn->tls == NULL ? 0 : tls_error(conn->tls));
conn->error = rc;
} }
return rc; return rc;
} }
@@ -910,7 +948,10 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn)
flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled | flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory | XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl; XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
;
return flags; return flags;
} }
@@ -927,6 +968,8 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn)
* - XMPP_CONN_FLAG_DISABLE_TLS * - XMPP_CONN_FLAG_DISABLE_TLS
* - XMPP_CONN_FLAG_MANDATORY_TLS * - XMPP_CONN_FLAG_MANDATORY_TLS
* - XMPP_CONN_FLAG_LEGACY_SSL * - XMPP_CONN_FLAG_LEGACY_SSL
* - XMPP_CONN_FLAG_TRUST_TLS
* - XMPP_CONN_FLAG_LEGACY_AUTH
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
* @param flags ORed connection flags * @param flags ORed connection flags
@@ -938,12 +981,14 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn)
int xmpp_conn_set_flags(xmpp_conn_t *const conn, long flags) int xmpp_conn_set_flags(xmpp_conn_t *const conn, long flags)
{ {
if (conn->state != XMPP_STATE_DISCONNECTED) { if (conn->state != XMPP_STATE_DISCONNECTED) {
xmpp_error(conn->ctx, "conn", "Flags can be set only " xmpp_error(conn->ctx, "conn",
"Flags can be set only "
"for disconnected connection"); "for disconnected connection");
return XMPP_EINVOP; return XMPP_EINVOP;
} }
if (flags & XMPP_CONN_FLAG_DISABLE_TLS && if (flags & XMPP_CONN_FLAG_DISABLE_TLS &&
flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL)) { flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
XMPP_CONN_FLAG_TRUST_TLS)) {
xmpp_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags); xmpp_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
return XMPP_EINVOP; return XMPP_EINVOP;
} }
@@ -951,6 +996,8 @@ int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags)
conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0; conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0; conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0; conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
return 0; return 0;
} }
@@ -982,23 +1029,55 @@ void xmpp_conn_disable_tls(xmpp_conn_t * const conn)
*/ */
int xmpp_conn_is_secured(xmpp_conn_t *const conn) int xmpp_conn_is_secured(xmpp_conn_t *const conn)
{ {
return conn->secured && !conn->tls_failed && conn->tls != NULL ? 1 : 0; return conn->secured && !conn->tls_failed && conn->tls != NULL;
}
/**
* @return TRUE if connection is in connecting state and FALSE otherwise
*
* @ingroup Connections
*/
int xmpp_conn_is_connecting(xmpp_conn_t *const conn)
{
return conn->state == XMPP_STATE_CONNECTING;
}
/**
* @return TRUE if connection is in connected state and FALSE otherwise
*
* @ingroup Connections
*/
int xmpp_conn_is_connected(xmpp_conn_t *const conn)
{
return conn->state == XMPP_STATE_CONNECTED;
}
/**
* @return TRUE if connection is in disconnected state and FALSE otherwise
*
* @ingroup Connections
*/
int xmpp_conn_is_disconnected(xmpp_conn_t *const conn)
{
return conn->state == XMPP_STATE_DISCONNECTED;
} }
/* timed handler for cleanup if normal disconnect procedure takes too long */ /* timed handler for cleanup if normal disconnect procedure takes too long */
static int _disconnect_cleanup(xmpp_conn_t * const conn, static int _disconnect_cleanup(xmpp_conn_t *const conn, void *const userdata)
void * const userdata)
{ {
xmpp_debug(conn->ctx, "xmpp", UNUSED(userdata);
"disconnection forced by cleanup timeout");
xmpp_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
conn->error = conn->error ?: XMPP_ETIMEDOUT;
conn_disconnect(conn); conn_disconnect(conn);
return 0; return 0;
} }
static char *_conn_build_stream_tag(xmpp_conn_t *const conn, static char *_conn_build_stream_tag(xmpp_conn_t *const conn,
char **attributes, size_t attributes_len) char **attributes,
size_t attributes_len)
{ {
char *tag; char *tag;
size_t len; size_t len;
@@ -1014,7 +1093,8 @@ static char *_conn_build_stream_tag(xmpp_conn_t * const conn,
for (i = 0; i < attributes_len; ++i) for (i = 0; i < attributes_len; ++i)
len += strlen(attributes[i]) + 2; len += strlen(attributes[i]) + 2;
tag = xmpp_alloc(conn->ctx, len + 1); tag = xmpp_alloc(conn->ctx, len + 1);
if (!tag) return NULL; if (!tag)
return NULL;
strcpy(tag, tag_head); strcpy(tag, tag_head);
for (i = 0; i < attributes_len; ++i) { for (i = 0; i < attributes_len; ++i) {
@@ -1030,7 +1110,8 @@ static char *_conn_build_stream_tag(xmpp_conn_t * const conn,
strcat(tag, tag_tail); strcat(tag, tag_tail);
if (strlen(tag) != len) { if (strlen(tag) != len) {
xmpp_error(conn->ctx, "xmpp", "Internal error in " xmpp_error(conn->ctx, "xmpp",
"Internal error in "
"_conn_build_stream_tag()."); "_conn_build_stream_tag().");
xmpp_free(conn->ctx, tag); xmpp_free(conn->ctx, tag);
tag = NULL; tag = NULL;
@@ -1039,20 +1120,24 @@ static char *_conn_build_stream_tag(xmpp_conn_t * const conn,
return tag; return tag;
} }
static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs, static void _conn_attributes_new(xmpp_conn_t *conn,
char ***attributes, size_t *attributes_len) char **attrs,
char ***attributes,
size_t *attributes_len)
{ {
char **array = NULL; char **array = NULL;
size_t nr = 0; size_t nr = 0;
size_t i; size_t i;
if (attrs) { if (attrs) {
for (; attrs[nr]; ++nr); for (; attrs[nr]; ++nr)
;
array = xmpp_alloc(conn->ctx, sizeof(*array) * nr); array = xmpp_alloc(conn->ctx, sizeof(*array) * nr);
for (i = 0; array && i < nr; ++i) { for (i = 0; array && i < nr; ++i) {
array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i]) array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
: xmpp_strdup(conn->ctx, attrs[i]); : xmpp_strdup(conn->ctx, attrs[i]);
if (array[i] == NULL) break; if (array[i] == NULL)
break;
} }
if (!array || i < nr) { if (!array || i < nr) {
xmpp_error(conn->ctx, "xmpp", "Memory allocation error."); xmpp_error(conn->ctx, "xmpp", "Memory allocation error.");
@@ -1065,7 +1150,8 @@ static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs,
*attributes_len = nr; *attributes_len = nr;
} }
static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes, static void _conn_attributes_destroy(xmpp_conn_t *conn,
char **attributes,
size_t attributes_len) size_t attributes_len)
{ {
size_t i; size_t i;
@@ -1096,7 +1182,8 @@ static char *_get_stream_attribute(char **attrs, char *name)
{ {
int i; int i;
if (!attrs) return NULL; if (!attrs)
return NULL;
for (i = 0; attrs[i]; i += 2) for (i = 0; attrs[i]; i += 2)
if (strcmp(name, attrs[i]) == 0) if (strcmp(name, attrs[i]) == 0)
@@ -1105,14 +1192,14 @@ static char *_get_stream_attribute(char **attrs, char *name)
return NULL; return NULL;
} }
static void _handle_stream_start(char *name, char **attrs, static void _handle_stream_start(char *name, char **attrs, void *const userdata)
void * const userdata)
{ {
xmpp_conn_t *conn = (xmpp_conn_t *)userdata; xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
char *id; char *id;
int failed = 0; int ret = 0;
if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id); if (conn->stream_id)
xmpp_free(conn->ctx, conn->stream_id);
conn->stream_id = NULL; conn->stream_id = NULL;
if (strcmp(name, "stream") == 0) { if (strcmp(name, "stream") == 0) {
@@ -1123,34 +1210,37 @@ static void _handle_stream_start(char *name, char **attrs,
if (id && !conn->stream_id) { if (id && !conn->stream_id) {
xmpp_error(conn->ctx, "conn", "Memory allocation failed."); xmpp_error(conn->ctx, "conn", "Memory allocation failed.");
failed = 1; ret = XMPP_EMEM;
} }
} else { } else {
xmpp_error(conn->ctx, "conn", "Server did not open valid stream." xmpp_error(conn->ctx, "conn",
" name = %s.", name); "Server did not open valid stream."
failed = 1; " name = %s.",
name);
ret = XMPP_EINT;
} }
if (!failed) { if (ret == 0) {
/* call stream open handler */ /* call stream open handler */
conn->open_handler(conn); conn->open_handler(conn);
} else { } else {
conn->error = ret;
conn_disconnect(conn); conn_disconnect(conn);
} }
} }
static void _handle_stream_end(char *name, static void _handle_stream_end(char *name, void *const userdata)
void * const userdata)
{ {
xmpp_conn_t *conn = (xmpp_conn_t *)userdata; xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
UNUSED(name);
/* stream is over */ /* stream is over */
xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>"); xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
conn_disconnect_clean(conn); conn_disconnect_clean(conn);
} }
static void _handle_stream_stanza(xmpp_stanza_t *stanza, static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *const userdata)
void * const userdata)
{ {
xmpp_conn_t *conn = (xmpp_conn_t *)userdata; xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
char *buf; char *buf;
@@ -1169,8 +1259,8 @@ static unsigned short _conn_default_port(xmpp_conn_t * const conn,
{ {
switch (type) { switch (type) {
case XMPP_CLIENT: case XMPP_CLIENT:
return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL : return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
XMPP_PORT_CLIENT; : XMPP_PORT_CLIENT;
case XMPP_COMPONENT: case XMPP_COMPONENT:
return XMPP_PORT_COMPONENT; return XMPP_PORT_COMPONENT;
default: default:
@@ -1196,6 +1286,9 @@ static void _conn_reset(xmpp_conn_t * const conn)
xmpp_free(ctx, tsq->data); xmpp_free(ctx, tsq->data);
xmpp_free(ctx, tsq); xmpp_free(ctx, tsq);
} }
conn->send_queue_head = NULL;
conn->send_queue_tail = NULL;
conn->send_queue_len = 0;
if (conn->stream_error) { if (conn->stream_error) {
xmpp_stanza_release(conn->stream_error->stanza); xmpp_stanza_release(conn->stream_error->stanza);
@@ -1205,9 +1298,12 @@ static void _conn_reset(xmpp_conn_t * const conn)
conn->stream_error = NULL; conn->stream_error = NULL;
} }
if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->domain)
if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); xmpp_free(ctx, conn->domain);
if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->bound_jid)
xmpp_free(ctx, conn->bound_jid);
if (conn->stream_id)
xmpp_free(ctx, conn->stream_id);
conn->domain = NULL; conn->domain = NULL;
conn->bound_jid = NULL; conn->bound_jid = NULL;
conn->stream_id = NULL; conn->stream_id = NULL;
@@ -1215,6 +1311,12 @@ static void _conn_reset(xmpp_conn_t * const conn)
conn->secured = 0; conn->secured = 0;
conn->tls_failed = 0; conn->tls_failed = 0;
conn->error = 0; conn->error = 0;
conn->tls_support = 0;
conn->bind_required = 0;
conn->session_required = 0;
handler_system_delete_all(conn);
} }
static int _conn_connect(xmpp_conn_t *const conn, static int _conn_connect(xmpp_conn_t *const conn,
@@ -1227,20 +1329,25 @@ static int _conn_connect(xmpp_conn_t * const conn,
{ {
xmpp_open_handler open_handler; xmpp_open_handler open_handler;
if (conn->state != XMPP_STATE_DISCONNECTED) return XMPP_EINVOP; if (conn->state != XMPP_STATE_DISCONNECTED)
if (type != XMPP_CLIENT && type != XMPP_COMPONENT) return XMPP_EINVOP; return XMPP_EINVOP;
if (host == NULL || port == 0) return XMPP_EINT; if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
return XMPP_EINVOP;
if (host == NULL || port == 0)
return XMPP_EINT;
_conn_reset(conn); _conn_reset(conn);
conn->type = type; conn->type = type;
conn->domain = xmpp_strdup(conn->ctx, domain); conn->domain = xmpp_strdup(conn->ctx, domain);
if (!conn->domain) return XMPP_EMEM; if (!conn->domain)
return XMPP_EMEM;
conn->sock = sock_connect(host, port); conn->sock = sock_connect(host, port);
xmpp_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d", xmpp_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d", host,
host, port, conn->sock); port, conn->sock);
if (conn->sock == -1) return XMPP_EINT; if (conn->sock == -1)
return XMPP_EINT;
if (conn->ka_timeout || conn->ka_interval) if (conn->ka_timeout || conn->ka_interval)
sock_set_keepalive(conn->sock, conn->ka_timeout, conn->ka_interval); sock_set_keepalive(conn->sock, conn->ka_timeout, conn->ka_interval);
@@ -1248,9 +1355,10 @@ static int _conn_connect(xmpp_conn_t * const conn,
conn->conn_handler = callback; conn->conn_handler = callback;
conn->userdata = userdata; conn->userdata = userdata;
open_handler = conn->is_raw ? auth_handle_open_stub : open_handler = conn->is_raw
type == XMPP_CLIENT ? auth_handle_open : ? auth_handle_open_stub
auth_handle_component_open; : type == XMPP_CLIENT ? auth_handle_open
: auth_handle_component_open;
conn_prepare_reset(conn, open_handler); conn_prepare_reset(conn, open_handler);
/* FIXME: it could happen that the connect returns immediately as /* FIXME: it could happen that the connect returns immediately as

View File

@@ -61,7 +61,7 @@ static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
return s; return s;
} }
/** Compute SHA1 message digest /** Compute SHA1 message digest.
* Returns an allocated string which represents SHA1 message digest in * Returns an allocated string which represents SHA1 message digest in
* hexadecimal notation. The string must be freed with xmpp_free(). * hexadecimal notation. The string must be freed with xmpp_free().
* *
@@ -81,9 +81,38 @@ char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
return digest_to_string_alloc(ctx, digest); return digest_to_string_alloc(ctx, digest);
} }
/** Create new SHA1 object /** Compute SHA1 message digest.
* Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE
* bytes long.
* *
* @param ctx a Strophe context onject * @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 * @return new SHA1 object
* *
@@ -102,7 +131,7 @@ xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
return sha1; return sha1;
} }
/** Destroy SHA1 object /** Destroy SHA1 object.
* *
* @param sha1 a SHA1 object * @param sha1 a SHA1 object
* *
@@ -113,7 +142,7 @@ void xmpp_sha1_free(xmpp_sha1_t *sha1)
xmpp_free(sha1->xmpp_ctx, sha1); xmpp_free(sha1->xmpp_ctx, sha1);
} }
/** Update SHA1 context with the next portion of data /** Update SHA1 context with the next portion of data.
* Can be called repeatedly. * Can be called repeatedly.
* *
* @param sha1 a SHA1 object * @param sha1 a SHA1 object
@@ -127,7 +156,7 @@ void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len)
crypto_SHA1_Update(&sha1->ctx, data, len); crypto_SHA1_Update(&sha1->ctx, data, len);
} }
/** Finish SHA1 computation /** Finish SHA1 computation.
* Don't call xmpp_sha1_update() after this function. Retrieve resulting * Don't call xmpp_sha1_update() after this function. Retrieve resulting
* message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest(). * message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest().
* *
@@ -140,7 +169,7 @@ void xmpp_sha1_final(xmpp_sha1_t *sha1)
crypto_SHA1_Final(&sha1->ctx, sha1->digest); crypto_SHA1_Final(&sha1->ctx, sha1->digest);
} }
/** Return message digest rendered as a string /** Return message digest rendered as a string.
* Stores the string to a user's buffer and returns the buffer. Call this * Stores the string to a user's buffer and returns the buffer. Call this
* function after xmpp_sha1_final(). * function after xmpp_sha1_final().
* *
@@ -157,7 +186,7 @@ char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
return digest_to_string(sha1->digest, s, slen); return digest_to_string(sha1->digest, s, slen);
} }
/** Return message digest rendered as a string /** Return message digest rendered as a string.
* Returns an allocated string. Free the string using the Strophe context * Returns an allocated string. Free the string using the Strophe context
* which is passed to xmpp_sha1_new(). Call this function after * which is passed to xmpp_sha1_new(). Call this function after
* xmpp_sha1_final(). * xmpp_sha1_final().
@@ -173,7 +202,7 @@ char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1)
return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest); return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest);
} }
/** Stores message digest to a user's buffer /** Stores message digest to a user's buffer.
* *
* @param sha1 a SHA1 object * @param sha1 a SHA1 object
* @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes * @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
@@ -186,43 +215,34 @@ void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest)
memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE); memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
} }
/* Base64 encoding routines. Implemented according to RFC 3548. */ /* Base64 encoding routines. Implemented according to RFC 3548. */
/* map of all byte values to the base64 values, or to /* map of all byte values to the base64 values, or to
'65' which indicates an invalid character. '=' is '64' */ '65' which indicates an invalid character. '=' is '64' */
static const unsigned char _base64_invcharmap[256] = { 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, 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, 65, 65, 65, 65, 65, 62, 65, 65, 65, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65, 61, 65, 65, 65, 64, 65, 65, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 65, 65, 65, 65,
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65, 65, 65, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 43, 44, 45, 46, 47, 48, 49, 50, 51, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
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, 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 /* map of all 6-bit values to their corresponding byte
in the base64 alphabet. Padding char is the value '64' */ in the base64 alphabet. Padding char is the value '64' */
static const char _base64_charmap[65] = { static const char _base64_charmap[65] = {
'A','B','C','D', 'E','F','G','H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'I','J','K','L', 'M','N','O','P', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'Q','R','S','T', 'U','V','W','X', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'Y','Z','a','b', 'c','d','e','f', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'g','h','i','j', 'k','l','m','n', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
'o','p','q','r', 's','t','u','v',
'w','x','y','z', '0','1','2','3',
'4','5','6','7', '8','9','+','/',
'='
};
static size_t base64_encoded_len(const size_t len) static size_t base64_encoded_len(const size_t len)
{ {
@@ -231,7 +251,8 @@ static size_t base64_encoded_len(const size_t len)
} }
static char *base64_encode(xmpp_ctx_t *ctx, static char *base64_encode(xmpp_ctx_t *ctx,
const unsigned char * const buffer, const size_t len) const unsigned char *const buffer,
const size_t len)
{ {
size_t clen; size_t clen;
char *cbuf, *c; char *cbuf, *c;
@@ -289,24 +310,31 @@ static size_t base64_decoded_len(const char * const buffer, const size_t len)
unsigned char c; unsigned char c;
size_t i; size_t i;
if (len < 4) return 0; if (len < 4)
return 0;
/* count the padding characters for the remainder */ /* count the padding characters for the remainder */
for (i = len; i > 0; --i) { for (i = len; i > 0; --i) {
c = _base64_invcharmap[(unsigned char)buffer[i - 1]]; c = _base64_invcharmap[(unsigned char)buffer[i - 1]];
if (c < 64) break; if (c < 64)
if (c == 64) ++nudge; break;
if (c > 64) return 0; if (c == 64)
++nudge;
if (c > 64)
return 0;
} }
if (nudge > 2) return 0; if (nudge > 2)
return 0;
/* decoded steam is 3 bytes for every four */ /* decoded steam is 3 bytes for every four */
return 3 * (len >> 2) - nudge; return 3 * (len >> 2) - nudge;
} }
static void base64_decode(xmpp_ctx_t *ctx, static void base64_decode(xmpp_ctx_t *ctx,
const char * const buffer, const size_t len, const char *const buffer,
unsigned char **out, size_t *outlen) const size_t len,
unsigned char **out,
size_t *outlen)
{ {
size_t dlen; size_t dlen;
unsigned char *dbuf, *d; unsigned char *dbuf, *d;
@@ -314,10 +342,12 @@ static void base64_decode(xmpp_ctx_t *ctx,
size_t i; size_t i;
/* len must be a multiple of 4 */ /* len must be a multiple of 4 */
if (len & 0x03) goto _base64_error; if (len & 0x03)
goto _base64_error;
dlen = base64_decoded_len(buffer, len); dlen = base64_decoded_len(buffer, len);
if (dlen == 0) goto _base64_error; if (dlen == 0)
goto _base64_error;
dbuf = xmpp_alloc(ctx, dlen + 1); dbuf = xmpp_alloc(ctx, dlen + 1);
if (dbuf != NULL) { if (dbuf != NULL) {
@@ -325,22 +355,27 @@ static void base64_decode(xmpp_ctx_t *ctx,
/* loop over each set of 4 characters, decoding 3 bytes */ /* loop over each set of 4 characters, decoding 3 bytes */
for (i = 0; i + 3 < len; i += 4) { for (i = 0; i + 3 < len; i += 4) {
hextet = _base64_invcharmap[(unsigned char)buffer[i]]; hextet = _base64_invcharmap[(unsigned char)buffer[i]];
if (hextet & 0xC0) break; if (hextet & 0xC0)
break;
word = hextet << 18; word = hextet << 18;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 1]]; hextet = _base64_invcharmap[(unsigned char)buffer[i + 1]];
if (hextet & 0xC0) break; if (hextet & 0xC0)
break;
word |= hextet << 12; word |= hextet << 12;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 2]]; hextet = _base64_invcharmap[(unsigned char)buffer[i + 2]];
if (hextet & 0xC0) break; if (hextet & 0xC0)
break;
word |= hextet << 6; word |= hextet << 6;
hextet = _base64_invcharmap[(unsigned char)buffer[i + 3]]; hextet = _base64_invcharmap[(unsigned char)buffer[i + 3]];
if (hextet & 0xC0) break; if (hextet & 0xC0)
break;
word |= hextet; word |= hextet;
*d++ = (word & 0x00FF0000) >> 16; *d++ = (word & 0x00FF0000) >> 16;
*d++ = (word & 0x0000FF00) >> 8; *d++ = (word & 0x0000FF00) >> 8;
*d++ = (word & 0x000000FF); *d++ = (word & 0x000000FF);
} }
if (hextet > 64) goto _base64_decode_error; if (hextet > 64)
goto _base64_decode_error;
/* handle the remainder */ /* handle the remainder */
switch (dlen % 3) { switch (dlen % 3) {
case 0: case 0:
@@ -349,32 +384,40 @@ static void base64_decode(xmpp_ctx_t *ctx,
case 1: case 1:
/* redo the last quartet, checking for correctness */ /* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
if (hextet & 0xC0) goto _base64_decode_error; if (hextet & 0xC0)
goto _base64_decode_error;
word = hextet << 2; word = hextet << 2;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
if (hextet & 0xC0) goto _base64_decode_error; if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet >> 4; word |= hextet >> 4;
*d++ = word & 0xFF; *d++ = word & 0xFF;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
if (hextet != 64) goto _base64_decode_error; if (hextet != 64)
goto _base64_decode_error;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
if (hextet != 64) goto _base64_decode_error; if (hextet != 64)
goto _base64_decode_error;
break; break;
case 2: case 2:
/* redo the last quartet, checking for correctness */ /* redo the last quartet, checking for correctness */
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
if (hextet & 0xC0) goto _base64_decode_error; if (hextet & 0xC0)
goto _base64_decode_error;
word = hextet << 10; word = hextet << 10;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
if (hextet & 0xC0) goto _base64_decode_error; if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet << 4; word |= hextet << 4;
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
if (hextet & 0xC0) goto _base64_decode_error; if (hextet & 0xC0)
goto _base64_decode_error;
word |= hextet >> 2; word |= hextet >> 2;
*d++ = (word & 0xFF00) >> 8; *d++ = (word & 0xFF00) >> 8;
*d++ = (word & 0x00FF); *d++ = (word & 0x00FF);
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]]; hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
if (hextet != 64) goto _base64_decode_error; if (hextet != 64)
goto _base64_decode_error;
break; break;
} }
*d = '\0'; *d = '\0';
@@ -391,7 +434,7 @@ _base64_error:
*outlen = 0; *outlen = 0;
} }
/** Base64 encoding routine /** Base64 encoding routine.
* Returns an allocated string which must be freed with xmpp_free(). * Returns an allocated string which must be freed with xmpp_free().
* *
* @param ctx a Strophe context * @param ctx a Strophe context
@@ -407,7 +450,7 @@ char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
return base64_encode(ctx, data, len); return base64_encode(ctx, data, len);
} }
/** Base64 decoding routine /** Base64 decoding routine.
* Returns an allocated string which must be freed with xmpp_free(). User * 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 * calls this function when the result must be a string. When decoded buffer
* contains '\0' NULL is returned. * contains '\0' NULL is returned.
@@ -443,7 +486,7 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
return (char *)buf; return (char *)buf;
} }
/** Base64 decoding routine /** Base64 decoding routine.
* Returns an allocated buffer which must be freed with xmpp_free(). * Returns an allocated buffer which must be freed with xmpp_free().
* *
* @param ctx a Strophe context * @param ctx a Strophe context
@@ -456,8 +499,11 @@ char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
* *
* @ingroup Encodings * @ingroup Encodings
*/ */
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len, void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
unsigned char **out, size_t *outlen) const char *base64,
size_t len,
unsigned char **out,
size_t *outlen)
{ {
base64_decode(ctx, base64, len, out, outlen); base64_decode(ctx, base64, len, out, outlen);
} }

View File

@@ -47,11 +47,13 @@
#include "strophe.h" #include "strophe.h"
#include "common.h" #include "common.h"
#include "resolver.h"
#include "util.h" #include "util.h"
/* Workaround for visual studio without va_copy support. */ /* Workaround for systems without va_copy support. */
#if defined(_MSC_VER) && _MSC_VER < 1800 #if defined(_MSC_VER) && _MSC_VER < 1800 || \
#define va_copy(d,s) ((d) = (s)) !defined(_MSC_VER) && !defined(HAVE_DECL_VA_COPY)
#define va_copy(d, s) (memcpy(&d, &s, sizeof(va_list)))
#endif #endif
/** Initialize the Strophe library. /** Initialize the Strophe library.
@@ -63,6 +65,7 @@
void xmpp_initialize(void) void xmpp_initialize(void)
{ {
sock_initialize(); sock_initialize();
resolver_initialize();
tls_initialize(); tls_initialize();
} }
@@ -73,6 +76,7 @@
void xmpp_shutdown(void) void xmpp_shutdown(void)
{ {
tls_shutdown(); tls_shutdown();
resolver_shutdown();
sock_shutdown(); sock_shutdown();
} }
@@ -91,6 +95,14 @@ void xmpp_shutdown(void)
#define LIBXMPP_VERSION_MINOR (0) #define LIBXMPP_VERSION_MINOR (0)
#endif #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. /** Check that Strophe supports a specific API version.
* *
* @param major the major version number * @param major the major version number
@@ -102,8 +114,7 @@ void xmpp_shutdown(void)
*/ */
int xmpp_version_check(int major, int minor) int xmpp_version_check(int major, int minor)
{ {
return (major == LIBXMPP_VERSION_MAJOR) && return (major == LIBXMPP_VERSION_MAJOR) && (minor >= LIBXMPP_VERSION_MINOR);
(minor >= LIBXMPP_VERSION_MINOR);
} }
/* We define the global default allocator, logger, and context here. */ /* We define the global default allocator, logger, and context here. */
@@ -113,33 +124,32 @@ int xmpp_version_check(int major, int minor)
*/ */
static void *_malloc(const size_t size, void *const userdata) static void *_malloc(const size_t size, void *const userdata)
{ {
UNUSED(userdata);
return malloc(size); return malloc(size);
} }
static void _free(void *p, void *const userdata) static void _free(void *p, void *const userdata)
{ {
UNUSED(userdata);
free(p); free(p);
} }
static void *_realloc(void *p, const size_t size, void *const userdata) static void *_realloc(void *p, const size_t size, void *const userdata)
{ {
UNUSED(userdata);
return realloc(p, size); return realloc(p, size);
} }
/* default memory function map */ /* default memory function map */
static xmpp_mem_t xmpp_default_mem = { static xmpp_mem_t xmpp_default_mem = {
_malloc, /* use the thinly wrapped stdlib routines by default */ _malloc, /* use the thinly wrapped stdlib routines by default */
_free, _free, _realloc, NULL};
_realloc,
NULL
};
/* log levels and names */ /* log levels and names */
static const char * const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"}; static const char *const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN",
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG, "ERROR"};
XMPP_LEVEL_INFO, static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
XMPP_LEVEL_WARN, XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
XMPP_LEVEL_ERROR};
/** Log a message. /** Log a message.
* The default logger writes to stderr. * The default logger writes to stderr.
@@ -161,11 +171,14 @@ static void xmpp_default_logger(void * const userdata,
} }
static const xmpp_log_t _xmpp_default_loggers[] = { static const xmpp_log_t _xmpp_default_loggers[] = {
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]}, {&xmpp_default_logger,
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]}, (void *)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]}, {&xmpp_default_logger,
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]} (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. /** Get a default logger with filtering.
* The default logger provides a basic logging setup which writes log * The default logger provides a basic logging setup which writes log
@@ -181,8 +194,8 @@ static const xmpp_log_t _xmpp_default_loggers[] = {
xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level) xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
{ {
/* clamp to the known range */ /* clamp to the known range */
if (level > XMPP_LEVEL_ERROR) level = XMPP_LEVEL_ERROR; if (level > XMPP_LEVEL_ERROR)
if (level < XMPP_LEVEL_DEBUG) level = XMPP_LEVEL_DEBUG; level = XMPP_LEVEL_ERROR;
return (xmpp_log_t *)&_xmpp_default_loggers[level]; return (xmpp_log_t *)&_xmpp_default_loggers[level];
} }
@@ -224,8 +237,7 @@ void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
* *
* @return a pointer to the reallocated memory or NULL on an error * @return a pointer to the reallocated memory or NULL on an error
*/ */
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p, void *xmpp_realloc(const xmpp_ctx_t *const ctx, void *p, const size_t size)
const size_t size)
{ {
return ctx->mem->realloc(p, size, ctx->mem->userdata); return ctx->mem->realloc(p, size, ctx->mem->userdata);
} }
@@ -408,8 +420,10 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
ctx->log = log; ctx->log = log;
ctx->connlist = NULL; ctx->connlist = NULL;
ctx->timed_handlers = NULL;
ctx->loop_status = XMPP_LOOP_NOTSTARTED; ctx->loop_status = XMPP_LOOP_NOTSTARTED;
ctx->rand = xmpp_rand_new(ctx); ctx->rand = xmpp_rand_new(ctx);
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
if (ctx->rand == NULL) { if (ctx->rand == NULL) {
xmpp_free(ctx, ctx); xmpp_free(ctx, ctx);
ctx = NULL; ctx = NULL;
@@ -432,3 +446,14 @@ void xmpp_ctx_free(xmpp_ctx_t * const ctx)
xmpp_free(ctx, ctx); /* pull the hole in after us */ xmpp_free(ctx, ctx); /* pull the hole in after us */
} }
/** 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 Context
*/
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout)
{
ctx->timeout = timeout;
}

View File

@@ -41,23 +41,15 @@
#define _sleep(x) usleep((x)*1000) #define _sleep(x) usleep((x)*1000)
#else #else
#include <winsock2.h> #include <winsock2.h>
#define ETIMEDOUT WSAETIMEDOUT
#define ECONNRESET WSAECONNRESET
#define ECONNABORTED WSAECONNABORTED
#define _sleep(x) Sleep(x) #define _sleep(x) Sleep(x)
#endif #endif
#include <strophe.h> #include "strophe.h"
#include "common.h" #include "common.h"
#include "parser.h" #include "parser.h"
#ifndef DEFAULT_TIMEOUT /** Max buffer size for receiving messages. */
/** @def DEFAULT_TIMEOUT #define STROPE_MESSAGE_BUFFER_SIZE 4096
* The default timeout in milliseconds for the event loop.
* This is set to 1 millisecond.
*/
#define DEFAULT_TIMEOUT 1
#endif
/** Run the event loop once. /** Run the event loop once.
* This function will run send any data that has been queued by * This function will run send any data that has been queued by
@@ -82,12 +74,13 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
struct timeval tv; struct timeval tv;
xmpp_send_queue_t *sq, *tsq; xmpp_send_queue_t *sq, *tsq;
int towrite; int towrite;
char buf[4096]; char buf[STROPE_MESSAGE_BUFFER_SIZE];
uint64_t next; uint64_t next;
uint64_t usec; uint64_t usec;
int tls_read_bytes = 0; int tls_read_bytes = 0;
if (ctx->loop_status == XMPP_LOOP_QUIT) return; if (ctx->loop_status == XMPP_LOOP_QUIT)
return;
/* send queued data */ /* send queued data */
connitem = ctx->connlist; connitem = ctx->connlist;
@@ -104,44 +97,29 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
ret = tls_clear_pending_write(conn->tls); ret = tls_clear_pending_write(conn->tls);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) { if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
/* an error occured */ /* an error occurred */
xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting."); conn->error = XMPP_EIO;
conn->error = ECONNABORTED;
conn_disconnect(conn);
} }
} }
/* write all data from the send queue to the socket */ /* write all data from the send queue to the socket */
sq = conn->send_queue_head; sq = conn->send_queue_head;
while (sq) { while (!conn->error && sq) {
towrite = sq->len - sq->written; towrite = sq->len - sq->written;
if (conn->tls) { if (conn->tls) {
ret = tls_write(conn->tls, &sq->data[sq->written], towrite); ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls)))
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) { conn->error = XMPP_EIO;
/* 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;
}
} else { } else {
ret = sock_write(conn->sock, &sq->data[sq->written], towrite); ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
if (ret < 0 && !sock_is_recoverable(sock_error()))
if (ret < 0 && !sock_is_recoverable(sock_error())) { conn->error = XMPP_EIO;
/* 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;
}
} }
if (ret > 0 && ret < towrite)
sq->written += ret; /* not all data could be sent now */
if (ret != towrite)
break; /* partial write or an error */
/* all data for this queue item written, delete and move on */ /* all data for this queue item written, delete and move on */
xmpp_free(ctx, sq->data); xmpp_free(ctx, sq->data);
@@ -153,15 +131,15 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* pop the top item */ /* pop the top item */
conn->send_queue_head = sq; conn->send_queue_head = sq;
/* if we've sent everything update the tail */ /* if we've sent everything update the tail */
if (!sq) conn->send_queue_tail = NULL; if (!sq)
conn->send_queue_tail = NULL;
} }
/* tear down connection on error */ /* tear down connection on error */
if (conn->error) { if (conn->error) {
/* FIXME: need to tear down send queues and random other things /* FIXME: need to tear down send queues and random other things
* maybe this should be abstracted */ * maybe this should be abstracted */
xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting."); xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
conn->error = ECONNABORTED;
conn_disconnect(conn); conn_disconnect(conn);
} }
@@ -174,10 +152,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
conn_parser_reset(connitem->conn); conn_parser_reset(connitem->conn);
} }
/* fire any ready timed handlers, then make sure we don't wait past
/* fire any ready timed handlers, then the time when timed handlers need to be called */
make sure we don't wait past the time when timed handlers need
to be called */
next = handler_fire_timed(ctx); next = handler_fire_timed(ctx);
usec = ((next < timeout) ? next : timeout) * 1000; usec = ((next < timeout) ? next : timeout) * 1000;
@@ -202,13 +178,15 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
conn->connect_timeout) conn->connect_timeout)
FD_SET(conn->sock, &wfds); FD_SET(conn->sock, &wfds);
else { else {
conn->error = ETIMEDOUT;
xmpp_info(ctx, "xmpp", "Connection attempt timed out."); xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
conn->error = XMPP_ETIMEDOUT;
conn_disconnect(conn); conn_disconnect(conn);
} }
break; break;
case XMPP_STATE_CONNECTED: case XMPP_STATE_CONNECTED:
FD_SET(conn->sock, &rfds); FD_SET(conn->sock, &rfds);
if (conn->send_queue_len > 0)
FD_SET(conn->sock, &wfds);
break; break;
case XMPP_STATE_DISCONNECTED: case XMPP_STATE_DISCONNECTED:
/* do nothing */ /* do nothing */
@@ -217,9 +195,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
} }
/* Check if there is something in the SSL buffer. */ /* Check if there is something in the SSL buffer. */
if (conn->tls) { if (conn->tls)
tls_read_bytes += tls_pending(conn->tls); tls_read_bytes += tls_pending(conn->tls);
}
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max) if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
max = conn->sock; max = conn->sock;
@@ -245,7 +222,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
} }
/* no events happened */ /* no events happened */
if (ret == 0 && tls_read_bytes == 0) return; if (ret == 0 && tls_read_bytes == 0)
return;
/* process events */ /* process events */
connitem = ctx->connlist; connitem = ctx->connlist;
@@ -262,6 +240,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
if (ret != 0) { if (ret != 0) {
/* connection failed */ /* connection failed */
xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret); xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret);
conn->error = XMPP_EIO;
conn_disconnect(conn); conn_disconnect(conn);
break; break;
} }
@@ -273,33 +252,36 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
break; break;
case XMPP_STATE_CONNECTED: case XMPP_STATE_CONNECTED:
if (FD_ISSET(conn->sock, &rfds) || (conn->tls && tls_pending(conn->tls))) { if (FD_ISSET(conn->sock, &rfds) ||
(conn->tls && tls_pending(conn->tls))) {
if (conn->tls) { if (conn->tls) {
ret = tls_read(conn->tls, buf, 4096); ret = tls_read(conn->tls, buf, STROPE_MESSAGE_BUFFER_SIZE);
} else { } else {
ret = sock_read(conn->sock, buf, 4096); ret =
sock_read(conn->sock, buf, STROPE_MESSAGE_BUFFER_SIZE);
} }
if (ret > 0) { if (ret > 0) {
ret = parser_feed(conn->parser, buf, ret); ret = parser_feed(conn->parser, buf, ret);
if (!ret) { if (!ret) {
/* parse error, we need to shut down */ xmpp_debug(ctx, "xmpp", "parse error [%s]", buf);
/* FIXME */ xmpp_send_error(conn, XMPP_SE_INVALID_XML,
xmpp_debug(ctx, "xmpp", "parse error, disconnecting"); "parse error");
conn_disconnect(conn);
} }
} else { } else {
if (conn->tls) { if (conn->tls) {
if (!tls_is_recoverable(tls_error(conn->tls))) if (!tls_is_recoverable(tls_error(conn->tls))) {
{ xmpp_debug(ctx, "xmpp",
xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls)); "Unrecoverable TLS error, %d.",
conn->error = tls_error(conn->tls); tls_error(conn->tls));
conn->error = XMPP_EIO;
conn_disconnect(conn); conn_disconnect(conn);
} }
} else { } else {
/* return of 0 means socket closed by server */ /* return of 0 means socket closed by server */
xmpp_debug(ctx, "xmpp", "Socket closed by remote host."); xmpp_debug(ctx, "xmpp",
conn->error = ECONNRESET; "Socket closed by remote host.");
conn->error = ret == 0 ? XMPP_ERESET : XMPP_EIO;
conn_disconnect(conn); conn_disconnect(conn);
} }
} }
@@ -329,11 +311,12 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
*/ */
void xmpp_run(xmpp_ctx_t *ctx) 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; ctx->loop_status = XMPP_LOOP_RUNNING;
while (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);
} }
/* make it possible to start event loop again */ /* make it possible to start event loop again */

View File

@@ -24,6 +24,28 @@
#include "common.h" #include "common.h"
#include "ostypes.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;
}
}
/** Fire off all stanza handlers that match. /** Fire off all stanza handlers that match.
* This function is called internally by the event loop whenever stanzas * This function is called internally by the event loop whenever stanzas
* are received from the XMPP server. * are received from the XMPP server.
@@ -31,39 +53,43 @@
* @param conn a Strophe connection object * @param conn a Strophe connection object
* @param stanza a Strophe stanza object * @param stanza a Strophe stanza object
*/ */
void handler_fire_stanza(xmpp_conn_t * const conn, void handler_fire_stanza(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza)
xmpp_stanza_t * const stanza)
{ {
xmpp_handlist_t *item, *prev; xmpp_handlist_t *item, *next, *head, *head_old;
const char *id, *ns, *name, *type; const char *id, *ns, *name, *type;
int ret;
/* call id handlers */ /* call id handlers */
id = xmpp_stanza_get_id(stanza); id = xmpp_stanza_get_id(stanza);
if (id) { if (id) {
prev = NULL; head = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); /* enable all added handlers */
while (item) { for (item = head; item; item = item->next)
xmpp_handlist_t *next = item->next; item->enabled = 1;
if (item->user_handler && !conn->authenticated) { item = head;
item = next; while (item) {
/* don't fire user handlers until authentication succeeds and
and skip newly added handlers */
if ((item->user_handler && !conn->authenticated) ||
!item->enabled) {
item = item->next;
continue; continue;
} }
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) { ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
next = item->next;
if (!ret) {
/* handler is one-shot, so delete it */ /* handler is one-shot, so delete it */
if (prev) head_old = head;
prev->next = next; _handler_item_remove(&head, item);
else { if (head != head_old) {
hash_drop(conn->id_handlers, id); /* replace old value */
hash_add(conn->id_handlers, id, next); hash_add(conn->id_handlers, id, head);
} }
xmpp_free(conn->ctx, item->id); xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
item = NULL;
} }
if (item)
prev = item;
item = next; item = next;
} }
} }
@@ -77,47 +103,37 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
for (item = conn->handlers; item; item = item->next) for (item = conn->handlers; item; item = item->next)
item->enabled = 1; item->enabled = 1;
prev = NULL;
item = conn->handlers; item = conn->handlers;
while (item) { while (item) {
/* skip newly added handlers */ /* don't fire user handlers until authentication succeeds and
if (!item->enabled) { skip newly added handlers */
prev = item; if ((item->user_handler && !conn->authenticated) || !item->enabled) {
item = item->next; item = item->next;
continue; continue;
} }
/* don't call user handlers until authentication succeeds */ next = item->next;
if (item->user_handler && !conn->authenticated) { if ((!item->u.ns || (ns && strcmp(ns, item->u.ns) == 0) ||
prev = item; xmpp_stanza_get_child_by_ns(stanza, item->u.ns)) &&
item = item->next; (!item->u.name || (name && strcmp(name, item->u.name) == 0)) &&
continue; (!item->u.type || (type && strcmp(type, item->u.type) == 0))) {
}
if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) || ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
xmpp_stanza_get_child_by_ns(stanza, item->ns)) && /* list may be changed during execution of a handler */
(!item->name || (name && strcmp(name, item->name) == 0)) && next = item->next;
(!item->type || (type && strcmp(type, item->type) == 0))) if (!ret) {
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
/* handler is one-shot, so delete it */ /* handler is one-shot, so delete it */
if (prev) _handler_item_remove(&conn->handlers, item);
prev->next = item->next; if (item->u.ns)
else xmpp_free(conn->ctx, item->u.ns);
conn->handlers = item->next; if (item->u.name)
if (item->ns) xmpp_free(conn->ctx, item->ns); xmpp_free(conn->ctx, item->u.name);
if (item->name) xmpp_free(conn->ctx, item->name); if (item->u.type)
if (item->type) xmpp_free(conn->ctx, item->type); xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
item = NULL;
} }
}
if (item) { item = next;
prev = item;
item = item->next;
} else if (prev)
item = prev->next;
else
item = conn->handlers;
} }
} }
@@ -131,59 +147,87 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
uint64_t handler_fire_timed(xmpp_ctx_t *const ctx) uint64_t handler_fire_timed(xmpp_ctx_t *const ctx)
{ {
xmpp_connlist_t *connitem; xmpp_connlist_t *connitem;
xmpp_handlist_t *handitem, *temp; xmpp_handlist_t *item, *next;
int ret, fired; xmpp_conn_t *conn;
uint64_t elapsed, min; uint64_t elapsed, min;
uint64_t timestamp;
int ret;
min = (uint64_t)(-1); min = (uint64_t)(-1);
connitem = ctx->connlist; connitem = ctx->connlist;
while (connitem) { while (connitem) {
if (connitem->conn->state != XMPP_STATE_CONNECTED) { conn = connitem->conn;
if (conn->state != XMPP_STATE_CONNECTED) {
connitem = connitem->next; connitem = connitem->next;
continue; continue;
} }
/* enable all handlers that were added */ /* enable all handlers that were added */
for (handitem = connitem->conn->timed_handlers; handitem; for (item = conn->timed_handlers; item; item = item->next)
handitem = handitem->next) item->enabled = 1;
handitem->enabled = 1;
handitem = connitem->conn->timed_handlers; item = conn->timed_handlers;
while (handitem) { while (item) {
/* skip newly added handlers */ /* don't fire user handlers until authentication succeeds and
if (!handitem->enabled) { skip newly added handlers */
handitem = handitem->next; if ((item->user_handler && !conn->authenticated) ||
!item->enabled) {
item = item->next;
continue; continue;
} }
/* only fire user handlers after authentication */ next = item->next;
if (handitem->user_handler && !connitem->conn->authenticated) { timestamp = time_stamp();
handitem = handitem->next; elapsed = time_elapsed(item->u.last_stamp, timestamp);
continue; if (elapsed >= item->u.period) {
}
fired = 0;
elapsed = time_elapsed(handitem->last_stamp, time_stamp());
if (elapsed >= handitem->period) {
/* fire! */ /* fire! */
fired = 1; item->u.last_stamp = timestamp;
handitem->last_stamp = time_stamp(); ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata);
ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata); /* list may be changed during execution of a handler */
} else if (min > (handitem->period - elapsed)) next = item->next;
min = handitem->period - elapsed; if (!ret) {
temp = handitem;
handitem = handitem->next;
/* delete handler if it returned false */ /* delete handler if it returned false */
if (fired && !ret) _handler_item_remove(&conn->timed_handlers, item);
xmpp_timed_handler_delete(connitem->conn, temp->handler); xmpp_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
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);
xmpp_free(ctx, item);
}
} else if (min > (item->u.period - elapsed))
min = item->u.period - elapsed;
item = next;
}
return min; return min;
} }
@@ -200,48 +244,63 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
handitem = conn->timed_handlers; handitem = conn->timed_handlers;
while (handitem) { while (handitem) {
if ((user_only && handitem->user_handler) || !user_only) if ((user_only && handitem->user_handler) || !user_only)
handitem->last_stamp = time_stamp(); handitem->u.last_stamp = time_stamp();
handitem = handitem->next; handitem = handitem->next;
} }
} }
static void _timed_handler_add(xmpp_conn_t * const conn, static void _timed_handler_add(xmpp_ctx_t *ctx,
xmpp_timed_handler handler, xmpp_handlist_t **handlers_list,
xmpp_void_handler handler,
const unsigned long period, const unsigned long period,
void *const userdata, void *const userdata,
const int user_handler) const int user_handler)
{ {
xmpp_handlist_t *item, *tail; xmpp_handlist_t *item;
/* check if handler is already in the list */ /* check if handler is already in the list */
for (item = conn->timed_handlers; item; item = item->next) { for (item = *handlers_list; item; item = item->next) {
if (item->handler == (void *)handler) if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(ctx, "xmpp", "Timed handler already exists.");
break; break;
} }
if (item) return; }
if (item)
return;
/* build new item */ /* build new item */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); item = xmpp_alloc(ctx, sizeof(xmpp_handlist_t));
if (!item) return; if (!item)
return;
item->user_handler = user_handler; item->user_handler = user_handler;
item->handler = (void *)handler; item->handler = handler;
item->userdata = userdata; item->userdata = userdata;
item->enabled = 0; item->enabled = 0;
item->next = NULL;
item->period = period; item->u.period = period;
item->last_stamp = time_stamp(); item->u.last_stamp = time_stamp();
/* append item to list */ /* append item to list */
if (!conn->timed_handlers) item->next = *handlers_list;
conn->timed_handlers = item; *handlers_list = item;
else { }
tail = conn->timed_handlers;
while (tail->next) static void _timed_handler_delete(xmpp_ctx_t *ctx,
tail = tail->next; xmpp_handlist_t **handlers_list,
tail->next = item; xmpp_void_handler handler)
{
xmpp_handlist_t *item;
while (*handlers_list) {
item = *handlers_list;
if (item->handler == handler) {
*handlers_list = item->next;
xmpp_free(ctx, item);
} else {
handlers_list = &item->next;
}
} }
} }
@@ -255,57 +314,42 @@ static void _timed_handler_add(xmpp_conn_t * const conn,
void xmpp_timed_handler_delete(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
xmpp_timed_handler handler) xmpp_timed_handler handler)
{ {
xmpp_handlist_t *item, *prev; _timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
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);
}
} }
static void _id_handler_add(xmpp_conn_t *const conn, static void _id_handler_add(xmpp_conn_t *const conn,
xmpp_handler handler, xmpp_handler handler,
const char *const id, const char *const id,
void * const userdata, int user_handler) void *const userdata,
int user_handler)
{ {
xmpp_handlist_t *item, *tail; xmpp_handlist_t *item, *tail;
/* check if handler is already in the list */ /* check if handler is already in the list */
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
while (item) { while (item) {
if (item->handler == (void *)handler) if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(conn->ctx, "xmpp", "Id handler already exists.");
break; break;
}
item = item->next; item = item->next;
} }
if (item) return; if (item)
return;
/* build new item */ /* build new item */
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item) return; if (!item)
return;
item->user_handler = user_handler; item->user_handler = user_handler;
item->handler = (void *)handler; item->handler = handler;
item->userdata = userdata; item->userdata = userdata;
item->enabled = 0; item->enabled = 0;
item->next = NULL; item->next = NULL;
item->id = xmpp_strdup(conn->ctx, id); item->u.id = xmpp_strdup(conn->ctx, id);
if (!item->id) { if (!item->u.id) {
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
return; return;
} }
@@ -333,29 +377,31 @@ void xmpp_id_handler_delete(xmpp_conn_t * const conn,
xmpp_handler handler, xmpp_handler handler,
const char *const id) const char *const id)
{ {
xmpp_handlist_t *item, *prev; xmpp_handlist_t *item, *prev, *next;
prev = NULL; prev = NULL;
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id); item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
if (!item) return; if (!item)
return;
while (item) { while (item) {
if (item->handler == (void *)handler) next = item->next;
break;
prev = item; if (item->handler == handler) {
item = item->next;
}
if (item) {
if (prev) if (prev)
prev->next = item->next; prev->next = next;
else { else {
hash_drop(conn->id_handlers, id); hash_drop(conn->id_handlers, id);
hash_add(conn->id_handlers, id, item->next); hash_add(conn->id_handlers, id, next);
} }
xmpp_free(conn->ctx, item->id);
xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
item = next;
} else {
prev = item;
item = next;
}
} }
} }
@@ -365,53 +411,65 @@ static void _handler_add(xmpp_conn_t * const conn,
const char *const ns, const char *const ns,
const char *const name, const char *const name,
const char *const type, const char *const type,
void * const userdata, int user_handler) void *const userdata,
int user_handler)
{ {
xmpp_handlist_t *item, *tail; xmpp_handlist_t *item, *tail;
/* check if handler already in list */ /* check if handler already in list */
for (item = conn->handlers; item; item = item->next) { for (item = conn->handlers; item; item = item->next) {
if (item->handler == (void *)handler) /* same handler function can process different stanzas and
distinguish them according to userdata. */
if (item->handler == handler && item->userdata == userdata) {
xmpp_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
break; break;
} }
if (item) return; }
if (item)
return;
/* build new item */ /* build new item */
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t)); item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
if (!item) return; if (!item)
return;
item->user_handler = user_handler; item->user_handler = user_handler;
item->handler = (void *)handler; item->handler = handler;
item->userdata = userdata; item->userdata = userdata;
item->enabled = 0; item->enabled = 0;
item->next = NULL; item->next = NULL;
if (ns) { if (ns) {
item->ns = xmpp_strdup(conn->ctx, ns); item->u.ns = xmpp_strdup(conn->ctx, ns);
if (!item->ns) { if (!item->u.ns) {
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
return; return;
} }
} else } else
item->ns = NULL; item->u.ns = NULL;
if (name) { if (name) {
item->name = xmpp_strdup(conn->ctx, name); item->u.name = xmpp_strdup(conn->ctx, name);
if (!item->name) { if (!item->u.name) {
if (item->ns) xmpp_free(conn->ctx, item->ns); if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
return; return;
} }
} else } else
item->name = NULL; item->u.name = NULL;
if (type) { if (type) {
item->type = xmpp_strdup(conn->ctx, type); item->u.type = xmpp_strdup(conn->ctx, type);
if (!item->type) { if (!item->u.type) {
if (item->ns) xmpp_free(conn->ctx, item->ns); if (item->u.ns)
if (item->name) xmpp_free(conn->ctx, item->name); xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
} }
} else } else
item->type = NULL; item->u.type = NULL;
/* append to list */ /* append to list */
if (!conn->handlers) if (!conn->handlers)
@@ -431,33 +489,34 @@ static void _handler_add(xmpp_conn_t * const conn,
* *
* @ingroup Handlers * @ingroup Handlers
*/ */
void xmpp_handler_delete(xmpp_conn_t * const conn, void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler)
xmpp_handler handler)
{ {
xmpp_handlist_t *prev, *item; xmpp_handlist_t *prev, *item;
if (!conn->handlers) return; if (!conn->handlers)
return;
prev = NULL; prev = NULL;
item = conn->handlers; item = conn->handlers;
while (item) { while (item) {
if (item->handler == (void *)handler) if (item->handler == handler) {
break;
prev = item;
item = item->next;
}
if (item) {
if (prev) if (prev)
prev->next = item->next; prev->next = item->next;
else else
conn->handlers = item->next; conn->handlers = item->next;
if (item->ns) xmpp_free(conn->ctx, item->ns); if (item->u.ns)
if (item->name) xmpp_free(conn->ctx, item->name); xmpp_free(conn->ctx, item->u.ns);
if (item->type) xmpp_free(conn->ctx, item->type); if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
if (item->u.type)
xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item); xmpp_free(conn->ctx, item);
item = prev ? prev->next : conn->handlers;
} else {
prev = item;
item = item->next;
}
} }
} }
@@ -482,7 +541,8 @@ void xmpp_timed_handler_add(xmpp_conn_t * const conn,
const unsigned long period, const unsigned long period,
void *const userdata) void *const 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. /** Add a timed system handler.
@@ -499,7 +559,8 @@ void handler_add_timed(xmpp_conn_t * const conn,
const unsigned long period, const unsigned long period,
void *const userdata) void *const 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. /** Add an id based stanza handler.
@@ -595,3 +656,129 @@ void handler_add(xmpp_conn_t * const conn,
{ {
_handler_add(conn, handler, ns, name, type, userdata, 0); _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;
char *key2 = NULL;
/* 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);
if (item->u.ns)
xmpp_free(conn->ctx, item->u.ns);
if (item->u.name)
xmpp_free(conn->ctx, item->u.name);
if (item->u.type)
xmpp_free(conn->ctx, item->u.type);
xmpp_free(conn->ctx, item);
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);
xmpp_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);
xmpp_free(conn->ctx, item->u.id);
xmpp_free(conn->ctx, item);
item = next;
} else
item = item->next;
}
if (head != head_old)
key2 = xmpp_strdup(conn->ctx, key);
/* Hash table implementation is not perfect, so we need to find next
key before dropping current one. Otherwise, we will get access to
freed memory. */
key = hash_iter_next(iter);
if (head != head_old) {
/* hash_add() replaces value if the key exists */
if (head != NULL)
hash_add(conn->id_handlers, key2, head);
else
hash_drop(conn->id_handlers, key2);
xmpp_free(conn->ctx, 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 *const ctx,
xmpp_global_timed_handler handler,
const unsigned long period,
void *const 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 *const ctx,
xmpp_global_timed_handler handler)
{
_timed_handler_delete(ctx, &ctx->timed_handlers, handler);
}

View File

@@ -46,8 +46,8 @@ struct _hash_iterator_t {
}; };
/** allocate and initialize a new hash table */ /** allocate and initialize a new hash table */
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size, hash_t *
hash_free_func free_func) hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func)
{ {
hash_t *result = NULL; hash_t *result = NULL;
@@ -93,7 +93,8 @@ void hash_release(hash_t * const table)
while (entry != NULL) { while (entry != NULL) {
next = entry->next; next = entry->next;
xmpp_free(ctx, entry->key); xmpp_free(ctx, entry->key);
if (table->free) table->free(ctx, entry->value); if (table->free)
table->free(ctx, entry->value);
xmpp_free(ctx, entry); xmpp_free(ctx, entry);
entry = next; entry = next;
} }
@@ -106,18 +107,36 @@ void hash_release(hash_t * const table)
/** hash a key for our table lookup */ /** hash a key for our table lookup */
static int _hash_key(hash_t *table, const char *key) static int _hash_key(hash_t *table, const char *key)
{ {
int hash = 0; unsigned hash = 0;
int shift = 0; unsigned shift = 0;
const char *c = key; const unsigned char *c = (const unsigned char *)key;
while (*c != '\0') { while (*c != 0) {
/* assume 32 bit ints */ /* assume 32 bit ints */
hash ^= ((int)*c++ << shift); hash ^= ((unsigned)*c++ << shift);
shift += 8; shift += 8;
if (shift > 24) shift = 0; 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. /** add a key, value pair to a hash table.
@@ -130,23 +149,29 @@ int hash_add(hash_t *table, const char * const key, void *data)
hashentry_t *entry = NULL; hashentry_t *entry = NULL;
int table_index = _hash_key(table, key); int table_index = _hash_key(table, key);
/* drop existing entry, if any */ /* find and replace existing entry, if any */
hash_drop(table, key); entry = _hash_entry_find(table, key);
if (entry == NULL) {
/* allocate and fill a new entry */ /* allocate and fill a new entry */
entry = xmpp_alloc(ctx, sizeof(hashentry_t)); entry = xmpp_alloc(ctx, sizeof(hashentry_t));
if (!entry) return -1; if (!entry)
return -1;
entry->key = xmpp_strdup(ctx, key); entry->key = xmpp_strdup(ctx, key);
if (!entry->key) { if (!entry->key) {
xmpp_free(ctx, entry); xmpp_free(ctx, entry);
return -1; return -1;
} }
entry->value = data;
/* insert ourselves in the linked list */ /* insert ourselves in the linked list */
/* TODO: this leaks duplicate keys */
entry->next = table->entries[table_index]; entry->next = table->entries[table_index];
table->entries[table_index] = entry; table->entries[table_index] = entry;
table->num_keys++; table->num_keys++;
} else {
if (table->free)
table->free(ctx, entry->value);
}
entry->value = data;
return 0; return 0;
} }
@@ -155,22 +180,9 @@ int hash_add(hash_t *table, const char * const key, void *data)
void *hash_get(hash_t *table, const char *key) void *hash_get(hash_t *table, const char *key)
{ {
hashentry_t *entry; hashentry_t *entry;
int table_index = _hash_key(table, key);
void *result = NULL;
/* look up the hash entry */ entry = _hash_entry_find(table, key);
entry = table->entries[table_index]; return entry == NULL ? NULL : entry->value;
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;
} }
/** delete a key from a hash table */ /** delete a key from a hash table */
@@ -188,7 +200,8 @@ int hash_drop(hash_t *table, const char *key)
if (!strcmp(key, entry->key)) { if (!strcmp(key, entry->key)) {
/* match, remove the entry */ /* match, remove the entry */
xmpp_free(ctx, entry->key); xmpp_free(ctx, entry->key);
if (table->free) table->free(ctx, entry->value); if (table->free)
table->free(ctx, entry->value);
if (prev == NULL) { if (prev == NULL) {
table->entries[table_index] = entry->next; table->entries[table_index] = entry->next;
} else { } else {
@@ -227,7 +240,6 @@ hash_iterator_t *hash_iter_new(hash_t *table)
return iter; return iter;
} }
/** release an iterator that is no longer needed */ /** release an iterator that is no longer needed */
void hash_iter_release(hash_iterator_t *iter) void hash_iter_release(hash_iterator_t *iter)
{ {
@@ -235,7 +247,7 @@ void hash_iter_release(hash_iterator_t *iter)
iter->ref--; iter->ref--;
if (iter->ref <= 0) { if (iter->ref == 0) { // ref is unsigned!!!
hash_release(iter->table); hash_release(iter->table);
xmpp_free(ctx, iter); xmpp_free(ctx, iter);
} }
@@ -250,7 +262,8 @@ const char * hash_iter_next(hash_iterator_t *iter)
int i; int i;
/* advance until we find the next entry */ /* advance until we find the next entry */
if (entry != NULL) entry = entry->next; if (entry != NULL)
entry = entry->next;
if (entry == NULL) { if (entry == NULL) {
/* we're off the end of list, search for a new entry */ /* we're off the end of list, search for a new entry */
i = iter->index + 1; i = iter->index + 1;
@@ -273,4 +286,3 @@ const char * hash_iter_next(hash_iterator_t *iter)
iter->entry = entry; iter->entry = entry;
return entry->key; return entry->key;
} }

View File

@@ -21,8 +21,8 @@ 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 *const ctx, void *p);
/** allocate and initialize a new hash table */ /** allocate and initialize a new hash table */
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size, hash_t *
hash_free_func free_func); hash_new(xmpp_ctx_t *const ctx, const int size, hash_free_func free_func);
/** allocate a new reference to an existing hash table */ /** allocate a new reference to an existing hash table */
hash_t *hash_clone(hash_t *const table); hash_t *hash_clone(hash_t *const table);

View File

@@ -28,7 +28,8 @@
* @return an allocated string with the full JID or NULL if no domain * @return an allocated string with the full JID or NULL if no domain
* is specified * is specified
*/ */
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node, char *xmpp_jid_new(xmpp_ctx_t *ctx,
const char *node,
const char *domain, const char *domain,
const char *resource) const char *resource)
{ {
@@ -36,7 +37,8 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
size_t len, nlen, dlen, rlen; size_t len, nlen, dlen, rlen;
/* jid must at least have a domain */ /* jid must at least have a domain */
if (domain == NULL) return NULL; if (domain == NULL)
return NULL;
/* accumulate lengths */ /* accumulate lengths */
dlen = strlen(domain); dlen = strlen(domain);
@@ -72,15 +74,13 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid) char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
{ {
char *result; char *result;
const char *c; size_t len;
c = strchr(jid, '/'); len = strcspn(jid, "/");
if (c == NULL) return xmpp_strdup(ctx, jid); result = xmpp_alloc(ctx, len + 1);
result = xmpp_alloc(ctx, c-jid+1);
if (result != NULL) { if (result != NULL) {
memcpy(result, jid, c-jid); memcpy(result, jid, len);
result[c-jid] = '\0'; result[len] = '\0';
} }
return result; return result;
@@ -121,7 +121,8 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid) char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
{ {
char *result = NULL; char *result = NULL;
const char *c,*s; const char *c;
size_t dlen;
c = strchr(jid, '@'); c = strchr(jid, '@');
if (c == NULL) { if (c == NULL) {
@@ -131,15 +132,11 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
/* advance past the separator */ /* advance past the separator */
c++; c++;
} }
s = strchr(c, '/'); dlen = strcspn(c, "/"); /* do not include resource */
if (s == NULL) { result = xmpp_alloc(ctx, dlen + 1);
/* no resource */
s = c + strlen(c);
}
result = xmpp_alloc(ctx, (s-c) + 1);
if (result != NULL) { if (result != NULL) {
memcpy(result, c, (s-c)); memcpy(result, c, dlen);
result[s-c] = '\0'; result[dlen] = '\0';
} }
return result; return result;
@@ -155,20 +152,8 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
*/ */
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid) char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
{ {
char *result = NULL;
const char *c; const char *c;
size_t len;
c = strchr(jid, '/'); c = strchr(jid, '/');
if (c != NULL) { return c != NULL ? xmpp_strdup(ctx, c + 1) : NULL;
c++;
len = strlen(c);
result = xmpp_alloc(ctx, len + 1);
if (result != NULL) {
memcpy(result, c, len);
result[len] = '\0';
}
}
return result;
} }

View File

@@ -43,8 +43,7 @@
(cp)[3] = ((value) >> 24) & 0xFF; \ (cp)[3] = ((value) >> 24) & 0xFF; \
} while (0) } while (0)
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64], static void MD5Transform(uint32_t buf[4], const unsigned char inext[64]);
struct MD5Context *ctx);
/* /*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
@@ -91,7 +90,7 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
return; return;
} }
memcpy(p, buf, t); memcpy(p, buf, t);
MD5Transform(ctx->buf, ctx->in, ctx); MD5Transform(ctx->buf, ctx->in);
buf += t; buf += t;
len -= t; len -= t;
} }
@@ -99,7 +98,7 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
while (len >= 64) { while (len >= 64) {
memcpy(ctx->in, buf, 64); memcpy(ctx->in, buf, 64);
MD5Transform(ctx->buf, ctx->in, ctx); MD5Transform(ctx->buf, ctx->in);
buf += 64; buf += 64;
len -= 64; len -= 64;
} }
@@ -133,7 +132,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
if (count < 8) { if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */ /* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count); memset(p, 0, count);
MD5Transform(ctx->buf, ctx->in, ctx); MD5Transform(ctx->buf, ctx->in);
/* Now fill the next block with 56 bytes */ /* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56); memset(ctx->in, 0, 56);
@@ -146,7 +145,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]); PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]); 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, ctx->buf[0]);
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]); PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]); PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
@@ -180,8 +179,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
* reflect the addition of 16 longwords of new data. MD5Update blocks * reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine. * the data and converts bytes into longwords for this routine.
*/ */
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64], static void MD5Transform(uint32_t buf[4], const unsigned char inext[64])
struct MD5Context *ctx)
{ {
register uint32_t a, b, c, d, i; register uint32_t a, b, c, d, i;
uint32_t in[16]; uint32_t in[16];

View File

@@ -21,7 +21,8 @@ struct MD5Context {
}; };
void MD5Init(struct MD5Context *context); void MD5Init(struct MD5Context *context);
void MD5Update(struct MD5Context *context, unsigned char const *buf, void MD5Update(struct MD5Context *context,
unsigned char const *buf,
uint32_t len); uint32_t len);
void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Final(unsigned char digest[16], struct MD5Context *context);

View File

@@ -1,85 +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 program is dual licensed under the MIT and GPLv3 licenses.
*/
#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,79 +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 program is dual licensed under the MIT and GPLv3 licenses.
*/
#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

@@ -27,7 +27,6 @@ typedef void (*parser_end_callback)(char *name, void * const userdata);
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza, typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
void *const userdata); void *const userdata);
parser_t *parser_new(xmpp_ctx_t *ctx, parser_t *parser_new(xmpp_ctx_t *ctx,
parser_start_callback startcb, parser_start_callback startcb,
parser_end_callback endcb, parser_end_callback endcb,

View File

@@ -19,12 +19,15 @@
#include <expat.h> #include <expat.h>
#include <strophe.h> #include "strophe.h"
#include "common.h" #include "common.h"
#include "parser.h" #include "parser.h"
/* Use the Unit Separator to delimit namespace and name in our XML*/ /* Allocate inner text by this number bytes more. Expat splits string
#define NAMESPACE_SEP ('\x1F') * "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 { struct _parser_t {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
@@ -35,6 +38,51 @@ struct _parser_t {
void *userdata; void *userdata;
int depth; int depth;
xmpp_stanza_t *stanza; 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 xmpp_alloc(mem_ctx, size);
else
return NULL;
}
static void *parser_mem_realloc(void *ptr, size_t size)
{
if (mem_ctx != NULL)
return xmpp_realloc(mem_ctx, ptr, size);
else
return NULL;
}
static void parser_mem_free(void *ptr)
{
if (mem_ctx != NULL)
xmpp_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 /* return allocated string with the name from a delimited
@@ -45,8 +93,9 @@ static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
const char *c; const char *c;
size_t len; size_t len;
c = strchr(nsname, NAMESPACE_SEP); c = strchr(nsname, namespace_sep);
if (c == NULL) return xmpp_strdup(ctx, nsname); if (c == NULL)
return xmpp_strdup(ctx, nsname);
c++; c++;
len = strlen(c); len = strlen(c);
@@ -65,7 +114,7 @@ static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
char *result = NULL; char *result = NULL;
const char *c; const char *c;
c = strchr(nsname, NAMESPACE_SEP); c = strchr(nsname, namespace_sep);
if (c != NULL) { if (c != NULL) {
result = xmpp_alloc(ctx, (c - nsname) + 1); result = xmpp_alloc(ctx, (c - nsname) + 1);
if (result != NULL) { if (result != NULL) {
@@ -82,7 +131,8 @@ static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
char *attr; char *attr;
int i; int i;
if (!attrs) return; if (!attrs)
return;
for (i = 0; attrs[i]; i += 2) { for (i = 0; attrs[i]; i += 2) {
/* namespaced attributes aren't used in xmpp, discard namespace */ /* namespaced attributes aren't used in xmpp, discard namespace */
@@ -92,9 +142,28 @@ static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
} }
} }
static void _start_element(void *userdata, static void complete_inner_text(parser_t *parser)
const XML_Char *nsname, {
const XML_Char **attrs) 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(parser->stanza, stanza);
xmpp_stanza_release(stanza);
}
xmpp_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; parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *child; xmpp_stanza_t *child;
@@ -106,8 +175,7 @@ static void _start_element(void *userdata,
if (parser->depth == 0) { if (parser->depth == 0) {
/* notify the owner */ /* notify the owner */
if (parser->startcb) if (parser->startcb)
parser->startcb((char *)name, (char **)attrs, parser->startcb(name, (char **)attrs, parser->userdata);
parser->userdata);
} else { } else {
/* build stanzas at depth 1 */ /* build stanzas at depth 1 */
if (!parser->stanza && parser->depth != 1) { if (!parser->stanza && parser->depth != 1) {
@@ -125,6 +193,7 @@ static void _start_element(void *userdata,
xmpp_stanza_set_ns(child, ns); xmpp_stanza_set_ns(child, ns);
if (parser->stanza != NULL) { if (parser->stanza != NULL) {
complete_inner_text(parser);
xmpp_stanza_add_child(parser->stanza, child); xmpp_stanza_add_child(parser->stanza, child);
xmpp_stanza_release(child); xmpp_stanza_release(child);
} }
@@ -132,8 +201,10 @@ static void _start_element(void *userdata,
} }
} }
if (ns) xmpp_free(parser->ctx, ns); if (ns)
if (name) xmpp_free(parser->ctx, name); xmpp_free(parser->ctx, ns);
if (name)
xmpp_free(parser->ctx, name);
parser->depth++; parser->depth++;
} }
@@ -149,13 +220,13 @@ static void _end_element(void *userdata, const XML_Char *name)
if (parser->endcb) if (parser->endcb)
parser->endcb((char *)name, parser->userdata); parser->endcb((char *)name, parser->userdata);
} else { } else {
complete_inner_text(parser);
if (parser->stanza->parent) { if (parser->stanza->parent) {
/* we're finishing a child stanza, so set current to the parent */ /* we're finishing a child stanza, so set current to the parent */
parser->stanza = parser->stanza->parent; parser->stanza = parser->stanza->parent;
} else { } else {
if (parser->stanzacb) if (parser->stanzacb)
parser->stanzacb(parser->stanza, parser->stanzacb(parser->stanza, parser->userdata);
parser->userdata);
xmpp_stanza_release(parser->stanza); xmpp_stanza_release(parser->stanza);
parser->stanza = NULL; parser->stanza = NULL;
} }
@@ -165,20 +236,30 @@ static void _end_element(void *userdata, const XML_Char *name)
static void _characters(void *userdata, const XML_Char *s, int len) static void _characters(void *userdata, const XML_Char *s, int len)
{ {
parser_t *parser = (parser_t *)userdata; 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 */ /* Join all parts to a single resulting string. Stanza is created in
stanza = xmpp_stanza_new(parser->ctx); * _start_element() and _end_element(). */
if (!stanza) { if (parser->inner_text_used + len >= parser->inner_text_size) {
/* FIXME: allocation error, disconnect */ parser->inner_text_size =
parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
p = xmpp_realloc(parser->ctx, parser->inner_text,
parser->inner_text_size);
if (p == NULL) {
xmpp_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
parser->inner_text_used = 0;
parser->inner_text_size = 0;
return; return;
} }
xmpp_stanza_set_text_with_size(stanza, s, len); parser->inner_text = p;
parser->inner_text[parser->inner_text_used] = '\0';
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, parser_t *parser_new(xmpp_ctx_t *ctx,
@@ -199,6 +280,9 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
parser->userdata = userdata; parser->userdata = userdata;
parser->depth = 0; parser->depth = 0;
parser->stanza = NULL; parser->stanza = NULL;
parser->inner_text = NULL;
parser->inner_text_size = 0;
parser->inner_text_used = 0;
parser_reset(parser); parser_reset(parser);
} }
@@ -217,23 +301,48 @@ void parser_free(parser_t *parser)
if (parser->expat) if (parser->expat)
XML_ParserFree(parser->expat); XML_ParserFree(parser->expat);
if (parser->inner_text) {
xmpp_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}
xmpp_free(parser->ctx, parser); xmpp_free(parser->ctx, parser);
} }
/* shuts down and restarts XML parser. true on success */ /* shuts down and restarts XML parser. true on success */
int parser_reset(parser_t *parser) int parser_reset(parser_t *parser)
{ {
if (parser->expat) XML_Bool ret;
const XML_Memory_Handling_Suite *mem = NULL;
if (parser->expat) {
ret = XML_ParserReset(parser->expat, NULL);
if (ret != XML_TRUE) {
XML_ParserFree(parser->expat); 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);
}
if (parser->stanza) if (parser->stanza) {
xmpp_stanza_release(parser->stanza); xmpp_stanza_release(parser->stanza);
parser->stanza = NULL;
}
parser->expat = XML_ParserCreateNS(NULL, NAMESPACE_SEP); if (parser->inner_text) {
if (!parser->expat) return 0; xmpp_free(parser->ctx, parser->inner_text);
parser->inner_text = NULL;
}
if (!parser->expat)
return 0;
parser->depth = 0; parser->depth = 0;
parser->stanza = NULL;
XML_SetUserData(parser->expat, parser); XML_SetUserData(parser->expat, parser);
XML_SetElementHandler(parser->expat, _start_element, _end_element); XML_SetElementHandler(parser->expat, _start_element, _end_element);

View File

@@ -20,7 +20,7 @@
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include <strophe.h> #include "strophe.h"
#include "common.h" #include "common.h"
#include "parser.h" #include "parser.h"
@@ -36,13 +36,14 @@ struct _parser_t {
xmpp_stanza_t *stanza; xmpp_stanza_t *stanza;
}; };
static void _set_attributes(xmpp_stanza_t *stanza, int nattrs, static void
const xmlChar **attrs) _set_attributes(xmpp_stanza_t *stanza, int nattrs, const xmlChar **attrs)
{ {
int i, len; int i, len;
char *value; char *value;
if (!attrs) return; if (!attrs)
return;
/* SAX2 uses array of localname/prefix/uri/value_begin/value_end */ /* SAX2 uses array of localname/prefix/uri/value_begin/value_end */
for (i = 0; i < nattrs * 5; i += 5) { for (i = 0; i < nattrs * 5; i += 5) {
@@ -59,17 +60,19 @@ static void _set_attributes(xmpp_stanza_t *stanza, int nattrs,
/* SAX2 gives us the attrs in an incredibly inconvenient array, /* SAX2 gives us the attrs in an incredibly inconvenient array,
* convert it to what the start callback is expecting */ * convert it to what the start callback is expecting */
static char **_convert_attrs(parser_t *parser, int nattrs, static char **
const xmlChar **attrs) _convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
{ {
int c, i, o, len; int c, i, o, len;
char *value; char *value;
char **ret; char **ret;
if (!attrs) return NULL; if (!attrs)
return NULL;
ret = xmpp_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *)); ret = xmpp_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
if (!ret) return NULL; if (!ret)
return NULL;
memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *)); memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *));
for (c = 0; c < nattrs; c++) { for (c = 0; c < nattrs; c++) {
@@ -97,30 +100,41 @@ static void _free_cbattrs(parser_t *parser, char **attrs)
return; return;
for (i = 0; attrs[i]; i += 2) { for (i = 0; attrs[i]; i += 2) {
if (attrs[i]) xmpp_free(parser->ctx, attrs[i]); if (attrs[i])
if (attrs[i+1]) xmpp_free(parser->ctx, attrs[i+1]); xmpp_free(parser->ctx, attrs[i]);
if (attrs[i + 1])
xmpp_free(parser->ctx, attrs[i + 1]);
} }
xmpp_free(parser->ctx, attrs); xmpp_free(parser->ctx, attrs);
} }
static void _start_element(void *userdata, static void _start_element(void *userdata,
const xmlChar *name, const xmlChar *prefix, const xmlChar *name,
const xmlChar *uri, int nnamespaces, const xmlChar *prefix,
const xmlChar **namespaces, int nattrs, const xmlChar *uri,
int ndefaulted, const xmlChar **attrs) int nnamespaces,
const xmlChar **namespaces,
int nattrs,
int ndefaulted,
const xmlChar **attrs)
{ {
parser_t *parser = (parser_t *)userdata; parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *child; xmpp_stanza_t *child;
char **cbattrs; char **cbattrs;
UNUSED(prefix);
UNUSED(nnamespaces);
UNUSED(namespaces);
UNUSED(ndefaulted);
if (parser->depth == 0) { if (parser->depth == 0) {
/* notify the owner */ /* notify the owner */
if (parser->startcb) if (parser->startcb) {
cbattrs = _convert_attrs(parser, nattrs, attrs); cbattrs = _convert_attrs(parser, nattrs, attrs);
parser->startcb((char *)name, cbattrs, parser->startcb((char *)name, cbattrs, parser->userdata);
parser->userdata);
_free_cbattrs(parser, cbattrs); _free_cbattrs(parser, cbattrs);
}
} else { } else {
/* build stanzas at depth 1 */ /* build stanzas at depth 1 */
if (!parser->stanza && parser->depth != 1) { if (!parser->stanza && parser->depth != 1) {
@@ -162,11 +176,16 @@ static void _start_element(void *userdata,
parser->depth++; parser->depth++;
} }
static void _end_element(void *userdata, const xmlChar *name, static void _end_element(void *userdata,
const xmlChar *prefix, const xmlChar *uri) const xmlChar *name,
const xmlChar *prefix,
const xmlChar *uri)
{ {
parser_t *parser = (parser_t *)userdata; parser_t *parser = (parser_t *)userdata;
UNUSED(prefix);
UNUSED(uri);
parser->depth--; parser->depth--;
if (parser->depth == 0) { if (parser->depth == 0) {
@@ -179,8 +198,7 @@ static void _end_element(void *userdata, const xmlChar *name,
parser->stanza = parser->stanza->parent; parser->stanza = parser->stanza->parent;
} else { } else {
if (parser->stanzacb) if (parser->stanzacb)
parser->stanzacb(parser->stanza, parser->stanzacb(parser->stanza, parser->userdata);
parser->userdata);
xmpp_stanza_release(parser->stanza); xmpp_stanza_release(parser->stanza);
parser->stanza = NULL; parser->stanza = NULL;
} }
@@ -193,7 +211,8 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
xmpp_stanza_t *stanza; xmpp_stanza_t *stanza;
/* skip unimportant whitespace, etc */ /* skip unimportant whitespace, etc */
if (parser->depth < 2) return; if (parser->depth < 2)
return;
/* create and populate stanza */ /* create and populate stanza */
stanza = xmpp_stanza_new(parser->ctx); stanza = xmpp_stanza_new(parser->ctx);
@@ -248,6 +267,8 @@ void parser_free(parser_t *parser)
{ {
if (parser->xmlctx) if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx); xmlFreeParserCtxt(parser->xmlctx);
if (parser->stanza)
xmpp_stanza_release(parser->stanza);
xmpp_free(parser->ctx, parser); xmpp_free(parser->ctx, parser);
} }
@@ -256,18 +277,16 @@ int parser_reset(parser_t *parser)
{ {
if (parser->xmlctx) if (parser->xmlctx)
xmlFreeParserCtxt(parser->xmlctx); xmlFreeParserCtxt(parser->xmlctx);
if (parser->stanza) if (parser->stanza)
xmpp_stanza_release(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->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 */ /* feed a chunk of data to the parser */
@@ -275,9 +294,5 @@ 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 */ the status returned by parser_feed */
if(!xmlParseChunk(parser->xmlctx, chunk, len, 0)) { return !xmlParseChunk(parser->xmlctx, chunk, len, 0);
return 1;
} else {
return 0;
}
} }

View File

@@ -62,8 +62,8 @@ struct _xmpp_rand_t {
/* adds two arrays as numbers in big-endian representation and stores /* adds two arrays as numbers in big-endian representation and stores
* result in the first one. * result in the first one.
*/ */
static void arr_add(uint8_t *arr1, size_t arr1_len, static void
uint8_t *arr2, size_t arr2_len) arr_add(uint8_t *arr1, size_t arr1_len, uint8_t *arr2, size_t arr2_len)
{ {
size_t i; size_t i;
uint32_t acc; uint32_t acc;
@@ -89,8 +89,10 @@ static void store_be32(uint32_t val, uint8_t be[4])
be[3] = (uint8_t)(val & 0xff); be[3] = (uint8_t)(val & 0xff);
} }
static void Hash_df(uint8_t *input_string, size_t input_string_len, static void Hash_df(uint8_t *input_string,
uint8_t *output_string, size_t no_of_bytes_to_return) size_t input_string_len,
uint8_t *output_string,
size_t no_of_bytes_to_return)
{ {
uint8_t counter; uint8_t counter;
uint8_t temp[round_up(seedlen, outlen)]; uint8_t temp[round_up(seedlen, outlen)];
@@ -119,7 +121,8 @@ static void Hash_df(uint8_t *input_string, size_t input_string_len,
static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx, static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
uint8_t *entropy_input, uint8_t *entropy_input,
size_t entropy_input_len, size_t entropy_input_len,
uint8_t *nonce, size_t nonce_len) uint8_t *nonce,
size_t nonce_len)
{ {
uint8_t seed_material[ENTROPY_MAX + NONCE_MAX]; uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
uint8_t seed0[seedlen + 1]; uint8_t seed0[seedlen + 1];
@@ -127,8 +130,10 @@ static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
assert(entropy_input_len <= ENTROPY_MAX); assert(entropy_input_len <= ENTROPY_MAX);
assert(nonce_len <= NONCE_MAX); assert(nonce_len <= NONCE_MAX);
assert(nonce != NULL || nonce_len == 0);
memcpy(seed_material, entropy_input, entropy_input_len); memcpy(seed_material, entropy_input, entropy_input_len);
if (nonce != NULL)
memcpy(seed_material + entropy_input_len, nonce, nonce_len); memcpy(seed_material + entropy_input_len, nonce, nonce_len);
Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen); Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
seed0[0] = 0; seed0[0] = 0;
@@ -160,8 +165,8 @@ static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
ctx->reseed_counter = 1; ctx->reseed_counter = 1;
} }
static void Hashgen(uint8_t *V, uint8_t *output, static void
size_t requested_number_of_bytes) Hashgen(uint8_t *V, uint8_t *output, size_t requested_number_of_bytes)
{ {
uint8_t data[seedlen]; uint8_t data[seedlen];
uint8_t W[GENERATE_MAX]; uint8_t W[GENERATE_MAX];
@@ -185,7 +190,8 @@ static void Hashgen(uint8_t *V, uint8_t *output,
} }
/* assume additional_input is zero length string */ /* assume additional_input is zero length string */
static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx, uint8_t *output, static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx,
uint8_t *output,
size_t requested_number_of_bytes) size_t requested_number_of_bytes)
{ {
uint8_t H[outlen]; uint8_t H[outlen];
@@ -287,25 +293,32 @@ int xmpp_rand(xmpp_rand_t *rand)
return 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) void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len)
{ {
size_t i; size_t i;
size_t rand_len = len / 2; const size_t rand_len = len / 2;
#ifndef _MSC_VER
unsigned char rand_buf[rand_len];
#else
unsigned char *rand_buf = (unsigned char *)_alloca(rand_len);
#endif
/* current implementation returns printable HEX representation of /*
* a random buffer, however base64 encoding can be used instead; * We don't want to use any allocation here, because this function
* the only problem is that base64_encode() allocates memory and * can't fail. Also we want to avoid VLA.
* as result can fail. * Current implementation uses half of the output buffer for random buffer
* generation and then converts it to HEX representation.
*/ */
xmpp_rand_bytes(rand, rand_buf, rand_len); if (rand_len > 0) {
for (i = 0; i < rand_len; ++i) { xmpp_rand_bytes(rand, (unsigned char *)output, rand_len);
xmpp_snprintf(output + i * 2, len, "%02x", rand_buf[i]); for (i = rand_len; i > 0; --i)
len -= 2; rand_byte2hex(output[i - 1], &output[(i - 1) * 2]);
} }
if (len > 0)
output[len - 1] = '\0';
} }

View File

@@ -36,7 +36,7 @@ xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
*/ */
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand); void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
/** Generate random integer /** Generate random integer.
* Analogue of rand(3). * Analogue of rand(3).
* *
* @ingroup Random * @ingroup Random

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,9 @@ typedef struct resolver_srv_rr_struc {
struct resolver_srv_rr_struc *next; struct resolver_srv_rr_struc *next;
} resolver_srv_rr_t; } resolver_srv_rr_t;
void resolver_initialize(void);
void resolver_shutdown(void);
/** Perform lookup for RFC1035 message format. /** Perform lookup for RFC1035 message format.
* This function allocates all elements. * This function allocates all elements.
* *
@@ -43,8 +46,10 @@ typedef struct resolver_srv_rr_struc {
* *
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail * @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, int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
size_t len, resolver_srv_rr_t **srv_rr_list); const unsigned char *buf,
size_t len,
resolver_srv_rr_t **srv_rr_list);
/** Resolve SRV record. /** Resolve SRV record.
* *
* @param ctx a Strophe context object * @param ctx a Strophe context object
@@ -55,8 +60,11 @@ int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf,
* *
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail * @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, int resolver_srv_lookup(xmpp_ctx_t *ctx,
const char *domain, resolver_srv_rr_t **srv_rr_list); 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 /** Release a list returned by resolver_srv_lookup() or
* resolver_srv_lookup_buf(). * resolver_srv_lookup_buf().
@@ -65,4 +73,5 @@ int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
* @param srv_rr_list a list allocated by lookup functions * @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); void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list);
#endif /* __LIBSTROPHE_RESOLVER_H__ */ #endif /* __LIBSTROPHE_RESOLVER_H__ */

View File

@@ -21,7 +21,6 @@
#include "ostypes.h" #include "ostypes.h"
#include "sasl.h" #include "sasl.h"
#include "md5.h" #include "md5.h"
#include "sha1.h"
#include "scram.h" #include "scram.h"
#include "rand.h" #include "rand.h"
#include "util.h" #include "util.h"
@@ -36,9 +35,9 @@
#endif #endif
#endif /* _MSC_VER */ #endif /* _MSC_VER */
/** generate authentication string for the SASL PLAIN mechanism */ /** generate authentication string for the SASL PLAIN mechanism */
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) { char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
{
size_t idlen, passlen; size_t idlen, passlen;
size_t msglen; size_t msglen;
char *result = NULL; char *result = NULL;
@@ -113,13 +112,17 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
s = text; s = text;
while (*s != '\0') { while (*s != '\0') {
/* skip any leading commas and spaces */ /* skip any leading commas and spaces */
while ((*s == ',') || (*s == ' ')) s++; while ((*s == ',') || (*s == ' '))
s++;
/* accumulate a key ending at '=' */ /* accumulate a key ending at '=' */
t = s; t = s;
while ((*t != '=') && (*t != '\0')) t++; while ((*t != '=') && (*t != '\0'))
if (*t == '\0') break; /* bad string */ t++;
if (*t == '\0')
break; /* bad string */
key = _make_string(ctx, (char *)s, (t - s)); key = _make_string(ctx, (char *)s, (t - s));
if (key == NULL) break; if (key == NULL)
break;
/* advance our start pointer past the key */ /* advance our start pointer past the key */
s = t + 1; s = t + 1;
t = s; t = s;
@@ -136,7 +139,8 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
} }
/* otherwise, accumulate a value ending in ',' or '\0' */ /* otherwise, accumulate a value ending in ',' or '\0' */
} else { } else {
while ((*t != ',') && (*t != '\0')) t++; while ((*t != ',') && (*t != '\0'))
t++;
value = _make_string(ctx, (char *)s, (t - s)); value = _make_string(ctx, (char *)s, (t - s));
s = t; s = t;
} }
@@ -168,8 +172,8 @@ static void _digest_to_hex(const char *digest, char *hex)
} }
/** append 'key="value"' to a buffer, growing as necessary */ /** append 'key="value"' to a buffer, growing as necessary */
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, static char *
char *buf, int *len, int quote) _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; int keylen, valuelen;
@@ -181,7 +185,8 @@ static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
buf = xmpp_alloc(ctx, 1); buf = xmpp_alloc(ctx, 1);
buf[0] = '\0'; buf[0] = '\0';
} }
if (buf == NULL) return NULL; if (buf == NULL)
return NULL;
/* get current string length */ /* get current string length */
olen = strlen(buf); olen = strlen(buf);
@@ -204,27 +209,33 @@ static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
if (buf != NULL) { if (buf != NULL) {
c = buf + olen; c = buf + olen;
if (olen) *c++ = ','; if (olen)
memcpy(c, key, keylen); c += keylen; *c++ = ',';
memcpy(c, key, keylen);
c += keylen;
*c++ = '='; *c++ = '=';
memcpy(c, qvalue, valuelen); c += valuelen; memcpy(c, qvalue, valuelen);
c += valuelen;
*c++ = '\0'; *c++ = '\0';
} }
if (quote) xmpp_free(ctx, (char *)qvalue); if (quote)
xmpp_free(ctx, (char *)qvalue);
return buf; return buf;
} }
/** generate auth response string for the SASL DIGEST-MD5 mechanism */ /** generate auth response string for the SASL DIGEST-MD5 mechanism */
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, char *sasl_digest_md5(xmpp_ctx_t *ctx,
const char *jid, const char *password) { const char *challenge,
const char *jid,
const char *password)
{
hash_t *table; hash_t *table;
char *result = NULL; char *result = NULL;
char *node, *domain, *realm; char *node, *domain, *realm;
char *value; char *value;
char *response; char *response;
int rlen;
struct MD5Context MD5; struct MD5Context MD5;
unsigned char digest[16], HA1[16], HA2[16]; unsigned char digest[16], HA1[16], HA2[16];
char hex[32]; char hex[32];
@@ -264,6 +275,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce)); xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce)); hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce));
hash_add(table, "nc", xmpp_strdup(ctx, "00000001")); hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
if (hash_get(table, "qop") == NULL)
hash_add(table, "qop", xmpp_strdup(ctx, "auth")); hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1); value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
memcpy(value, "xmpp/", 5); memcpy(value, "xmpp/", 5);
@@ -339,16 +351,15 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
/* construct reply */ /* construct reply */
result = NULL; result = NULL;
rlen = 0; result = _add_key(ctx, table, "username", result, 1);
result = _add_key(ctx, table, "username", result, &rlen, 1); result = _add_key(ctx, table, "realm", result, 1);
result = _add_key(ctx, table, "realm", result, &rlen, 1); result = _add_key(ctx, table, "nonce", result, 1);
result = _add_key(ctx, table, "nonce", result, &rlen, 1); result = _add_key(ctx, table, "cnonce", result, 1);
result = _add_key(ctx, table, "cnonce", result, &rlen, 1); result = _add_key(ctx, table, "nc", result, 0);
result = _add_key(ctx, table, "nc", result, &rlen, 0); result = _add_key(ctx, table, "qop", result, 0);
result = _add_key(ctx, table, "qop", result, &rlen, 0); result = _add_key(ctx, table, "digest-uri", result, 1);
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1); result = _add_key(ctx, table, "response", result, 0);
result = _add_key(ctx, table, "response", result, &rlen, 0); result = _add_key(ctx, table, "charset", result, 0);
result = _add_key(ctx, table, "charset", result, &rlen, 0);
xmpp_free(ctx, node); xmpp_free(ctx, node);
xmpp_free(ctx, domain); xmpp_free(ctx, domain);
@@ -361,13 +372,16 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
return response; return response;
} }
/** generate auth response string for the SASL SCRAM-SHA-1 mechanism */ /** generate auth response string for the SASL SCRAM mechanism */
char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge, char *sasl_scram(xmpp_ctx_t *ctx,
const char *first_bare, const char *jid, const struct hash_alg *alg,
const char *challenge,
const char *first_bare,
const char *jid,
const char *password) const char *password)
{ {
uint8_t key[SHA1_DIGEST_SIZE]; uint8_t key[SCRAM_DIGEST_SIZE];
uint8_t sign[SHA1_DIGEST_SIZE]; uint8_t sign[SCRAM_DIGEST_SIZE];
char *r = NULL; char *r = NULL;
char *s = NULL; char *s = NULL;
char *i = NULL; char *i = NULL;
@@ -384,7 +398,8 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
char *result = NULL; char *result = NULL;
size_t response_len; size_t response_len;
size_t auth_len; size_t auth_len;
int j;
UNUSED(jid);
tmp = xmpp_strdup(ctx, challenge); tmp = xmpp_strdup(ctx, challenge);
if (!tmp) { if (!tmp) {
@@ -419,7 +434,8 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
goto out_sval; goto out_sval;
} }
response_len = 39 + strlen(r); /* "c=biws," + r + ",p=" + sign_b64 + '\0' */
response_len = 7 + strlen(r) + 3 + ((alg->digest_size + 2) / 3 * 4) + 1;
response = xmpp_alloc(ctx, response_len); response = xmpp_alloc(ctx, response_len);
if (!response) { if (!response) {
goto out_auth; goto out_auth;
@@ -429,18 +445,17 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge, xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge,
response); response);
SCRAM_SHA1_ClientKey((uint8_t *)password, strlen(password), SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval,
(uint8_t *)sval, sval_len, (uint32_t)ival, key); sval_len, (uint32_t)ival, key);
SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign);
for (j = 0; j < SHA1_DIGEST_SIZE; j++) { SCRAM_ClientProof(alg, sign, key, sign);
sign[j] ^= key[j];
}
sign_b64 = xmpp_base64_encode(ctx, sign, sizeof(sign)); sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size);
if (!sign_b64) { if (!sign_b64) {
goto out_response; goto out_response;
} }
/* Check for buffer overflow */
if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) { if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
xmpp_free(ctx, sign_b64); xmpp_free(ctx, sign_b64);
goto out_response; goto out_response;
@@ -449,8 +464,8 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
strcat(response, sign_b64); strcat(response, sign_b64);
xmpp_free(ctx, sign_b64); xmpp_free(ctx, sign_b64);
response_b64 = xmpp_base64_encode(ctx, (unsigned char *)response, response_b64 =
strlen(response)); xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response));
if (!response_b64) { if (!response_b64) {
goto out_response; goto out_response;
} }

View File

@@ -17,14 +17,20 @@
#define __LIBSTROPHE_SASL_H__ #define __LIBSTROPHE_SASL_H__
#include "strophe.h" #include "strophe.h"
#include "scram.h"
/** low-level sasl routines */ /** low-level sasl routines */
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password); char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, char *sasl_digest_md5(xmpp_ctx_t *ctx,
const char *jid, const char *password); const char *challenge,
char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge, const char *jid,
const char *first_bare, const char *jid, const char *password);
char *sasl_scram(xmpp_ctx_t *ctx,
const struct hash_alg *alg,
const char *challenge,
const char *first_bare,
const char *jid,
const char *password); const char *password);
#endif /* _LIBXMPP_SASL_H__ */ #endif /* _LIBXMPP_SASL_H__ */

View File

@@ -19,56 +19,103 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include "common.h"
#include "sha1.h" #include "sha1.h"
#include "sha256.h"
#include "sha512.h"
#include "ostypes.h" #include "ostypes.h"
#include "scram.h" #include "scram.h"
#define HMAC_BLOCK_SIZE 64 #define HMAC_BLOCK_SIZE_MAX 128
static const uint8_t ipad = 0x36; static const uint8_t ipad = 0x36;
static const uint8_t opad = 0x5C; static const uint8_t opad = 0x5C;
static void crypto_HMAC_SHA1(const uint8_t *key, size_t key_len, const struct hash_alg scram_sha1 = {
const uint8_t *text, size_t len, "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_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_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};
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 *digest)
{ {
uint8_t key_pad[HMAC_BLOCK_SIZE]; uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
uint8_t key_ipad[HMAC_BLOCK_SIZE]; uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
uint8_t key_opad[HMAC_BLOCK_SIZE]; uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
uint8_t sha_digest[SHA1_DIGEST_SIZE]; uint8_t sha_digest[SCRAM_DIGEST_SIZE];
int i; size_t blocksize;
SHA1_CTX ctx; size_t i;
union common_hash_ctx ctx;
memset(key_pad, 0, sizeof(key_pad)); assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
if (key_len <= HMAC_BLOCK_SIZE) { blocksize = alg->digest_size < 48 ? 64 : 128;
memset(key_pad, 0, blocksize);
if (key_len <= blocksize) {
memcpy(key_pad, key, key_len); memcpy(key_pad, key, key_len);
} else { } else {
/* according to RFC2104 */ /* according to RFC2104 */
crypto_SHA1(key, key_len, key_pad); alg->hash(key, key_len, key_pad);
} }
for (i = 0; i < HMAC_BLOCK_SIZE; i++) { for (i = 0; i < blocksize; i++) {
key_ipad[i] = key_pad[i] ^ ipad; key_ipad[i] = key_pad[i] ^ ipad;
key_opad[i] = key_pad[i] ^ opad; key_opad[i] = key_pad[i] ^ opad;
} }
crypto_SHA1_Init(&ctx); alg->init((void *)&ctx);
crypto_SHA1_Update(&ctx, key_ipad, HMAC_BLOCK_SIZE); alg->update((void *)&ctx, key_ipad, blocksize);
crypto_SHA1_Update(&ctx, text, len); alg->update((void *)&ctx, text, len);
crypto_SHA1_Final(&ctx, sha_digest); alg->final((void *)&ctx, sha_digest);
crypto_SHA1_Init(&ctx); alg->init((void *)&ctx);
crypto_SHA1_Update(&ctx, key_opad, HMAC_BLOCK_SIZE); alg->update((void *)&ctx, key_opad, blocksize);
crypto_SHA1_Update(&ctx, sha_digest, SHA1_DIGEST_SIZE); alg->update((void *)&ctx, sha_digest, alg->digest_size);
crypto_SHA1_Final(&ctx, digest); alg->final((void *)&ctx, digest);
} }
static void SCRAM_SHA1_Hi(const uint8_t *text, size_t len, static void SCRAM_Hi(const struct hash_alg *alg,
const uint8_t *salt, size_t salt_len, uint32_t i, const uint8_t *text,
size_t len,
const uint8_t *salt,
size_t salt_len,
uint32_t i,
uint8_t *digest) uint8_t *digest)
{ {
int k; size_t k;
uint32_t j; uint32_t j;
uint8_t tmp[128]; uint8_t tmp[128];
@@ -77,7 +124,7 @@ static void SCRAM_SHA1_Hi(const uint8_t *text, size_t len,
/* assume salt + INT(1) isn't longer than sizeof(tmp) */ /* assume salt + INT(1) isn't longer than sizeof(tmp) */
assert(salt_len <= sizeof(tmp) - sizeof(int1)); assert(salt_len <= sizeof(tmp) - sizeof(int1));
memset(digest, 0, SHA1_DIGEST_SIZE); memset(digest, 0, alg->digest_size);
if (i == 0) { if (i == 0) {
return; return;
} }
@@ -86,46 +133,53 @@ static void SCRAM_SHA1_Hi(const uint8_t *text, size_t len,
memcpy(&tmp[salt_len], int1, sizeof(int1)); memcpy(&tmp[salt_len], int1, sizeof(int1));
/* 'text' for Hi is a 'key' for HMAC */ /* 'text' for Hi is a 'key' for HMAC */
crypto_HMAC_SHA1(text, len, tmp, salt_len + sizeof(int1), digest); crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
memcpy(tmp, digest, SHA1_DIGEST_SIZE); memcpy(tmp, digest, alg->digest_size);
for (j = 1; j < i; j++) { for (j = 1; j < i; j++) {
crypto_HMAC_SHA1(text, len, tmp, SHA1_DIGEST_SIZE, tmp); crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
for (k = 0; k < SHA1_DIGEST_SIZE; k++) { for (k = 0; k < alg->digest_size; k++) {
digest[k] ^= tmp[k]; digest[k] ^= tmp[k];
} }
} }
} }
void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len, void SCRAM_ClientKey(const struct hash_alg *alg,
const uint8_t *salt, size_t salt_len, uint32_t i, const uint8_t *password,
size_t len,
const uint8_t *salt,
size_t salt_len,
uint32_t i,
uint8_t *key) uint8_t *key)
{ {
uint8_t salted[SHA1_DIGEST_SIZE]; uint8_t salted[SCRAM_DIGEST_SIZE];
/* XXX: Normalize(password) is omitted */ /* XXX: Normalize(password) is omitted */
SCRAM_SHA1_Hi(password, len, salt, salt_len, i, salted); SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
crypto_HMAC_SHA1(salted, SHA1_DIGEST_SIZE, (uint8_t *)"Client Key", crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
strlen("Client Key"), key); strlen("Client Key"), key);
} }
void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey, void SCRAM_ClientSignature(const struct hash_alg *alg,
const uint8_t *AuthMessage, size_t len, const uint8_t *ClientKey,
const uint8_t *AuthMessage,
size_t len,
uint8_t *sign) uint8_t *sign)
{ {
uint8_t stored[SHA1_DIGEST_SIZE]; uint8_t stored[SCRAM_DIGEST_SIZE];
crypto_SHA1(ClientKey, SHA1_DIGEST_SIZE, stored); alg->hash(ClientKey, alg->digest_size, stored);
crypto_HMAC_SHA1(stored, SHA1_DIGEST_SIZE, AuthMessage, len, sign); crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
} }
void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey, void SCRAM_ClientProof(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *ClientSignature, const uint8_t *ClientSignature,
uint8_t *proof) uint8_t *proof)
{ {
int i; size_t i;
for (i = 0; i < SHA1_DIGEST_SIZE; i++) { for (i = 0; i < alg->digest_size; i++) {
proof[i] = ClientKey[i] ^ ClientSignature[i]; proof[i] = ClientKey[i] ^ ClientSignature[i];
} }
} }

View File

@@ -1,5 +1,5 @@
/* scram.h /* scram.h
* strophe XMPP client library -- SCRAM-SHA1 helper functions * strophe XMPP client library -- SCRAM helper functions
* *
* Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com> * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
* *
@@ -10,7 +10,7 @@
*/ */
/** @file /** @file
* SCRAM-SHA1 helper functions. * SCRAM helper functions.
*/ */
#ifndef __LIBSTROPHE_SCRAM_H__ #ifndef __LIBSTROPHE_SCRAM_H__
@@ -19,17 +19,40 @@
/* make sure the stdint.h types are available */ /* make sure the stdint.h types are available */
#include "ostypes.h" #include "ostypes.h"
#include "sha1.h" /* Maximum possible digest size. Used for buffers allocation. */
#include "sha512.h"
#define SCRAM_DIGEST_SIZE SHA512_DIGEST_SIZE
void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len, struct hash_alg {
const uint8_t *salt, size_t salt_len, uint32_t i, 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_sha1;
extern const struct hash_alg scram_sha256;
extern const struct hash_alg scram_sha512;
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 *key);
void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey, void SCRAM_ClientSignature(const struct hash_alg *alg,
const uint8_t *AuthMessage, size_t len, const uint8_t *ClientKey,
const uint8_t *AuthMessage,
size_t len,
uint8_t *sign); uint8_t *sign);
void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey, void SCRAM_ClientProof(const struct hash_alg *alg,
const uint8_t *ClientKey,
const uint8_t *ClientSignature, const uint8_t *ClientSignature,
uint8_t *proof); uint8_t *proof);

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

@@ -75,9 +75,6 @@ move public api to sha1.h
#include "ostypes.h" #include "ostypes.h"
#include "sha1.h" #include "sha1.h"
/* Don't change user's data */
#define SHA1HANDSOFF
static uint32_t host_to_be(uint32_t i); static uint32_t host_to_be(uint32_t i);
static 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]);
@@ -86,16 +83,27 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
/* blk0() and blk() perform the initial expand. */ /* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */ /* I got the idea of expanding during the round function from SSLeay */
#define blk0(i) (block->l[i] = host_to_be(block->l[i])) #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] \ #define blk(i) \
^block->l[(i+2)&15]^block->l[i&15],1)) (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 */ /* (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 R0(v, w, x, y, z, i) \
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 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 R1(v, w, x, y, z, i) \
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 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);
static uint32_t host_to_be(uint32_t i) static uint32_t host_to_be(uint32_t i)
{ {
@@ -118,6 +126,13 @@ static uint32_t host_to_be(uint32_t i)
#undef le_to_be #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);
}
/* Hash a single 512-bit block. This is the core of the algorithm. */ /* Hash a single 512-bit block. This is the core of the algorithm. */
static 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])
{ {
@@ -128,13 +143,9 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
} CHAR64LONG16; } CHAR64LONG16;
CHAR64LONG16 *block; CHAR64LONG16 *block;
#ifdef SHA1HANDSOFF
static uint8_t workspace[64]; static uint8_t workspace[64];
block = (CHAR64LONG16 *)workspace; block = (CHAR64LONG16 *)workspace;
memcpy(block, buffer, 64); memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */ /* Copy context->state[] to working vars */
a = state[0]; a = state[0];
@@ -144,6 +155,7 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
e = state[4]; e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */ /* 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(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(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); 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);
@@ -164,6 +176,7 @@ static 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(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(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); 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[] */ /* Add the working vars back into context.state[] */
state[0] += a; state[0] += a;
@@ -173,10 +186,13 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
state[4] += e; state[4] += e;
/* Wipe variables */ /* 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 */ /* SHA1Init - Initialize new context */
void crypto_SHA1_Init(SHA1_CTX *context) void crypto_SHA1_Init(SHA1_CTX *context)
{ {
@@ -189,16 +205,17 @@ void crypto_SHA1_Init(SHA1_CTX* context)
context->count[0] = context->count[1] = 0; context->count[0] = context->count[1] = 0;
} }
/* Run your data through this. */ /* Run your data through this. */
void crypto_SHA1_Update(SHA1_CTX* context, const uint8_t* data, void crypto_SHA1_Update(SHA1_CTX *context,
const uint8_t *data,
const size_t len) const size_t len)
{ {
size_t i, j; size_t i, j;
j = (context->count[0] >> 3) & 63; j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; if ((context->count[0] += (uint32_t)len << 3) < ((uint32_t)len << 3))
context->count[1] += (len >> 29); context->count[1]++;
context->count[1] += (uint32_t)(len >> 29);
if ((j + len) > 63) { 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); SHA1_Transform(context->state, context->buffer);
@@ -206,12 +223,11 @@ void crypto_SHA1_Update(SHA1_CTX* context, const uint8_t* data,
SHA1_Transform(context->state, data + i); SHA1_Transform(context->state, data + i);
} }
j = 0; j = 0;
} } else
else i = 0; i = 0;
memcpy(&context->buffer[j], &data[i], len - i); memcpy(&context->buffer[j], &data[i], len - i);
} }
/* Add padding and return the message digest. */ /* Add padding and return the message digest. */
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest) void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest)
{ {
@@ -219,32 +235,30 @@ void crypto_SHA1_Final(SHA1_CTX* context, uint8_t* digest)
uint8_t finalcount[8]; uint8_t finalcount[8];
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >>
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ ((3 - (i & 3)) * 8)) &
255); /* Endian independent */
} }
crypto_SHA1_Update(context, (uint8_t *)"\200", 1); crypto_SHA1_Update(context, (uint8_t *)"\200", 1);
while ((context->count[0] & 504) != 448) { while ((context->count[0] & 504) != 448) {
crypto_SHA1_Update(context, (uint8_t *)"\0", 1); crypto_SHA1_Update(context, (uint8_t *)"\0", 1);
} }
crypto_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++) { for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
digest[i] = (uint8_t) digest[i] =
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
} }
/* Wipe variables */ /* Wipe variables */
i = 0; SHA1_cleanse(context->buffer, 64);
memset(context->buffer, 0, 64); SHA1_cleanse(context->state, 20);
memset(context->state, 0, 20); SHA1_cleanse(context->count, 8);
memset(context->count, 0, 8); SHA1_cleanse(finalcount, 8); /* SWR */
memset(finalcount, 0, 8); /* SWR */
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
SHA1_Transform(context->state, context->buffer); SHA1_Transform(context->state, context->buffer);
#endif
} }
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest) void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest)
{ {
SHA1_CTX ctx; SHA1_CTX ctx;

View File

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

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

View File

@@ -85,14 +85,29 @@
#define LDOUBLE double #define LDOUBLE double
#endif #endif
static int dopr (char *buffer, size_t maxlen, const char *format, static int dopr(char *buffer, size_t maxlen, const char *format, va_list args);
va_list args); static int fmtstr(char *buffer,
static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, size_t *currlen,
char *value, int flags, int min, int max); size_t maxlen,
static int fmtint (char *buffer, size_t *currlen, size_t maxlen, char *value,
long value, int base, int min, int max, int flags); int flags,
static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, int min,
LDOUBLE fvalue, int min, int max, int flags); int max);
static int fmtint(char *buffer,
size_t *currlen,
size_t maxlen,
long value,
int base,
int min,
int max,
int flags);
static int fmtfp(char *buffer,
size_t *currlen,
size_t maxlen,
LDOUBLE fvalue,
int min,
int max,
int flags);
static int dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); static int dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
/* /*
@@ -147,13 +162,11 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
ch = *format++; ch = *format++;
total = 0; total = 0;
while (state != DP_S_DONE) while (state != DP_S_DONE) {
{
if (ch == '\0') if (ch == '\0')
state = DP_S_DONE; state = DP_S_DONE;
switch(state) switch (state) {
{
case DP_S_DEFAULT: case DP_S_DEFAULT:
if (ch == '%') if (ch == '%')
state = DP_S_FLAGS; state = DP_S_FLAGS;
@@ -162,8 +175,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
ch = *format++; ch = *format++;
break; break;
case DP_S_FLAGS: case DP_S_FLAGS:
switch (ch) switch (ch) {
{
case '-': case '-':
flags |= DP_F_MINUS; flags |= DP_F_MINUS;
ch = *format++; ch = *format++;
@@ -190,50 +202,39 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
} }
break; break;
case DP_S_MIN: case DP_S_MIN:
if (isdigit(ch)) if (isdigit(ch)) {
{
min = 10 * min + char_to_int(ch); min = 10 * min + char_to_int(ch);
ch = *format++; ch = *format++;
} } else if (ch == '*') {
else if (ch == '*')
{
min = va_arg(args, int); min = va_arg(args, int);
ch = *format++; ch = *format++;
state = DP_S_DOT; state = DP_S_DOT;
} } else
else
state = DP_S_DOT; state = DP_S_DOT;
break; break;
case DP_S_DOT: case DP_S_DOT:
if (ch == '.') if (ch == '.') {
{
state = DP_S_MAX; state = DP_S_MAX;
ch = *format++; ch = *format++;
} } else
else
state = DP_S_MOD; state = DP_S_MOD;
break; break;
case DP_S_MAX: case DP_S_MAX:
if (isdigit(ch)) if (isdigit(ch)) {
{
if (max < 0) if (max < 0)
max = 0; max = 0;
max = 10 * max + char_to_int(ch); max = 10 * max + char_to_int(ch);
ch = *format++; ch = *format++;
} } else if (ch == '*') {
else if (ch == '*')
{
max = va_arg(args, int); max = va_arg(args, int);
ch = *format++; ch = *format++;
state = DP_S_MOD; state = DP_S_MOD;
} } else
else
state = DP_S_MOD; state = DP_S_MOD;
break; break;
case DP_S_MOD: case DP_S_MOD:
/* Currently, we don't support Long Long, bummer */ /* Currently, we don't support Long Long, bummer */
switch (ch) switch (ch) {
{
case 'h': case 'h':
cflags = DP_C_SHORT; cflags = DP_C_SHORT;
ch = *format++; ch = *format++;
@@ -252,8 +253,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
state = DP_S_CONV; state = DP_S_CONV;
break; break;
case DP_S_CONV: case DP_S_CONV:
switch (ch) switch (ch) {
{
case 'd': case 'd':
case 'i': case 'i':
if (cflags == DP_C_SHORT) if (cflags == DP_C_SHORT)
@@ -262,7 +262,8 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
value = va_arg(args, long int); value = va_arg(args, long int);
else else
value = va_arg(args, int); value = va_arg(args, int);
total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); total += fmtint(buffer, &currlen, maxlen, value, 10, min, max,
flags);
break; break;
case 'o': case 'o':
flags |= DP_F_UNSIGNED; flags |= DP_F_UNSIGNED;
@@ -272,7 +273,8 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
value = va_arg(args, unsigned long int); value = va_arg(args, unsigned long int);
else else
value = va_arg(args, unsigned int); value = va_arg(args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); total +=
fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
break; break;
case 'u': case 'u':
flags |= DP_F_UNSIGNED; flags |= DP_F_UNSIGNED;
@@ -282,10 +284,12 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
value = va_arg(args, unsigned long int); value = va_arg(args, unsigned long int);
else else
value = va_arg(args, unsigned int); value = va_arg(args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); total += fmtint(buffer, &currlen, maxlen, value, 10, min, max,
flags);
break; break;
case 'X': case 'X':
flags |= DP_F_UP; flags |= DP_F_UP;
//-fallthrough
case 'x': case 'x':
flags |= DP_F_UNSIGNED; flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT) if (cflags == DP_C_SHORT)
@@ -294,7 +298,8 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
value = va_arg(args, unsigned long int); value = va_arg(args, unsigned long int);
else else
value = va_arg(args, unsigned int); value = va_arg(args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); total += fmtint(buffer, &currlen, maxlen, value, 16, min, max,
flags);
break; break;
case 'f': case 'f':
if (cflags == DP_C_LDOUBLE) if (cflags == DP_C_LDOUBLE)
@@ -302,10 +307,12 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
else else
fvalue = va_arg(args, double); fvalue = va_arg(args, double);
/* um, floating point? */ /* um, floating point? */
total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); total +=
fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
break; break;
case 'E': case 'E':
flags |= DP_F_UP; flags |= DP_F_UP;
//-fallthrough
case 'e': case 'e':
if (cflags == DP_C_LDOUBLE) if (cflags == DP_C_LDOUBLE)
fvalue = va_arg(args, LDOUBLE); fvalue = va_arg(args, LDOUBLE);
@@ -314,6 +321,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
break; break;
case 'G': case 'G':
flags |= DP_F_UP; flags |= DP_F_UP;
//-fallthrough
case 'g': case 'g':
if (cflags == DP_C_LDOUBLE) if (cflags == DP_C_LDOUBLE)
fvalue = va_arg(args, LDOUBLE); fvalue = va_arg(args, LDOUBLE);
@@ -321,32 +329,29 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
fvalue = va_arg(args, double); fvalue = va_arg(args, double);
break; break;
case 'c': case 'c':
total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); total +=
dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
break; break;
case 's': case 's':
strvalue = va_arg(args, char *); strvalue = va_arg(args, char *);
total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); total +=
fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
break; break;
case 'p': case 'p':
strvalue = va_arg(args, void *); strvalue = va_arg(args, void *);
total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, total += fmtint(buffer, &currlen, maxlen, (long)strvalue, 16,
max, flags); min, max, flags);
break; break;
case 'n': case 'n':
if (cflags == DP_C_SHORT) if (cflags == DP_C_SHORT) {
{
short int *num; short int *num;
num = va_arg(args, short int *); num = va_arg(args, short int *);
*num = currlen; *num = currlen;
} } else if (cflags == DP_C_LONG) {
else if (cflags == DP_C_LONG)
{
long int *num; long int *num;
num = va_arg(args, long int *); num = va_arg(args, long int *);
*num = currlen; *num = currlen;
} } else {
else
{
int *num; int *num;
num = va_arg(args, int *); num = va_arg(args, int *);
*num = currlen; *num = currlen;
@@ -375,8 +380,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
break; /* some picky compilers need this */ break; /* some picky compilers need this */
} }
} }
if (buffer != NULL && maxlen > 0) if (buffer != NULL && maxlen > 0) {
{
if (currlen < maxlen - 1) if (currlen < maxlen - 1)
buffer[currlen] = '\0'; buffer[currlen] = '\0';
else else
@@ -385,19 +389,24 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
return total; return total;
} }
static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, static int fmtstr(char *buffer,
char *value, int flags, int min, int max) size_t *currlen,
size_t maxlen,
char *value,
int flags,
int min,
int max)
{ {
int padlen, strln; /* amount to pad */ int padlen, strln; /* amount to pad */
int cnt = 0; int cnt = 0;
int total = 0; int total = 0;
if (value == 0) if (value == 0) {
{
value = "<NULL>"; value = "<NULL>";
} }
for (strln = 0; value[strln]; ++strln); /* strlen */ for (strln = 0; value[strln]; ++strln)
; /* strlen */
if (max >= 0 && max < strln) if (max >= 0 && max < strln)
strln = max; strln = max;
padlen = min - strln; padlen = min - strln;
@@ -406,18 +415,15 @@ static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
if (flags & DP_F_MINUS) if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justify */ padlen = -padlen; /* Left Justify */
while (padlen > 0) while (padlen > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, ' '); total += dopr_outch(buffer, currlen, maxlen, ' ');
--padlen; --padlen;
} }
while (*value && ((max < 0) || (cnt < max))) while (*value && ((max < 0) || (cnt < max))) {
{
total += dopr_outch(buffer, currlen, maxlen, *value++); total += dopr_outch(buffer, currlen, maxlen, *value++);
++cnt; ++cnt;
} }
while (padlen < 0) while (padlen < 0) {
{
total += dopr_outch(buffer, currlen, maxlen, ' '); total += dopr_outch(buffer, currlen, maxlen, ' ');
++padlen; ++padlen;
} }
@@ -426,8 +432,14 @@ static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
static int fmtint (char *buffer, size_t *currlen, size_t maxlen, static int fmtint(char *buffer,
long value, int base, int min, int max, int flags) size_t *currlen,
size_t maxlen,
long value,
int base,
int min,
int max,
int flags)
{ {
int signvalue = 0; int signvalue = 0;
unsigned long uvalue; unsigned long uvalue;
@@ -443,37 +455,35 @@ static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
uvalue = value; uvalue = value;
if(!(flags & DP_F_UNSIGNED)) if (!(flags & DP_F_UNSIGNED)) {
{
if (value < 0) { if (value < 0) {
signvalue = '-'; signvalue = '-';
uvalue = -value; uvalue = -value;
} } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
else
if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+'; signvalue = '+';
else else if (flags & DP_F_SPACE)
if (flags & DP_F_SPACE)
signvalue = ' '; signvalue = ' ';
} }
if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ if (flags & DP_F_UP)
caps = 1; /* Should characters be upper case? */
do { do {
convert[place++] = convert[place++] = (caps ? "0123456789ABCDEF"
(caps? "0123456789ABCDEF":"0123456789abcdef") : "0123456789abcdef")[uvalue % (unsigned)base];
[uvalue % (unsigned)base ];
uvalue = (uvalue / (unsigned)base); uvalue = (uvalue / (unsigned)base);
} while (uvalue && (place < 20)); } while (uvalue && (place < 20));
if (place == 20) place--; if (place == 20)
place--;
convert[place] = 0; convert[place] = 0;
zpadlen = max - place; zpadlen = max - place;
spadlen = min - MAX(max, place) - (signvalue ? 1 : 0); spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
if (zpadlen < 0) zpadlen = 0; if (zpadlen < 0)
if (spadlen < 0) spadlen = 0; zpadlen = 0;
if (flags & DP_F_ZERO) if (spadlen < 0)
{ spadlen = 0;
if (flags & DP_F_ZERO) {
zpadlen = MAX(zpadlen, spadlen); zpadlen = MAX(zpadlen, spadlen);
spadlen = 0; spadlen = 0;
} }
@@ -486,8 +496,7 @@ static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
#endif #endif
/* Spaces */ /* Spaces */
while (spadlen > 0) while (spadlen > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, ' '); total += dopr_outch(buffer, currlen, maxlen, ' ');
--spadlen; --spadlen;
} }
@@ -497,10 +506,8 @@ static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
total += dopr_outch(buffer, currlen, maxlen, signvalue); total += dopr_outch(buffer, currlen, maxlen, signvalue);
/* Zeros */ /* Zeros */
if (zpadlen > 0) if (zpadlen > 0) {
{ while (zpadlen > 0) {
while (zpadlen > 0)
{
total += dopr_outch(buffer, currlen, maxlen, '0'); total += dopr_outch(buffer, currlen, maxlen, '0');
--zpadlen; --zpadlen;
} }
@@ -533,8 +540,7 @@ static LDOUBLE _snp_pow10 (int exp)
{ {
LDOUBLE result = 1; LDOUBLE result = 1;
while (exp) while (exp) {
{
result *= 10; result *= 10;
exp--; exp--;
} }
@@ -554,8 +560,13 @@ static long _snp_round (LDOUBLE value)
return intpart; return intpart;
} }
static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, static int fmtfp(char *buffer,
LDOUBLE fvalue, int min, int max, int flags) size_t *currlen,
size_t maxlen,
LDOUBLE fvalue,
int min,
int max,
int flags)
{ {
int signvalue = 0; int signvalue = 0;
LDOUBLE ufvalue; LDOUBLE ufvalue;
@@ -581,11 +592,9 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
if (fvalue < 0) if (fvalue < 0)
signvalue = '-'; signvalue = '-';
else else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+'; signvalue = '+';
else else if (flags & DP_F_SPACE)
if (flags & DP_F_SPACE)
signvalue = ' '; signvalue = ' ';
#if 0 #if 0
@@ -606,8 +615,7 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
*/ */
fracpart = _snp_round((_snp_pow10(max)) * (ufvalue - intpart)); fracpart = _snp_round((_snp_pow10(max)) * (ufvalue - intpart));
if (fracpart >= _snp_pow10 (max)) if (fracpart >= _snp_pow10(max)) {
{
intpart++; intpart++;
fracpart -= _snp_pow10(max); fracpart -= _snp_pow10(max);
} }
@@ -622,7 +630,8 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
(caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10]; (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
intpart = (intpart / 10); intpart = (intpart / 10);
} while (intpart && (iplace < 20)); } while (intpart && (iplace < 20));
if (iplace == 20) iplace--; if (iplace == 20)
iplace--;
iconvert[iplace] = 0; iconvert[iplace] = 0;
/* Convert fractional part */ /* Convert fractional part */
@@ -631,7 +640,8 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
(caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10]; (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
fracpart = (fracpart / 10); fracpart = (fracpart / 10);
} while (fracpart && (fplace < 20)); } while (fracpart && (fplace < 20));
if (fplace == 20) fplace--; if (fplace == 20)
fplace--;
fconvert[fplace] = 0; fconvert[fplace] = 0;
/* -1 for decimal point, another -1 if we are printing a sign */ /* -1 for decimal point, another -1 if we are printing a sign */
@@ -644,22 +654,18 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
if (flags & DP_F_MINUS) if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justifty */ padlen = -padlen; /* Left Justifty */
if ((flags & DP_F_ZERO) && (padlen > 0)) if ((flags & DP_F_ZERO) && (padlen > 0)) {
{ if (signvalue) {
if (signvalue)
{
total += dopr_outch(buffer, currlen, maxlen, signvalue); total += dopr_outch(buffer, currlen, maxlen, signvalue);
--padlen; --padlen;
signvalue = 0; signvalue = 0;
} }
while (padlen > 0) while (padlen > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, '0'); total += dopr_outch(buffer, currlen, maxlen, '0');
--padlen; --padlen;
} }
} }
while (padlen > 0) while (padlen > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, ' '); total += dopr_outch(buffer, currlen, maxlen, ' ');
--padlen; --padlen;
} }
@@ -673,22 +679,19 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
* Decimal point. This should probably use locale to find the correct * Decimal point. This should probably use locale to find the correct
* char to print out. * char to print out.
*/ */
if (max > 0) if (max > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, '.'); total += dopr_outch(buffer, currlen, maxlen, '.');
while (fplace > 0) while (fplace > 0)
total += dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]); total += dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
} }
while (zpadlen > 0) while (zpadlen > 0) {
{
total += dopr_outch(buffer, currlen, maxlen, '0'); total += dopr_outch(buffer, currlen, maxlen, '0');
--zpadlen; --zpadlen;
} }
while (padlen < 0) while (padlen < 0) {
{
total += dopr_outch(buffer, currlen, maxlen, ' '); total += dopr_outch(buffer, currlen, maxlen, ' ');
++padlen; ++padlen;
} }

View File

@@ -21,8 +21,8 @@
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <Iphlpapi.h> #include <iphlpapi.h>
#include <Mstcpip.h> /* tcp_keepalive */ #include <mstcpip.h> /* tcp_keepalive */
#else #else
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@@ -123,7 +123,8 @@ int sock_set_keepalive(const sock_t sock, int timeout, int interval)
ka.onoff = optval; ka.onoff = optval;
ka.keepalivetime = timeout * 1000; ka.keepalivetime = timeout * 1000;
ka.keepaliveinterval = interval * 1000; ka.keepaliveinterval = interval * 1000;
ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw, NULL, NULL); ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
NULL, NULL);
#else #else
ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
if (ret < 0) if (ret < 0)
@@ -131,15 +132,19 @@ int sock_set_keepalive(const sock_t sock, int timeout, int interval)
if (optval) { if (optval) {
#ifdef TCP_KEEPIDLE #ifdef TCP_KEEPIDLE
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout)); ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
sizeof(timeout));
#elif defined(TCP_KEEPALIVE) #elif defined(TCP_KEEPALIVE)
/* QNX receives `struct timeval' as argument, but it seems OSX does int */ /* QNX receives `struct timeval' as argument, but it seems OSX does int
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout, sizeof(timeout)); */
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
sizeof(timeout));
#endif /* TCP_KEEPIDLE */ #endif /* TCP_KEEPIDLE */
if (ret < 0) if (ret < 0)
return ret; return ret;
#ifdef TCP_KEEPINTVL #ifdef TCP_KEEPINTVL
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
sizeof(interval));
if (ret < 0) if (ret < 0)
return ret; return ret;
#endif /* TCP_KEEPINTVL */ #endif /* TCP_KEEPINTVL */
@@ -158,38 +163,33 @@ int sock_close(const sock_t sock)
#endif #endif
} }
int sock_set_blocking(const sock_t sock) static int _sock_set_blocking_mode(sock_t sock, int blocking)
{ {
#ifdef _WIN32 #ifdef _WIN32
u_long block = 0; u_long nonblock = blocking ? 0 : 1;
return ioctlsocket(sock, FIONBIO, &block);
#else
int rc;
rc = fcntl(sock, F_GETFL, NULL);
if (rc >= 0) {
rc = fcntl(sock, F_SETFL, rc & (~O_NONBLOCK));
}
return rc;
#endif
}
int sock_set_nonblocking(const sock_t sock)
{
#ifdef _WIN32
u_long nonblock = 1;
return ioctlsocket(sock, FIONBIO, &nonblock); return ioctlsocket(sock, FIONBIO, &nonblock);
#else #else
int rc; int rc;
rc = fcntl(sock, F_GETFL, NULL); rc = fcntl(sock, F_GETFL, NULL);
if (rc >= 0) { if (rc >= 0) {
rc = fcntl(sock, F_SETFL, rc | O_NONBLOCK); rc = blocking ? rc & (~O_NONBLOCK) : rc | O_NONBLOCK;
rc = fcntl(sock, F_SETFL, rc);
} }
return rc; return rc;
#endif #endif
} }
int sock_set_blocking(const sock_t sock)
{
return _sock_set_blocking_mode(sock, 1);
}
int sock_set_nonblocking(const sock_t sock)
{
return _sock_set_blocking_mode(sock, 0);
}
int sock_read(const sock_t sock, void *const buff, const size_t len) int sock_read(const sock_t sock, void *const buff, const size_t len)
{ {
return recv(sock, buff, len, 0); return recv(sock, buff, len, 0);
@@ -212,27 +212,29 @@ int sock_is_recoverable(const int error)
int sock_connect_error(const sock_t sock) int sock_connect_error(const sock_t sock)
{ {
struct sockaddr sa; struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
socklen_t len; socklen_t len;
char temp; char temp;
memset(&sa, 0, sizeof(sa)); memset(&ss, 0, sizeof(ss));
sa.sa_family = AF_UNSPEC; len = sizeof(ss);
len = sizeof(sa); sa->sa_family = AF_UNSPEC;
/* we don't actually care about the peer name, we're just checking if /* we don't actually care about the peer name, we're just checking if
* we're connected or not */ * we're connected or not */
if (getpeername(sock, &sa, &len) == 0) if (getpeername(sock, sa, &len) == 0) {
{
return 0; return 0;
} }
/* it's possible that the error wasn't ENOTCONN, so if it wasn't, /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
* return that */ * return that */
#ifdef _WIN32 #ifdef _WIN32
if (sock_error() != WSAENOTCONN) return sock_error(); if (sock_error() != WSAENOTCONN)
return sock_error();
#else #else
if (sock_error() != ENOTCONN) return sock_error(); if (sock_error() != ENOTCONN)
return sock_error();
#endif #endif
/* load the correct error into errno through error slippage */ /* load the correct error into errno through error slippage */

View File

@@ -22,6 +22,7 @@
#include "strophe.h" #include "strophe.h"
#include "common.h" #include "common.h"
#include "hash.h" #include "hash.h"
#include "parser.h"
/** Create a stanza object. /** Create a stanza object.
* This function allocates and initializes a blank stanza object. * This function allocates and initializes a blank stanza object.
@@ -76,35 +77,29 @@ xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza)
static int _stanza_copy_attributes(xmpp_stanza_t *dst, static int _stanza_copy_attributes(xmpp_stanza_t *dst,
const xmpp_stanza_t *const src) const xmpp_stanza_t *const src)
{ {
hash_iterator_t *iter = NULL; hash_iterator_t *iter;
const char *key; const char *key;
void *val; const char *val;
int rc = XMPP_EOK;
dst->attributes = hash_new(src->ctx, 8, xmpp_free);
if (!dst->attributes)
return -1;
iter = hash_iter_new(src->attributes); iter = hash_iter_new(src->attributes);
if (!iter) if (!iter)
goto error; rc = XMPP_EMEM;
while ((key = hash_iter_next(iter))) {
val = xmpp_strdup(src->ctx, while (rc == XMPP_EOK && (key = hash_iter_next(iter))) {
(char *)hash_get(src->attributes, key)); val = hash_get(src->attributes, key);
if (!val) if (!val)
goto error; rc = XMPP_EINT;
if (rc == XMPP_EOK)
if (hash_add(dst->attributes, key, val)) { rc = xmpp_stanza_set_attribute(dst, key, val);
xmpp_free(src->ctx, val);
goto error;
}
} }
hash_iter_release(iter); hash_iter_release(iter);
return 0;
error: if (rc != XMPP_EOK && dst->attributes) {
if (iter != NULL)
hash_iter_release(iter);
hash_release(dst->attributes); hash_release(dst->attributes);
return -1; dst->attributes = NULL;
}
return rc;
} }
/** Copy a stanza and its children. /** Copy a stanza and its children.
@@ -124,13 +119,15 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
xmpp_stanza_t *copy, *child, *copychild, *tail; xmpp_stanza_t *copy, *child, *copychild, *tail;
copy = xmpp_stanza_new(stanza->ctx); copy = xmpp_stanza_new(stanza->ctx);
if (!copy) goto copy_error; if (!copy)
goto copy_error;
copy->type = stanza->type; copy->type = stanza->type;
if (stanza->data) { if (stanza->data) {
copy->data = xmpp_strdup(stanza->ctx, stanza->data); copy->data = xmpp_strdup(stanza->ctx, stanza->data);
if (!copy->data) goto copy_error; if (!copy->data)
goto copy_error;
} }
if (stanza->attributes) { if (stanza->attributes) {
@@ -141,7 +138,8 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
tail = copy->children; tail = copy->children;
for (child = stanza->children; child; child = child->next) { for (child = stanza->children; child; child = child->next) {
copychild = xmpp_stanza_copy(child); copychild = xmpp_stanza_copy(child);
if (!copychild) goto copy_error; if (!copychild)
goto copy_error;
copychild->parent = copy; copychild->parent = copy;
if (tail) { if (tail) {
@@ -156,7 +154,8 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
copy_error: copy_error:
/* release all the hitherto allocated memory */ /* release all the hitherto allocated memory */
if (copy) xmpp_stanza_release(copy); if (copy)
xmpp_stanza_release(copy);
return NULL; return NULL;
} }
@@ -187,8 +186,10 @@ int xmpp_stanza_release(xmpp_stanza_t * const stanza)
xmpp_stanza_release(tchild); xmpp_stanza_release(tchild);
} }
if (stanza->attributes) hash_release(stanza->attributes); if (stanza->attributes)
if (stanza->data) xmpp_free(stanza->ctx, stanza->data); hash_release(stanza->attributes);
if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
xmpp_free(stanza->ctx, stanza); xmpp_free(stanza->ctx, stanza);
released = 1; released = 1;
} }
@@ -196,6 +197,19 @@ int xmpp_stanza_release(xmpp_stanza_t * const stanza)
return released; return released;
} }
/** Get the strophe context that the stanza is associated with.
*
* @param stanza a Strophe stanza object
*
* @return a Strophe context
*
* @ingroup Stanza
*/
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza)
{
return stanza->ctx;
}
/** Determine if a stanza is a text node. /** Determine if a stanza is a text node.
* *
* @param stanza a Strophe stanza object * @param stanza a Strophe stanza object
@@ -281,9 +295,11 @@ static char *_escape_xml(xmpp_ctx_t * const ctx, char *text)
} }
/* small helper function */ /* small helper function */
static void _render_update(int *written, const int length, static void _render_update(int *written,
const int length,
const int lastwrite, const int lastwrite,
size_t *left, char **ptr) size_t *left,
char **ptr)
{ {
*written += lastwrite; *written += lastwrite;
@@ -302,7 +318,8 @@ static void _render_update(int *written, const int length,
* and return values > buflen indicate buffer was not large enough * and return values > buflen indicate buffer was not large enough
*/ */
static int _render_stanza_recursive(xmpp_stanza_t *stanza, static int _render_stanza_recursive(xmpp_stanza_t *stanza,
char * const buf, size_t const buflen) char *const buf,
size_t const buflen)
{ {
char *ptr = buf; char *ptr = buf;
size_t left = buflen; size_t left = buflen;
@@ -314,23 +331,29 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
written = 0; written = 0;
if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_UNKNOWN)
return XMPP_EINVOP;
if (stanza->type == XMPP_STANZA_TEXT) { if (stanza->type == XMPP_STANZA_TEXT) {
if (!stanza->data) return XMPP_EINVOP; if (!stanza->data)
return XMPP_EINVOP;
tmp = _escape_xml(stanza->ctx, stanza->data); tmp = _escape_xml(stanza->ctx, stanza->data);
if (tmp == NULL) return XMPP_EMEM; if (tmp == NULL)
return XMPP_EMEM;
ret = xmpp_snprintf(ptr, left, "%s", tmp); ret = xmpp_snprintf(ptr, left, "%s", tmp);
xmpp_free(stanza->ctx, tmp); xmpp_free(stanza->ctx, tmp);
if (ret < 0) return XMPP_EMEM; if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
} else { /* stanza->type == XMPP_STANZA_TAG */ } else { /* stanza->type == XMPP_STANZA_TAG */
if (!stanza->data) return XMPP_EINVOP; if (!stanza->data)
return XMPP_EINVOP;
/* write beginning of tag and attributes */ /* write beginning of tag and attributes */
ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); ret = xmpp_snprintf(ptr, left, "<%s", stanza->data);
if (ret < 0) return XMPP_EMEM; if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) {
@@ -338,10 +361,10 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
while ((key = hash_iter_next(iter))) { while ((key = hash_iter_next(iter))) {
if (!strcmp(key, "xmlns")) { if (!strcmp(key, "xmlns")) {
/* don't output namespace if parent stanza is the same */ /* don't output namespace if parent stanza is the same */
if (stanza->parent && if (stanza->parent && stanza->parent->attributes &&
stanza->parent->attributes &&
hash_get(stanza->parent->attributes, key) && hash_get(stanza->parent->attributes, key) &&
!strcmp((char*)hash_get(stanza->attributes, key), !strcmp(
(char *)hash_get(stanza->attributes, key),
(char *)hash_get(stanza->parent->attributes, key))) (char *)hash_get(stanza->parent->attributes, key)))
continue; continue;
/* or if this is the stream namespace */ /* or if this is the stream namespace */
@@ -352,10 +375,16 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
} }
tmp = _escape_xml(stanza->ctx, tmp = _escape_xml(stanza->ctx,
(char *)hash_get(stanza->attributes, key)); (char *)hash_get(stanza->attributes, key));
if (tmp == NULL) return XMPP_EMEM; if (tmp == NULL) {
hash_iter_release(iter);
return XMPP_EMEM;
}
ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
xmpp_free(stanza->ctx, tmp); xmpp_free(stanza->ctx, tmp);
if (ret < 0) return XMPP_EMEM; if (ret < 0) {
hash_iter_release(iter);
return XMPP_EMEM;
}
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
} }
hash_iter_release(iter); hash_iter_release(iter);
@@ -364,21 +393,24 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
if (!stanza->children) { if (!stanza->children) {
/* write end if singleton tag */ /* write end if singleton tag */
ret = xmpp_snprintf(ptr, left, "/>"); ret = xmpp_snprintf(ptr, left, "/>");
if (ret < 0) return XMPP_EMEM; if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
} else { } else {
/* this stanza has child stanzas */ /* this stanza has child stanzas */
/* write end of start tag */ /* write end of start tag */
ret = xmpp_snprintf(ptr, left, ">"); ret = xmpp_snprintf(ptr, left, ">");
if (ret < 0) return XMPP_EMEM; if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
/* iterate and recurse over child stanzas */ /* iterate and recurse over child stanzas */
child = stanza->children; child = stanza->children;
while (child) { while (child) {
ret = _render_stanza_recursive(child, ptr, left); ret = _render_stanza_recursive(child, ptr, left);
if (ret < 0) return ret; if (ret < 0)
return ret;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
@@ -387,7 +419,8 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
/* write end tag */ /* write end tag */
ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data);
if (ret < 0) return XMPP_EMEM; if (ret < 0)
return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr); _render_update(&written, buflen, ret, &left, &ptr);
} }
@@ -429,9 +462,14 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
} }
ret = _render_stanza_recursive(stanza, buffer, length); ret = _render_stanza_recursive(stanza, buffer, length);
if (ret < 0) return ret; if (ret < 0) {
xmpp_free(stanza->ctx, buffer);
*buf = NULL;
*buflen = 0;
return ret;
}
if (ret > length - 1) { if ((size_t)ret > length - 1) {
tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1); tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1);
if (!tmp) { if (!tmp) {
xmpp_free(stanza->ctx, buffer); xmpp_free(stanza->ctx, buffer);
@@ -443,7 +481,12 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
buffer = tmp; buffer = tmp;
ret = _render_stanza_recursive(stanza, buffer, length); ret = _render_stanza_recursive(stanza, buffer, length);
if (ret > length - 1) return XMPP_EMEM; if ((size_t)ret > length - 1) {
xmpp_free(stanza->ctx, buffer);
*buf = NULL;
*buflen = 0;
return XMPP_EMEM;
}
} }
buffer[length - 1] = 0; buffer[length - 1] = 0;
@@ -464,12 +507,13 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name)
const char * const name)
{ {
if (stanza->type == XMPP_STANZA_TEXT) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT)
return XMPP_EINVOP;
if (stanza->data) xmpp_free(stanza->ctx, stanza->data); if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
stanza->type = XMPP_STANZA_TAG; stanza->type = XMPP_STANZA_TAG;
stanza->data = xmpp_strdup(stanza->ctx, name); stanza->data = xmpp_strdup(stanza->ctx, name);
@@ -489,7 +533,8 @@ int xmpp_stanza_set_name(xmpp_stanza_t *stanza,
*/ */
const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza) const char *xmpp_stanza_get_name(xmpp_stanza_t *const stanza)
{ {
if (stanza->type == XMPP_STANZA_TEXT) return NULL; if (stanza->type == XMPP_STANZA_TEXT)
return NULL;
return stanza->data; return stanza->data;
} }
@@ -525,7 +570,8 @@ int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza)
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza, int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
const char **attr, int attrlen) const char **attr,
int attrlen)
{ {
hash_iterator_t *iter; hash_iterator_t *iter;
const char *key; const char *key;
@@ -570,21 +616,27 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
const char *const value) const char *const value)
{ {
char *val; char *val;
int rc;
if (stanza->type != XMPP_STANZA_TAG) return XMPP_EINVOP; if (stanza->type != XMPP_STANZA_TAG)
return XMPP_EINVOP;
if (!stanza->attributes) { if (!stanza->attributes) {
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free); stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
if (!stanza->attributes) return XMPP_EMEM; if (!stanza->attributes)
return XMPP_EMEM;
} }
val = xmpp_strdup(stanza->ctx, value); val = xmpp_strdup(stanza->ctx, value);
if (!val) { if (!val) {
hash_release(stanza->attributes);
return XMPP_EMEM; return XMPP_EMEM;
} }
hash_add(stanza->attributes, key, val); rc = hash_add(stanza->attributes, key, val);
if (rc < 0) {
xmpp_free(stanza->ctx, val);
return XMPP_EMEM;
}
return XMPP_EOK; return XMPP_EOK;
} }
@@ -600,12 +652,52 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, int xmpp_stanza_set_ns(xmpp_stanza_t *const stanza, const char *const ns)
const char * const ns)
{ {
return xmpp_stanza_set_attribute(stanza, "xmlns", ns); return xmpp_stanza_set_attribute(stanza, "xmlns", ns);
} }
/** Add a child stanza to a stanza object.
* If do_clone is TRUE, user keeps reference to the child stanza and must call
* xmpp_stanza_release() to release the reference. If do_clone is FALSE, user
* transfers ownership and must not neither call xmpp_stanza_release() for
* the child stanza nor use it.
*
* @param stanza a Strophe stanza object
* @param child the child stanza object
* @param do_clone TRUE to increase ref count of child (default for
* xmpp_stanza_add_child())
*
* @return XMPP_EOK (0) on success or a number less than 0 on failure
*
* @ingroup Stanza
*/
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
xmpp_stanza_t *child,
int do_clone)
{
xmpp_stanza_t *s;
if (do_clone) {
/* get a reference to the child */
xmpp_stanza_clone(child);
}
child->parent = stanza;
if (!stanza->children)
stanza->children = child;
else {
s = stanza->children;
while (s->next)
s = s->next;
s->next = child;
child->prev = s;
}
return XMPP_EOK;
}
/** Add a child stanza to a stanza object. /** Add a child stanza to a stanza object.
* This function clones the child and appends it to the stanza object's * This function clones the child and appends it to the stanza object's
* children. * children.
@@ -619,23 +711,7 @@ int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza,
*/ */
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child) int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
{ {
xmpp_stanza_t *s; return xmpp_stanza_add_child_ex(stanza, child, 1);
/* get a reference to the child */
xmpp_stanza_clone(child);
child->parent = stanza;
if (!stanza->children)
stanza->children = child;
else {
s = stanza->children;
while (s->next) s = s->next;
s->next = child;
child->prev = s;
}
return XMPP_EOK;
} }
/** Set the text data for a text stanza. /** Set the text data for a text stanza.
@@ -651,14 +727,15 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text)
const char * const text)
{ {
if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TAG)
return XMPP_EINVOP;
stanza->type = XMPP_STANZA_TEXT; stanza->type = XMPP_STANZA_TEXT;
if (stanza->data) xmpp_free(stanza->ctx, stanza->data); if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
stanza->data = xmpp_strdup(stanza->ctx, text); stanza->data = xmpp_strdup(stanza->ctx, text);
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK; return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
@@ -682,13 +759,16 @@ int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
const char *const text, const char *const text,
const size_t size) const size_t size)
{ {
if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TAG)
return XMPP_EINVOP;
stanza->type = XMPP_STANZA_TEXT; stanza->type = XMPP_STANZA_TEXT;
if (stanza->data) xmpp_free(stanza->ctx, stanza->data); if (stanza->data)
xmpp_free(stanza->ctx, stanza->data);
stanza->data = xmpp_alloc(stanza->ctx, size + 1); stanza->data = xmpp_alloc(stanza->ctx, size + 1);
if (!stanza->data) return XMPP_EMEM; if (!stanza->data)
return XMPP_EMEM;
memcpy(stanza->data, text, size); memcpy(stanza->data, text, size);
stanza->data[size] = 0; stanza->data[size] = 0;
@@ -812,16 +892,50 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t * const stanza,
const char *const ns) const char *const ns)
{ {
xmpp_stanza_t *child; xmpp_stanza_t *child;
const char *child_ns;
for (child = stanza->children; child; child = child->next) { for (child = stanza->children; child; child = child->next) {
if (xmpp_stanza_get_ns(child) && child_ns = xmpp_stanza_get_ns(child);
strcmp(ns, xmpp_stanza_get_ns(child)) == 0) if (child_ns && strcmp(ns, child_ns) == 0)
break; break;
} }
return child; return child;
} }
/** Get the first child of stanza with name and a given namespace.
* This function searches all the immediate children of stanza for a child
* stanza that matches the name and namespace provided.
* The first matching child is returned.
*
* @param stanza a Strophe stanza object
* @param name a string with the name to match
* @param ns a string with the namespace to match
*
* @return the matching child stanza object or NULL if no match was found
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
const char *const name,
const char *const ns)
{
xmpp_stanza_t *child;
const char *child_ns;
for (child = stanza->children; child; child = child->next) {
if (child->type == XMPP_STANZA_TAG &&
(strcmp(name, xmpp_stanza_get_name(child)) == 0)) {
child_ns = xmpp_stanza_get_ns(child);
if (child_ns && strcmp(ns, child_ns) == 0) {
break;
}
}
}
return child;
}
/** Get the list of children. /** Get the list of children.
* This function returns the first child of the stanza object. The rest * This function returns the first child of the stanza object. The rest
* of the children can be obtained by calling xmpp_stanza_get_next() to * of the children can be obtained by calling xmpp_stanza_get_next() to
@@ -880,10 +994,12 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
if (child->type == XMPP_STANZA_TEXT) if (child->type == XMPP_STANZA_TEXT)
len += strlen(child->data); len += strlen(child->data);
if (len == 0) return NULL; if (len == 0)
return NULL;
text = (char *)xmpp_alloc(stanza->ctx, len + 1); text = (char *)xmpp_alloc(stanza->ctx, len + 1);
if (!text) return NULL; if (!text)
return NULL;
len = 0; len = 0;
for (child = stanza->children; child; child = child->next) for (child = stanza->children; child; child = child->next)
@@ -930,8 +1046,7 @@ const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza)
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_id(xmpp_stanza_t * const stanza, int xmpp_stanza_set_id(xmpp_stanza_t *const stanza, const char *const id)
const char * const id)
{ {
return xmpp_stanza_set_attribute(stanza, "id", id); return xmpp_stanza_set_attribute(stanza, "id", id);
} }
@@ -947,8 +1062,7 @@ int xmpp_stanza_set_id(xmpp_stanza_t * const stanza,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_type(xmpp_stanza_t * const stanza, int xmpp_stanza_set_type(xmpp_stanza_t *const stanza, const char *const type)
const char * const type)
{ {
return xmpp_stanza_set_attribute(stanza, "type", type); return xmpp_stanza_set_attribute(stanza, "type", type);
} }
@@ -965,8 +1079,7 @@ int xmpp_stanza_set_type(xmpp_stanza_t * const stanza,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_to(xmpp_stanza_t * const stanza, int xmpp_stanza_set_to(xmpp_stanza_t *const stanza, const char *const to)
const char * const to)
{ {
return xmpp_stanza_set_attribute(stanza, "to", to); return xmpp_stanza_set_attribute(stanza, "to", to);
} }
@@ -983,8 +1096,7 @@ int xmpp_stanza_set_to(xmpp_stanza_t * const stanza,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
int xmpp_stanza_set_from(xmpp_stanza_t * const stanza, int xmpp_stanza_set_from(xmpp_stanza_t *const stanza, const char *const from)
const char * const from)
{ {
return xmpp_stanza_set_attribute(stanza, "from", from); return xmpp_stanza_set_attribute(stanza, "from", from);
} }
@@ -1047,36 +1159,127 @@ int xmpp_stanza_del_attribute(xmpp_stanza_t * const stanza,
*/ */
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza) xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza)
{ {
xmpp_stanza_t *copy; xmpp_stanza_t *copy = NULL;
const char *from;
int rc;
from = xmpp_stanza_get_from(stanza);
if (!from)
goto copy_error;
copy = xmpp_stanza_new(stanza->ctx); copy = xmpp_stanza_new(stanza->ctx);
if (!copy) goto copy_error; if (!copy)
goto copy_error;
copy->type = stanza->type; copy->type = stanza->type;
if (stanza->data) { if (stanza->data) {
copy->data = xmpp_strdup(stanza->ctx, stanza->data); copy->data = xmpp_strdup(stanza->ctx, stanza->data);
if (!copy->data) goto copy_error; if (!copy->data)
}
if (stanza->attributes) {
if (_stanza_copy_attributes(copy, stanza) == -1)
goto copy_error; goto copy_error;
} }
xmpp_stanza_set_to(copy, xmpp_stanza_get_from(stanza)); if (stanza->attributes) {
if (_stanza_copy_attributes(copy, stanza) < 0)
goto copy_error;
}
xmpp_stanza_del_attribute(copy, "to");
xmpp_stanza_del_attribute(copy, "from"); xmpp_stanza_del_attribute(copy, "from");
xmpp_stanza_del_attribute(copy, "xmlns");
rc = xmpp_stanza_set_to(copy, from);
if (rc != XMPP_EOK)
goto copy_error;
return copy; return copy;
copy_error: copy_error:
if (copy) xmpp_stanza_release(copy); if (copy)
xmpp_stanza_release(copy);
return NULL; return NULL;
} }
static xmpp_stanza_t * /** Create an error stanza in reply to the provided stanza.
_stanza_new_with_attrs(xmpp_ctx_t *ctx, const char * const name, *
const char * const type, const char * const id, * Check https://tools.ietf.org/html/rfc6120#section-8.3 for details.
*
* @param stanza a Strophe stanza object
* @param error_type type attribute in the <error/> child element
* @param condition the defined-condition (e.g. "item-not-found")
* @param text optional description, may be NULL
*
* @return a new Strophe stanza object
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
const char *const error_type,
const char *const condition,
const char *const text)
{
xmpp_ctx_t *ctx = stanza->ctx;
xmpp_stanza_t *reply = NULL;
xmpp_stanza_t *error;
xmpp_stanza_t *item;
xmpp_stanza_t *text_stanza;
const char *to;
if (!error_type || !condition)
goto quit_err;
reply = xmpp_stanza_reply(stanza);
if (!reply)
goto quit_err;
xmpp_stanza_set_type(reply, "error");
to = xmpp_stanza_get_to(stanza);
if (to)
xmpp_stanza_set_from(reply, to);
error = xmpp_stanza_new(ctx);
if (!error)
goto quit_err;
xmpp_stanza_set_name(error, "error");
xmpp_stanza_set_type(error, error_type);
xmpp_stanza_add_child(reply, error);
xmpp_stanza_release(error);
item = xmpp_stanza_new(ctx);
if (!item)
goto quit_err;
xmpp_stanza_set_name(item, condition);
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
xmpp_stanza_add_child(error, item);
xmpp_stanza_release(item);
if (text) {
item = xmpp_stanza_new(ctx);
if (!item)
goto quit_err;
xmpp_stanza_set_name(item, "text");
xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF);
xmpp_stanza_add_child(error, item);
xmpp_stanza_release(item);
text_stanza = xmpp_stanza_new(ctx);
if (!text_stanza)
goto quit_err;
xmpp_stanza_set_text(text_stanza, text);
xmpp_stanza_add_child(item, text_stanza);
xmpp_stanza_release(text_stanza);
}
return reply;
quit_err:
if (reply)
xmpp_stanza_release(reply);
return NULL;
}
static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
const char *const name,
const char *const type,
const char *const id,
const char *const to) const char *const to)
{ {
xmpp_stanza_t *stanza = xmpp_stanza_new(ctx); xmpp_stanza_t *stanza = xmpp_stanza_new(ctx);
@@ -1110,8 +1313,10 @@ _stanza_new_with_attrs(xmpp_ctx_t *ctx, const char * const name,
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx, const char * const type, xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
const char * const to, const char * const id) const char *const type,
const char *const to,
const char *const id)
{ {
return _stanza_new_with_attrs(ctx, "message", type, id, to); return _stanza_new_with_attrs(ctx, "message", type, id, to);
} }
@@ -1196,8 +1401,8 @@ int xmpp_message_set_body(xmpp_stanza_t *msg, const char * const text)
* *
* @ingroup Stanza * @ingroup Stanza
*/ */
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type, xmpp_stanza_t *
const char * const id) xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id)
{ {
return _stanza_new_with_attrs(ctx, "iq", type, id, NULL); return _stanza_new_with_attrs(ctx, "iq", type, id, NULL);
} }
@@ -1214,3 +1419,180 @@ xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
{ {
return _stanza_new_with_attrs(ctx, "presence", NULL, NULL, NULL); return _stanza_new_with_attrs(ctx, "presence", NULL, NULL, NULL);
} }
/** Create an <stream:error/> stanza object with given type and error text.
* The error text is optional and may be NULL.
*
* @param ctx a Strophe context object
* @param type enum of xmpp_error_type_t
* @param text content of a 'text'
*
* @return a new Strophe stanza object
*
* @todo Handle errors in this function
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
xmpp_error_type_t const type,
const char *const text)
{
xmpp_stanza_t *error =
_stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL);
xmpp_stanza_t *error_type = xmpp_stanza_new(ctx);
switch (type) {
case XMPP_SE_BAD_FORMAT:
xmpp_stanza_set_name(error_type, "bad-format");
break;
case XMPP_SE_BAD_NS_PREFIX:
xmpp_stanza_set_name(error_type, "bad-namespace-prefix");
break;
case XMPP_SE_CONFLICT:
xmpp_stanza_set_name(error_type, "conflict");
break;
case XMPP_SE_CONN_TIMEOUT:
xmpp_stanza_set_name(error_type, "connection-timeout");
break;
case XMPP_SE_HOST_GONE:
xmpp_stanza_set_name(error_type, "host-gone");
break;
case XMPP_SE_HOST_UNKNOWN:
xmpp_stanza_set_name(error_type, "host-unknown");
break;
case XMPP_SE_IMPROPER_ADDR:
xmpp_stanza_set_name(error_type, "improper-addressing");
break;
case XMPP_SE_INTERNAL_SERVER_ERROR:
xmpp_stanza_set_name(error_type, "internal-server-error");
break;
case XMPP_SE_INVALID_FROM:
xmpp_stanza_set_name(error_type, "invalid-from");
break;
case XMPP_SE_INVALID_ID:
xmpp_stanza_set_name(error_type, "invalid-id");
break;
case XMPP_SE_INVALID_NS:
xmpp_stanza_set_name(error_type, "invalid-namespace");
break;
case XMPP_SE_INVALID_XML:
xmpp_stanza_set_name(error_type, "invalid-xml");
break;
case XMPP_SE_NOT_AUTHORIZED:
xmpp_stanza_set_name(error_type, "not-authorized");
break;
case XMPP_SE_POLICY_VIOLATION:
xmpp_stanza_set_name(error_type, "policy-violation");
break;
case XMPP_SE_REMOTE_CONN_FAILED:
xmpp_stanza_set_name(error_type, "remote-connection-failed");
break;
case XMPP_SE_RESOURCE_CONSTRAINT:
xmpp_stanza_set_name(error_type, "resource-constraint");
break;
case XMPP_SE_RESTRICTED_XML:
xmpp_stanza_set_name(error_type, "restricted-xml");
break;
case XMPP_SE_SEE_OTHER_HOST:
xmpp_stanza_set_name(error_type, "see-other-host");
break;
case XMPP_SE_SYSTEM_SHUTDOWN:
xmpp_stanza_set_name(error_type, "system-shutdown");
break;
case XMPP_SE_UNDEFINED_CONDITION:
xmpp_stanza_set_name(error_type, "undefined-condition");
break;
case XMPP_SE_UNSUPPORTED_ENCODING:
xmpp_stanza_set_name(error_type, "unsupported-encoding");
break;
case XMPP_SE_UNSUPPORTED_STANZA_TYPE:
xmpp_stanza_set_name(error_type, "unsupported-stanza-type");
break;
case XMPP_SE_UNSUPPORTED_VERSION:
xmpp_stanza_set_name(error_type, "unsupported-version");
break;
case XMPP_SE_XML_NOT_WELL_FORMED:
xmpp_stanza_set_name(error_type, "xml-not-well-formed");
break;
default:
xmpp_stanza_set_name(error_type, "internal-server-error");
break;
}
xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
xmpp_stanza_add_child(error, error_type);
xmpp_stanza_release(error_type);
if (text) {
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
xmpp_stanza_t *content = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(error_text, "text");
xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
xmpp_stanza_set_text(content, text);
xmpp_stanza_add_child(error_text, content);
xmpp_stanza_release(content);
xmpp_stanza_add_child(error, error_text);
xmpp_stanza_release(error_text);
}
return error;
}
static void _stub_stream_start(char *name, char **attrs, void *userdata)
{
UNUSED(name);
UNUSED(attrs);
UNUSED(userdata);
}
static void _stub_stream_end(char *name, void *userdata)
{
UNUSED(name);
UNUSED(userdata);
}
static void _stream_stanza(xmpp_stanza_t *stanza, void *userdata)
{
stanza = xmpp_stanza_clone(stanza);
*(xmpp_stanza_t **)userdata = stanza;
}
/** Create a stanza object from the string.
* This function allocates and initializes a stanza object which represents
* stanza located in the string.
* The stanza will have a reference count of one, so the caller does not
* need to clone it.
*
* @param ctx a Strophe context object
* @param str stanza in NULL terminated string representation
*
* @return a stanza object or NULL on an error
*
* @ingroup Stanza
*/
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str)
{
xmpp_stanza_t *stanza = NULL;
parser_t *parser;
int ret;
static const char *start = "<stream>";
static const char *end = "</stream>";
parser = parser_new(ctx, _stub_stream_start, _stub_stream_end,
_stream_stanza, &stanza);
if (parser) {
ret = parser_feed(parser, (char *)start, strlen(start)) &&
parser_feed(parser, (char *)str, strlen(str)) &&
parser_feed(parser, (char *)end, strlen(end));
parser_free(parser);
if (!ret && stanza) {
xmpp_stanza_release(stanza);
stanza = NULL;
}
}
return stanza;
}

View File

@@ -1,116 +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 program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @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,40 +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 program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @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__ */

View File

@@ -24,7 +24,7 @@ typedef struct _tls tls_t;
void tls_initialize(void); void tls_initialize(void);
void tls_shutdown(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); void tls_free(tls_t *tls);
int tls_set_credentials(tls_t *tls, const char *cafilename); int tls_set_credentials(tls_t *tls, const char *cafilename);

View File

@@ -33,59 +33,75 @@ void tls_shutdown(void)
return; return;
} }
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) tls_t *tls_new(xmpp_conn_t *conn)
{ {
UNUSED(conn);
/* always fail */ /* always fail */
return NULL; return NULL;
} }
void tls_free(tls_t *tls) void tls_free(tls_t *tls)
{ {
UNUSED(tls);
return; return;
} }
int tls_set_credentials(tls_t *tls, const char *cafilename) int tls_set_credentials(tls_t *tls, const char *cafilename)
{ {
UNUSED(tls);
UNUSED(cafilename);
return -1; return -1;
} }
int tls_start(tls_t *tls) int tls_start(tls_t *tls)
{ {
UNUSED(tls);
return -1; return -1;
} }
int tls_stop(tls_t *tls) int tls_stop(tls_t *tls)
{ {
UNUSED(tls);
return -1; return -1;
} }
int tls_error(tls_t *tls) int tls_error(tls_t *tls)
{ {
UNUSED(tls);
/* todo: some kind of error polling/dump */ /* todo: some kind of error polling/dump */
return 0; return 0;
} }
int tls_pending(tls_t *tls) int tls_pending(tls_t *tls)
{ {
UNUSED(tls);
return 0; return 0;
} }
int tls_read(tls_t *tls, void *const buff, const size_t len) int tls_read(tls_t *tls, void *const buff, const size_t len)
{ {
UNUSED(tls);
UNUSED(buff);
UNUSED(len);
return -1; return -1;
} }
int tls_write(tls_t *tls, const void *const buff, const size_t len) int tls_write(tls_t *tls, const void *const buff, const size_t len)
{ {
UNUSED(tls);
UNUSED(buff);
UNUSED(len);
return -1; return -1;
} }
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(tls_t *tls)
{ {
UNUSED(tls);
return -1; return -1;
} }
int tls_is_recoverable(int error) int tls_is_recoverable(int error)
{ {
UNUSED(error);
return 0; return 0;
} }

View File

@@ -46,13 +46,13 @@ void tls_shutdown(void)
gnutls_global_deinit(); gnutls_global_deinit();
} }
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) tls_t *tls_new(xmpp_conn_t *conn)
{ {
tls_t *tls = xmpp_alloc(ctx, sizeof(tls_t)); tls_t *tls = xmpp_alloc(conn->ctx, sizeof(tls_t));
if (tls) { if (tls) {
tls->ctx = ctx; tls->ctx = conn->ctx;
tls->sock = sock; tls->sock = conn->sock;
gnutls_init(&tls->session, GNUTLS_CLIENT); gnutls_init(&tls->session, GNUTLS_CLIENT);
gnutls_certificate_allocate_credentials(&tls->cred); gnutls_certificate_allocate_credentials(&tls->cred);
@@ -61,7 +61,7 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
gnutls_set_default_priority(tls->session); gnutls_set_default_priority(tls->session);
/* fixme: this may require setting a callback on win32? */ /* fixme: this may require setting a callback on win32? */
gnutls_transport_set_int(tls->session, sock); gnutls_transport_set_int(tls->session, conn->sock);
} }
return tls; return tls;
@@ -79,8 +79,8 @@ int tls_set_credentials(tls_t *tls, const char *cafilename)
int err; int err;
/* set trusted credentials -- takes a .pem filename */ /* set trusted credentials -- takes a .pem filename */
err = gnutls_certificate_set_x509_trust_file(tls->cred, err = gnutls_certificate_set_x509_trust_file(tls->cred, cafilename,
cafilename, GNUTLS_X509_FMT_PEM); GNUTLS_X509_FMT_PEM);
if (err >= 0) { if (err >= 0) {
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
tls->cred); tls->cred);
@@ -142,5 +142,6 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(tls_t *tls)
{ {
UNUSED(tls);
return 0; return 0;
} }

View File

@@ -24,11 +24,29 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/opensslv.h>
#include <openssl/x509v3.h>
#include "common.h" #include "common.h"
#include "tls.h" #include "tls.h"
#include "sock.h" #include "sock.h"
/*
* Redefine OPENSSL_VERSION_NUMBER for LibreSSL.
* LibreSSL and OpenSSL use different and incompatible version schemes. Solve
* this issue in the way how nginx project did.
*/
#if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L)
#undef OPENSSL_VERSION_NUMBER
#if (LIBRESSL_VERSION_NUMBER >= 0x2080000fL)
#define OPENSSL_VERSION_NUMBER 0x1010000fL
#elif (LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
#define OPENSSL_VERSION_NUMBER 0x1000200fL
#else
#define OPENSSL_VERSION_NUMBER 0x1000107fL
#endif
#endif
struct _tls { struct _tls {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
sock_t sock; sock_t sock;
@@ -44,18 +62,149 @@ enum {
}; };
static void _tls_sock_wait(tls_t *tls, int error); static void _tls_sock_wait(tls_t *tls, int error);
static const char *_tls_error_str(int error, const char **tbl, size_t tbl_size);
static void _tls_set_error(tls_t *tls, int error); static void _tls_set_error(tls_t *tls, int error);
static void _tls_log_error(xmpp_ctx_t *ctx); static void _tls_log_error(xmpp_ctx_t *ctx);
static void _tls_dump_cert_info(tls_t *tls);
#define TLS_ERROR_STR(error, table) \
_tls_error_str(error, table, ARRAY_SIZE(table))
#define TLS_ERROR_FIELD(x) [x] = #x
const char *tls_errors[] = {
TLS_ERROR_FIELD(SSL_ERROR_NONE),
TLS_ERROR_FIELD(SSL_ERROR_SSL),
TLS_ERROR_FIELD(SSL_ERROR_WANT_READ),
TLS_ERROR_FIELD(SSL_ERROR_WANT_WRITE),
TLS_ERROR_FIELD(SSL_ERROR_WANT_X509_LOOKUP),
TLS_ERROR_FIELD(SSL_ERROR_SYSCALL),
TLS_ERROR_FIELD(SSL_ERROR_ZERO_RETURN),
TLS_ERROR_FIELD(SSL_ERROR_WANT_CONNECT),
TLS_ERROR_FIELD(SSL_ERROR_WANT_ACCEPT),
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
TLS_ERROR_FIELD(SSL_ERROR_WANT_ASYNC),
TLS_ERROR_FIELD(SSL_ERROR_WANT_ASYNC_JOB),
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
TLS_ERROR_FIELD(SSL_ERROR_WANT_CLIENT_HELLO_CB),
#endif
};
const char *cert_errors[] = {
TLS_ERROR_FIELD(X509_V_OK),
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
TLS_ERROR_FIELD(X509_V_ERR_UNSPECIFIED),
#endif
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_GET_CRL),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY),
TLS_ERROR_FIELD(X509_V_ERR_CERT_SIGNATURE_FAILURE),
TLS_ERROR_FIELD(X509_V_ERR_CRL_SIGNATURE_FAILURE),
TLS_ERROR_FIELD(X509_V_ERR_CERT_NOT_YET_VALID),
TLS_ERROR_FIELD(X509_V_ERR_CERT_HAS_EXPIRED),
TLS_ERROR_FIELD(X509_V_ERR_CRL_NOT_YET_VALID),
TLS_ERROR_FIELD(X509_V_ERR_CRL_HAS_EXPIRED),
TLS_ERROR_FIELD(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD),
TLS_ERROR_FIELD(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD),
TLS_ERROR_FIELD(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD),
TLS_ERROR_FIELD(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD),
TLS_ERROR_FIELD(X509_V_ERR_OUT_OF_MEM),
TLS_ERROR_FIELD(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT),
TLS_ERROR_FIELD(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE),
TLS_ERROR_FIELD(X509_V_ERR_CERT_CHAIN_TOO_LONG),
TLS_ERROR_FIELD(X509_V_ERR_CERT_REVOKED),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_CA),
TLS_ERROR_FIELD(X509_V_ERR_PATH_LENGTH_EXCEEDED),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_PURPOSE),
TLS_ERROR_FIELD(X509_V_ERR_CERT_UNTRUSTED),
TLS_ERROR_FIELD(X509_V_ERR_CERT_REJECTED),
TLS_ERROR_FIELD(X509_V_ERR_SUBJECT_ISSUER_MISMATCH),
TLS_ERROR_FIELD(X509_V_ERR_AKID_SKID_MISMATCH),
TLS_ERROR_FIELD(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH),
TLS_ERROR_FIELD(X509_V_ERR_KEYUSAGE_NO_CERTSIGN),
TLS_ERROR_FIELD(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER),
TLS_ERROR_FIELD(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION),
TLS_ERROR_FIELD(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN),
TLS_ERROR_FIELD(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_NON_CA),
TLS_ERROR_FIELD(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED),
TLS_ERROR_FIELD(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE),
TLS_ERROR_FIELD(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_EXTENSION),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_POLICY_EXTENSION),
TLS_ERROR_FIELD(X509_V_ERR_NO_EXPLICIT_POLICY),
TLS_ERROR_FIELD(X509_V_ERR_APPLICATION_VERIFICATION),
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
TLS_ERROR_FIELD(X509_V_ERR_DIFFERENT_CRL_SCOPE),
TLS_ERROR_FIELD(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE),
TLS_ERROR_FIELD(X509_V_ERR_UNNESTED_RESOURCE),
TLS_ERROR_FIELD(X509_V_ERR_PERMITTED_VIOLATION),
TLS_ERROR_FIELD(X509_V_ERR_EXCLUDED_VIOLATION),
TLS_ERROR_FIELD(X509_V_ERR_SUBTREE_MINMAX),
TLS_ERROR_FIELD(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE),
TLS_ERROR_FIELD(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX),
TLS_ERROR_FIELD(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX),
TLS_ERROR_FIELD(X509_V_ERR_CRL_PATH_VALIDATION_ERROR),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_INVALID_VERSION),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_INVALID_ALGORITHM),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_INVALID_CURVE),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED),
TLS_ERROR_FIELD(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256),
TLS_ERROR_FIELD(X509_V_ERR_HOSTNAME_MISMATCH),
TLS_ERROR_FIELD(X509_V_ERR_EMAIL_MISMATCH),
TLS_ERROR_FIELD(X509_V_ERR_IP_ADDRESS_MISMATCH),
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
TLS_ERROR_FIELD(X509_V_ERR_PATH_LOOP),
TLS_ERROR_FIELD(X509_V_ERR_DANE_NO_MATCH),
TLS_ERROR_FIELD(X509_V_ERR_EE_KEY_TOO_SMALL),
TLS_ERROR_FIELD(X509_V_ERR_CA_KEY_TOO_SMALL),
TLS_ERROR_FIELD(X509_V_ERR_CA_MD_TOO_WEAK),
TLS_ERROR_FIELD(X509_V_ERR_INVALID_CALL),
TLS_ERROR_FIELD(X509_V_ERR_STORE_LOOKUP),
TLS_ERROR_FIELD(X509_V_ERR_NO_VALID_SCTS),
TLS_ERROR_FIELD(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION),
TLS_ERROR_FIELD(X509_V_ERR_OCSP_VERIFY_NEEDED),
TLS_ERROR_FIELD(X509_V_ERR_OCSP_VERIFY_FAILED),
TLS_ERROR_FIELD(X509_V_ERR_OCSP_CERT_UNKNOWN),
#endif
};
#undef TLS_ERROR_FIELD
void tls_initialize(void) void tls_initialize(void)
{ {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init(); SSL_library_init();
SSL_load_error_strings(); SSL_load_error_strings();
#else
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#endif
} }
void tls_shutdown(void) void tls_shutdown(void)
{ {
return; /*
* FIXME: Don't free global tables, program or other libraries may use
* openssl after libstrophe finalization. Maybe better leak some fixed
* memory rather than cause random crashes of the main program.
*/
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_COMP_free_compression_methods();
#endif
#if OPENSSL_VERSION_NUMBER < 0x10000000L
ERR_remove_state(0);
#else
ERR_remove_thread_state(NULL);
#endif
#endif
} }
int tls_error(tls_t *tls) int tls_error(tls_t *tls)
@@ -63,29 +212,81 @@ int tls_error(tls_t *tls)
return tls->lasterror; return tls->lasterror;
} }
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) tls_t *tls_new(xmpp_conn_t *conn)
{ {
tls_t *tls = xmpp_alloc(ctx, sizeof(*tls)); tls_t *tls = xmpp_alloc(conn->ctx, sizeof(*tls));
int mode;
if (tls) { if (tls) {
int ret; int ret;
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
/* Hostname verification is supported in OpenSSL 1.0.2 and newer. */
X509_VERIFY_PARAM *param;
#endif
memset(tls, 0, sizeof(*tls)); memset(tls, 0, sizeof(*tls));
tls->ctx = ctx; tls->ctx = conn->ctx;
tls->sock = sock; tls->sock = conn->sock;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
#else
tls->ssl_ctx = SSL_CTX_new(TLS_client_method());
#endif
if (tls->ssl_ctx == NULL) if (tls->ssl_ctx == NULL)
goto err; goto err;
/* Enable bug workarounds. */
SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_ALL);
/* Disable insecure SSL/TLS versions. */
SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv2); /* DROWN */
SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv3); /* POODLE */
SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_TLSv1); /* BEAST */
SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL); SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL);
SSL_CTX_set_mode(tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_verify(tls->ssl_ctx, SSL_VERIFY_NONE, NULL);
ret = SSL_CTX_set_default_verify_paths(tls->ssl_ctx);
if (ret == 0 && !conn->tls_trust) {
/*
* Returns 1 on success and 0 on failure. A missing default
* location is still treated as a success.
* Ignore errors when XMPP_CONN_FLAG_TRUST_TLS is set.
*/
xmpp_error(tls->ctx, "tls",
"SSL_CTX_set_default_verify_paths() failed");
goto err_free_ctx;
}
tls->ssl = SSL_new(tls->ssl_ctx); tls->ssl = SSL_new(tls->ssl_ctx);
if (tls->ssl == NULL) if (tls->ssl == NULL)
goto err_free_ctx; goto err_free_ctx;
ret = SSL_set_fd(tls->ssl, sock); #if OPENSSL_VERSION_NUMBER >= 0x0908060L && !defined(OPENSSL_NO_TLSEXT)
/* Enable SNI. */
SSL_set_tlsext_host_name(tls->ssl, conn->domain);
#endif
/* Trust server's certificate when user sets the flag explicitly. */
mode = conn->tls_trust ? SSL_VERIFY_NONE : SSL_VERIFY_PEER;
SSL_set_verify(tls->ssl, mode, NULL);
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
/* Hostname verification is supported in OpenSSL 1.0.2 and newer. */
param = SSL_get0_param(tls->ssl);
/*
* Allow only complete wildcards. RFC 6125 discourages wildcard usage
* completely, and lists internationalized domain names as a reason
* against partial wildcards.
* See https://tools.ietf.org/html/rfc6125#section-7.2 for more
* information.
*/
X509_VERIFY_PARAM_set_hostflags(param,
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, conn->domain, 0);
#endif
ret = SSL_set_fd(tls->ssl, conn->sock);
if (ret <= 0) if (ret <= 0)
goto err_free_ssl; goto err_free_ssl;
} }
@@ -97,8 +298,8 @@ err_free_ssl:
err_free_ctx: err_free_ctx:
SSL_CTX_free(tls->ssl_ctx); SSL_CTX_free(tls->ssl_ctx);
err: err:
xmpp_free(ctx, tls); xmpp_free(conn->ctx, tls);
_tls_log_error(ctx); _tls_log_error(conn->ctx);
return NULL; return NULL;
} }
@@ -111,6 +312,8 @@ void tls_free(tls_t *tls)
int tls_set_credentials(tls_t *tls, const char *cafilename) int tls_set_credentials(tls_t *tls, const char *cafilename)
{ {
UNUSED(tls);
UNUSED(cafilename);
return -1; return -1;
} }
@@ -118,6 +321,7 @@ int tls_start(tls_t *tls)
{ {
int error; int error;
int ret; int ret;
long x509_res;
/* Since we're non-blocking, loop the connect call until it /* Since we're non-blocking, loop the connect call until it
succeeds or fails */ succeeds or fails */
@@ -134,8 +338,18 @@ int tls_start(tls_t *tls)
/* success or fatal error */ /* success or fatal error */
break; break;
} }
_tls_set_error(tls, error);
x509_res = SSL_get_verify_result(tls->ssl);
if (x509_res == X509_V_OK) {
xmpp_debug(tls->ctx, "tls", "Certificate verification passed");
} else {
xmpp_debug(tls->ctx, "tls",
"Certificate verification FAILED, result=%s(%ld)",
TLS_ERROR_STR((int)x509_res, cert_errors), x509_res);
}
_tls_dump_cert_info(tls);
_tls_set_error(tls, error);
return ret <= 0 ? 0 : 1; return ret <= 0 ? 0 : 1;
} }
@@ -145,6 +359,11 @@ int tls_stop(tls_t *tls)
int error; int error;
int ret; int ret;
/* According to OpenSSL.org, we must not call SSL_shutdown(3)
if a previous fatal error has occurred on a connection. */
if (tls->lasterror == SSL_ERROR_SYSCALL || tls->lasterror == SSL_ERROR_SSL)
return 1;
while (1) { while (1) {
++retries; ++retries;
ret = SSL_shutdown(tls->ssl); ret = SSL_shutdown(tls->ssl);
@@ -155,6 +374,14 @@ int tls_stop(tls_t *tls)
} }
_tls_sock_wait(tls, error); _tls_sock_wait(tls, error);
} }
if (error == SSL_ERROR_SYSCALL && errno == 0) {
/*
* Handle special case when peer closes connection instead of
* proper shutdown.
*/
error = 0;
ret = 1;
}
_tls_set_error(tls, error); _tls_set_error(tls, error);
return ret <= 0 ? 0 : 1; return ret <= 0 ? 0 : 1;
@@ -162,10 +389,9 @@ int tls_stop(tls_t *tls)
int tls_is_recoverable(int error) int tls_is_recoverable(int error)
{ {
return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ ||
|| error == SSL_ERROR_WANT_WRITE error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_CONNECT ||
|| error == SSL_ERROR_WANT_CONNECT error == SSL_ERROR_WANT_ACCEPT);
|| error == SSL_ERROR_WANT_ACCEPT);
} }
int tls_pending(tls_t *tls) int tls_pending(tls_t *tls)
@@ -195,6 +421,7 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(tls_t *tls)
{ {
UNUSED(tls);
return 0; return 0;
} }
@@ -206,14 +433,18 @@ static void _tls_sock_wait(tls_t *tls, int error)
int nfds; int nfds;
int ret; int ret;
if (error == SSL_ERROR_NONE)
return;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_ZERO(&wfds); FD_ZERO(&wfds);
if (error == SSL_ERROR_WANT_READ) if (error == SSL_ERROR_WANT_READ)
FD_SET(tls->sock, &rfds); FD_SET(tls->sock, &rfds);
if (error == SSL_ERROR_WANT_WRITE) if (error == SSL_ERROR_WANT_WRITE)
FD_SET(tls->sock, &wfds); FD_SET(tls->sock, &wfds);
nfds = (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) ? nfds = (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
tls->sock + 1 : 0; ? tls->sock + 1
: 0;
do { do {
tv.tv_sec = TLS_TIMEOUT_SEC; tv.tv_sec = TLS_TIMEOUT_SEC;
tv.tv_usec = TLS_TIMEOUT_USEC; tv.tv_usec = TLS_TIMEOUT_USEC;
@@ -221,9 +452,16 @@ static void _tls_sock_wait(tls_t *tls, int error)
} while (ret == -1 && errno == EINTR); } while (ret == -1 && errno == EINTR);
} }
static const char *_tls_error_str(int error, const char **tbl, size_t tbl_size)
{
return (error >= 0 && (size_t)error < tbl_size) ? tbl[error] : "UNKNOWN";
}
static void _tls_set_error(tls_t *tls, int error) static void _tls_set_error(tls_t *tls, int error)
{ {
if (error != 0 && !tls_is_recoverable(error)) { if (error != 0 && !tls_is_recoverable(error)) {
xmpp_debug(tls->ctx, "tls", "error=%s(%d) errno=%d",
TLS_ERROR_STR(error, tls_errors), error, errno);
_tls_log_error(tls->ctx); _tls_log_error(tls->ctx);
} }
tls->lasterror = error; tls->lasterror = error;
@@ -242,3 +480,26 @@ static void _tls_log_error(xmpp_ctx_t *ctx)
} }
} while (e != 0); } while (e != 0);
} }
static void _tls_dump_cert_info(tls_t *tls)
{
X509 *cert;
char *name;
cert = SSL_get_peer_certificate(tls->ssl);
if (cert == NULL)
xmpp_debug(tls->ctx, "tls", "Certificate was not presented by peer");
else {
name = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
if (name != NULL) {
xmpp_debug(tls->ctx, "tls", "Subject=%s", name);
OPENSSL_free(name);
}
name = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
if (name != NULL) {
xmpp_debug(tls->ctx, "tls", "Issuer=%s", name);
OPENSSL_free(name);
}
X509_free(cert);
}
}

View File

@@ -23,6 +23,7 @@
struct _tls { struct _tls {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
sock_t sock; sock_t sock;
HANDLE hsec32; HANDLE hsec32;
@@ -60,8 +61,10 @@ void tls_shutdown(void)
return; return;
} }
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) tls_t *tls_new(xmpp_conn_t *conn)
{ {
xmpp_ctx_t *ctx = conn->ctx;
sock_t sock = conn->sock;
tls_t *tls; tls_t *tls;
PSecurityFunctionTable (*pInitSecurityInterface)(void); PSecurityFunctionTable (*pInitSecurityInterface)(void);
SCHANNEL_CRED scred; SCHANNEL_CRED scred;
@@ -92,6 +95,7 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
memset(tls, 0, sizeof(*tls)); memset(tls, 0, sizeof(*tls));
tls->ctx = ctx; tls->ctx = ctx;
tls->conn = conn;
tls->sock = sock; tls->sock = sock;
if (!(tls->hsec32 = LoadLibrary("secur32.dll"))) { if (!(tls->hsec32 = LoadLibrary("secur32.dll"))) {
@@ -114,8 +118,7 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi)); ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi));
if (ret != SEC_E_OK) if (ret != SEC_E_OK) {
{
tls_free(tls); tls_free(tls);
return NULL; return NULL;
} }
@@ -136,11 +139,11 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
(void)algs; (void)algs;
#endif #endif
ret = tls->sft->AcquireCredentialsHandleA(NULL, UNISP_NAME, ret = tls->sft->AcquireCredentialsHandleA(
SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL, &(tls->hcred), NULL); NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL,
&(tls->hcred), NULL);
if (ret != SEC_E_OK) if (ret != SEC_E_OK) {
{
tls_free(tls); tls_free(tls);
return NULL; return NULL;
} }
@@ -151,23 +154,23 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
/* This bunch of queries should trip up wine until someone fixes /* This bunch of queries should trip up wine until someone fixes
* schannel support there */ * schannel support there */
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa); ret = tls->sft->QueryCredentialsAttributes(
if (ret != SEC_E_OK) &(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa);
{ if (ret != SEC_E_OK) {
tls_free(tls); tls_free(tls);
return NULL; return NULL;
} }
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs); ret = tls->sft->QueryCredentialsAttributes(
if (ret != SEC_E_OK) &(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs);
{ if (ret != SEC_E_OK) {
tls_free(tls); tls_free(tls);
return NULL; return NULL;
} }
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp); ret = tls->sft->QueryCredentialsAttributes(
if (ret != SEC_E_OK) &(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp);
{ if (ret != SEC_E_OK) {
tls_free(tls); tls_free(tls);
return NULL; return NULL;
} }
@@ -206,6 +209,8 @@ void tls_free(tls_t *tls)
int tls_set_credentials(tls_t *tls, const char *cafilename) int tls_set_credentials(tls_t *tls, const char *cafilename)
{ {
UNUSED(tls);
UNUSED(cafilename);
return -1; return -1;
} }
@@ -216,29 +221,15 @@ int tls_start(tls_t *tls)
SecBuffer sbin[2], sbout[1]; SecBuffer sbin[2], sbout[1];
SECURITY_STATUS ret; SECURITY_STATUS ret;
int sent; int sent;
char *name = NULL; char *name;
/* search the ctx's conns for our sock, and use the domain there as our /* use the domain there as our name */
* name */ name = tls->conn->domain;
{
xmpp_connlist_t *listentry = tls->ctx->connlist;
while (listentry) { ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
xmpp_conn_t *conn = listentry->conn; ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM |
if (conn->sock == tls->sock) { ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
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;
memset(&(sbout[0]), 0, sizeof(sbout[0])); memset(&(sbout[0]), 0, sizeof(sbout[0]));
sbout[0].BufferType = SECBUFFER_TOKEN; sbout[0].BufferType = SECBUFFER_TOKEN;
@@ -261,14 +252,22 @@ int tls_start(tls_t *tls)
sbdin.cBuffers = 2; sbdin.cBuffers = 2;
sbdin.pBuffers = sbin; sbdin.pBuffers = sbin;
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), NULL, name, ctxtreq, 0, 0, ret = tls->sft->InitializeSecurityContextA(
NULL, 0, &(tls->hctxt), &sbdout, &(tls->hcred), NULL, name, ctxtreq, 0, 0, NULL, 0, &(tls->hctxt),
&ctxtattr, NULL); &sbdout, &ctxtattr, NULL);
while (ret == SEC_I_CONTINUE_NEEDED
|| ret == SEC_I_INCOMPLETE_CREDENTIALS) {
unsigned char *p = sbin[0].pvBuffer; unsigned char *p = sbin[0].pvBuffer;
int len = 0, inbytes = 0; int len = 0;
while (ret == SEC_I_CONTINUE_NEEDED ||
ret == SEC_I_INCOMPLETE_CREDENTIALS ||
ret == SEC_E_INCOMPLETE_MESSAGE) {
int inbytes = 0;
if (ret != SEC_E_INCOMPLETE_MESSAGE) {
len = 0;
p = sbin[0].pvBuffer;
}
if (sbdout.pBuffers[0].cbBuffer) { if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer; unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
@@ -277,9 +276,7 @@ int tls_start(tls_t *tls)
sent = sock_write(tls->sock, writebuff, writelen); sent = sock_write(tls->sock, writebuff, writelen);
if (sent == -1) { if (sent == -1) {
tls->lasterror = sock_error(); tls->lasterror = sock_error();
} } else {
else
{
writebuff += sent; writebuff += sent;
writelen -= sent; writelen -= sent;
} }
@@ -321,20 +318,16 @@ int tls_start(tls_t *tls)
if (inbytes > 0) { if (inbytes > 0) {
len += inbytes; len += inbytes;
p += inbytes; p += inbytes;
} } else {
else
{
tls->lasterror = sock_error(); tls->lasterror = sock_error();
} }
} }
sbin[0].cbBuffer = len; sbin[0].cbBuffer = len;
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), &(tls->hctxt), name, ret = tls->sft->InitializeSecurityContextA(
ctxtreq, 0, 0, &sbdin, 0, &(tls->hcred), &(tls->hctxt), name, ctxtreq, 0, 0, &sbdin, 0,
&(tls->hctxt), &sbdout, &(tls->hctxt), &sbdout, &ctxtattr, NULL);
&ctxtattr, NULL);
} }
if (ret == SEC_E_OK) { if (ret == SEC_E_OK) {
@@ -344,9 +337,7 @@ int tls_start(tls_t *tls)
sent = sock_write(tls->sock, writebuff, writelen); sent = sock_write(tls->sock, writebuff, writelen);
if (sent == -1) { if (sent == -1) {
tls->lasterror = sock_error(); tls->lasterror = sock_error();
} } else {
else
{
writebuff += sent; writebuff += sent;
writelen -= sent; writelen -= sent;
} }
@@ -360,20 +351,21 @@ int tls_start(tls_t *tls)
if (ret != SEC_E_OK) { if (ret != SEC_E_OK) {
tls->lasterror = ret; tls->lasterror = ret;
xmpp_error(tls->ctx, "TLSS", "Schannel error 0x%lx", (unsigned long)ret); xmpp_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
(unsigned long)ret);
return 0; return 0;
} }
tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES, tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES,
&(tls->spcss)); &(tls->spcss));
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
+ tls->spcss.cbTrailer; tls->spcss.cbTrailer;
tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen); tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen);
tls->recvbufferpos = 0; tls->recvbufferpos = 0;
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
+ tls->spcss.cbTrailer; tls->spcss.cbTrailer;
tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen); tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen);
tls->sendbufferpos = 0; tls->sendbufferpos = 0;
tls->sendbufferlen = 0; tls->sendbufferlen = 0;
@@ -387,6 +379,7 @@ int tls_start(tls_t *tls)
int tls_stop(tls_t *tls) int tls_stop(tls_t *tls)
{ {
UNUSED(tls);
return -1; return -1;
} }
@@ -397,12 +390,13 @@ int tls_error(tls_t *tls)
int tls_is_recoverable(int error) int tls_is_recoverable(int error)
{ {
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
|| error == WSAEWOULDBLOCK || error == WSAEMSGSIZE error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
|| error == WSAEINPROGRESS); error == WSAEINPROGRESS);
} }
int tls_pending(tls_t *tls) { int tls_pending(tls_t *tls)
{
// There are 3 cases: // There are 3 cases:
// - there is data in ready buffer, so it is by default pending // - 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 // - there is data in recv buffer. If it is not decrypted yet, means it
@@ -423,8 +417,7 @@ int tls_read(tls_t *tls, void * const buff, const size_t len)
int bytes; int bytes;
/* first, if we've got some ready data, put that in the buffer */ /* first, if we've got some ready data, put that in the buffer */
if (tls->readybufferpos < tls->readybufferlen) if (tls->readybufferpos < tls->readybufferlen) {
{
if (len < tls->readybufferlen - tls->readybufferpos) { if (len < tls->readybufferlen - tls->readybufferpos) {
bytes = len; bytes = len;
} else { } else {
@@ -521,8 +514,7 @@ int tls_read(tls_t *tls, void * const buff, const size_t len)
return -1; return -1;
} else if (ret == SEC_I_RENEGOTIATE) { } else if (ret == SEC_I_RENEGOTIATE) {
ret = tls_start(tls); ret = tls_start(tls);
if (!ret) if (!ret) {
{
return -1; return -1;
} }
@@ -544,8 +536,7 @@ int tls_read(tls_t *tls, void * const buff, const size_t len)
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(tls_t *tls)
{ {
if (tls->sendbufferpos < tls->sendbufferlen) if (tls->sendbufferpos < tls->sendbufferlen) {
{
int bytes; int bytes;
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos, bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
@@ -603,23 +594,22 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
sbenc[1].pvBuffer = tls->sendbuffer + tls->spcss.cbHeader; sbenc[1].pvBuffer = tls->sendbuffer + tls->spcss.cbHeader;
while (remain > 0) while (remain > 0) {
{
if (remain > tls->spcss.cbMaximumMessage) { if (remain > tls->spcss.cbMaximumMessage) {
sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage; sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage;
} else { } else {
sbenc[1].cbBuffer = remain; sbenc[1].cbBuffer = remain;
} }
sbenc[2].pvBuffer = (unsigned char *)sbenc[1].pvBuffer sbenc[2].pvBuffer =
+ sbenc[1].cbBuffer; (unsigned char *)sbenc[1].pvBuffer + sbenc[1].cbBuffer;
sbenc[2].cbBuffer = tls->spcss.cbTrailer; sbenc[2].cbBuffer = tls->spcss.cbTrailer;
memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer); memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer);
p += tls->spcss.cbMaximumMessage; p += tls->spcss.cbMaximumMessage;
tls->sendbufferlen = sbenc[0].cbBuffer + sbenc[1].cbBuffer tls->sendbufferlen =
+ sbenc[2].cbBuffer; 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);
@@ -647,7 +637,6 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) { if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
return sent; return sent;
} }
} }
return sent; return sent;

View File

@@ -86,8 +86,24 @@ char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
*/ */
uint64_t time_stamp(void) uint64_t time_stamp(void)
{ {
#ifdef _WIN32 #if defined(_WIN32) || defined(_XBOX_ONE)
return timeGetTime();
#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 #else
struct timeval tv; struct timeval tv;
@@ -112,6 +128,20 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
return (uint64_t)(t2 - t1); return (uint64_t)(t2 - t1);
} }
/** Disconnect the stream with the error.
* This is a convenience function used internally by various parts of
* the Strophe library for terminating the connection because of an error.
*
* @param conn a Strophe connection object
* @param error an error code
*/
void disconnect_with_error(xmpp_conn_t *const conn, int error)
{
xmpp_error(conn->ctx, "xmpp", "Disconnecting with error %d", error);
conn->error = conn->error ?: error;
xmpp_disconnect(conn);
}
/** Disconnect the stream with a memory error. /** Disconnect the stream with a memory error.
* This is a convenience function used internally by various parts of * 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
@@ -122,5 +152,5 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
void disconnect_mem_error(xmpp_conn_t *const conn) void disconnect_mem_error(xmpp_conn_t *const conn)
{ {
xmpp_error(conn->ctx, "xmpp", "Memory allocation error"); xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
xmpp_disconnect(conn); disconnect_with_error(conn, XMPP_EMEM);
} }

View File

@@ -18,6 +18,13 @@
#include "ostypes.h" #include "ostypes.h"
#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 */ /* string functions */
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr); char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);

141
strophe.h
View File

@@ -39,6 +39,10 @@ extern "C" {
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'. * Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'.
*/ */
#define XMPP_NS_STREAMS_IETF "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 /** @def XMPP_NS_TLS
* Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'. * Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'.
*/ */
@@ -71,6 +75,10 @@ extern "C" {
* Namespace definition for 'jabber:iq:roster'. * Namespace definition for 'jabber:iq:roster'.
*/ */
#define XMPP_NS_ROSTER "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"
/* error defines */ /* error defines */
/** @def XMPP_EOK /** @def XMPP_EOK
@@ -95,6 +103,26 @@ extern "C" {
* Internal failure error code. * Internal failure error code.
*/ */
#define XMPP_EINT -3 #define XMPP_EINT -3
/** @def XMPP_ECERT
* Certificate verification failed during TLS establishment.
*/
#define XMPP_ECERT -4
/** @def XMPP_ETIMEDOUT
* Operation timed out error code.
*/
#define XMPP_ETIMEDOUT -5
/** @def XMPP_EIO
* I/O error code.
*/
#define XMPP_EIO -6
/** @def XMPP_ERESET
* Error code for situation when connection closed by remote side.
*/
#define XMPP_ERESET -7
/** @def XMPP_EAUTH
* Authentication failed error code.
*/
#define XMPP_EAUTH -8
/* initialization and shutdown */ /* initialization and shutdown */
void xmpp_initialize(void); void xmpp_initialize(void);
@@ -136,11 +164,7 @@ typedef enum {
XMPP_LEVEL_ERROR XMPP_LEVEL_ERROR
} xmpp_log_level_t; } xmpp_log_level_t;
typedef enum { typedef enum { XMPP_UNKNOWN, XMPP_CLIENT, XMPP_COMPONENT } xmpp_conn_type_t;
XMPP_UNKNOWN,
XMPP_CLIENT,
XMPP_COMPONENT
} xmpp_conn_type_t;
typedef void (*xmpp_log_handler)(void *const userdata, typedef void (*xmpp_log_handler)(void *const userdata,
const xmpp_log_level_t level, const xmpp_log_level_t level,
@@ -165,6 +189,14 @@ typedef struct _xmpp_stanza_t xmpp_stanza_t;
#define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0) #define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0)
#define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1) #define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1)
#define XMPP_CONN_FLAG_LEGACY_SSL (1UL << 2) #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)
/* connect callback */ /* connect callback */
typedef enum { typedef enum {
@@ -213,6 +245,9 @@ typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn,
xmpp_stream_error_t *const stream_error, xmpp_stream_error_t *const stream_error,
void *const userdata); void *const userdata);
void xmpp_send_error(xmpp_conn_t *const conn,
xmpp_error_type_t const type,
char *const text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *const ctx); xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *const ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *const conn); xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *const conn);
int xmpp_conn_release(xmpp_conn_t *const conn); int xmpp_conn_release(xmpp_conn_t *const conn);
@@ -227,7 +262,12 @@ 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); xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *const conn);
void xmpp_conn_disable_tls(xmpp_conn_t *const conn); void xmpp_conn_disable_tls(xmpp_conn_t *const conn);
int xmpp_conn_is_secured(xmpp_conn_t *const conn); int xmpp_conn_is_secured(xmpp_conn_t *const conn);
void xmpp_conn_set_keepalive(xmpp_conn_t * const conn, int timeout, int interval); void xmpp_conn_set_keepalive(xmpp_conn_t *const conn,
int timeout,
int interval);
int xmpp_conn_is_connecting(xmpp_conn_t *const conn);
int xmpp_conn_is_connected(xmpp_conn_t *const conn);
int xmpp_conn_is_disconnected(xmpp_conn_t *const conn);
int xmpp_connect_client(xmpp_conn_t *const conn, int xmpp_connect_client(xmpp_conn_t *const conn,
const char *const altdomain, const char *const altdomain,
@@ -235,8 +275,10 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *const userdata); void *const userdata);
int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, int xmpp_connect_component(xmpp_conn_t *const conn,
unsigned short port, xmpp_conn_handler callback, const char *const server,
unsigned short port,
xmpp_conn_handler callback,
void *const userdata); void *const userdata);
int xmpp_connect_raw(xmpp_conn_t *const conn, int xmpp_connect_raw(xmpp_conn_t *const conn,
@@ -245,24 +287,23 @@ int xmpp_connect_raw(xmpp_conn_t * const conn,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void *const userdata); void *const userdata);
int xmpp_conn_open_stream_default(xmpp_conn_t *const conn); int xmpp_conn_open_stream_default(xmpp_conn_t *const conn);
int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes, int xmpp_conn_open_stream(xmpp_conn_t *const conn,
char **attributes,
size_t attributes_len); size_t attributes_len);
int xmpp_conn_tls_start(xmpp_conn_t *const conn); int xmpp_conn_tls_start(xmpp_conn_t *const conn);
void xmpp_disconnect(xmpp_conn_t *const conn); void xmpp_disconnect(xmpp_conn_t *const conn);
void xmpp_send(xmpp_conn_t * const conn, void xmpp_send(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza);
xmpp_stanza_t * const stanza);
void xmpp_send_raw_string(xmpp_conn_t * const conn, void xmpp_send_raw_string(xmpp_conn_t *const conn, const char *const fmt, ...);
const char * const fmt, ...);
void xmpp_send_raw(xmpp_conn_t *const conn, void xmpp_send_raw(xmpp_conn_t *const conn,
const char * const data, const size_t len); const char *const data,
const size_t len);
/* handlers */ /* handlers */
/* if the handle returns false it is removed */ /* if the handler returns false it is removed */
typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn, typedef int (*xmpp_timed_handler)(xmpp_conn_t *const conn,
void *const userdata); void *const userdata);
@@ -273,6 +314,16 @@ void xmpp_timed_handler_add(xmpp_conn_t * const conn,
void xmpp_timed_handler_delete(xmpp_conn_t *const conn, void xmpp_timed_handler_delete(xmpp_conn_t *const conn,
xmpp_timed_handler handler); xmpp_timed_handler handler);
/* if the handler returns false it is removed */
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *const ctx,
void *const userdata);
void xmpp_global_timed_handler_add(xmpp_ctx_t *const ctx,
xmpp_global_timed_handler handler,
const unsigned long period,
void *const userdata);
void xmpp_global_timed_handler_delete(xmpp_ctx_t *const ctx,
xmpp_global_timed_handler handler);
/* if the handler returns false it is removed */ /* if the handler returns false it is removed */
typedef int (*xmpp_handler)(xmpp_conn_t *const conn, typedef int (*xmpp_handler)(xmpp_conn_t *const conn,
@@ -285,8 +336,7 @@ void xmpp_handler_add(xmpp_conn_t * const conn,
const char *const name, const char *const name,
const char *const type, const char *const type,
void *const userdata); void *const userdata);
void xmpp_handler_delete(xmpp_conn_t * const conn, void xmpp_handler_delete(xmpp_conn_t *const conn, xmpp_handler handler);
xmpp_handler handler);
void xmpp_id_handler_add(xmpp_conn_t *const conn, void xmpp_id_handler_add(xmpp_conn_t *const conn,
xmpp_handler handler, xmpp_handler handler,
@@ -304,6 +354,7 @@ void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
/* allocate and 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(xmpp_ctx_t *ctx);
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str);
/* clone a stanza */ /* clone a stanza */
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza); xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *const stanza);
@@ -314,26 +365,36 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza);
/* free a stanza object and it's contents */ /* free a stanza object and it's contents */
int xmpp_stanza_release(xmpp_stanza_t *const stanza); int xmpp_stanza_release(xmpp_stanza_t *const stanza);
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *const stanza);
int xmpp_stanza_is_text(xmpp_stanza_t *const 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_tag(xmpp_stanza_t *const stanza);
/* marshall a stanza into text for transmission or display */ /* marshall a stanza into text for transmission or display */
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
char ** const buf, size_t * const buflen); char **const buf,
size_t *const buflen);
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *const stanza); 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, xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *const stanza,
const char *const name); const char *const name);
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza, xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *const stanza,
const char *const ns); const char *const ns);
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *const stanza,
const char *const name,
const char *const ns);
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza); xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *const stanza);
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child); int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
xmpp_stanza_t *child,
int do_clone);
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza, const char *xmpp_stanza_get_attribute(xmpp_stanza_t *const stanza,
const char *const name); const char *const name);
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza); int xmpp_stanza_get_attribute_count(xmpp_stanza_t *const stanza);
int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza, int xmpp_stanza_get_attributes(xmpp_stanza_t *const stanza,
const char **attr, int attrlen); const char **attr,
int attrlen);
/* concatenate all child text nodes. this function /* concatenate all child text nodes. this function
* returns a string that must be freed by the caller */ * 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(xmpp_stanza_t *const stanza);
@@ -343,10 +404,8 @@ const char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza, int xmpp_stanza_set_attribute(xmpp_stanza_t *const stanza,
const char *const key, const char *const key,
const char *const value); const char *const value);
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *const name);
const char * const name); int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *const text);
int xmpp_stanza_set_text(xmpp_stanza_t *stanza,
const char * const text);
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza, int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
const char *const text, const char *const text,
const size_t size); const size_t size);
@@ -367,21 +426,31 @@ int xmpp_stanza_set_from(xmpp_stanza_t * const stanza, const char * const from);
/* allocate and initialize a stanza in reply to another */ /* allocate and initialize a stanza in reply to another */
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza); xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *const stanza);
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *const stanza,
const char *const error_type,
const char *const condition,
const char *const text);
/* stanza subclasses */ /* stanza subclasses */
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx, const char * const type, xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
const char * const to, const char * const id); const char *const type,
const char *const to,
const char *const id);
char *xmpp_message_get_body(xmpp_stanza_t *msg); char *xmpp_message_get_body(xmpp_stanza_t *msg);
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text); int xmpp_message_set_body(xmpp_stanza_t *msg, const char *const text);
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type, xmpp_stanza_t *
const char * const id); xmpp_iq_new(xmpp_ctx_t *ctx, const char *const type, const char *const id);
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx); xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx);
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx,
xmpp_error_type_t const type,
const char *const text);
/* jid */ /* jid */
/* these return new strings that must be xmpp_free()'d */ /* these return new strings that must be xmpp_free()'d */
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node, char *xmpp_jid_new(xmpp_ctx_t *ctx,
const char *node,
const char *domain, const char *domain,
const char *resource); const char *resource);
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid); char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
@@ -394,6 +463,7 @@ char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout); void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
void xmpp_run(xmpp_ctx_t *ctx); void xmpp_run(xmpp_ctx_t *ctx);
void xmpp_stop(xmpp_ctx_t *ctx); void xmpp_stop(xmpp_ctx_t *ctx);
void xmpp_ctx_set_timeout(xmpp_ctx_t *const ctx, const unsigned long timeout);
/* UUID */ /* UUID */
@@ -409,6 +479,9 @@ char *xmpp_uuid_gen(xmpp_ctx_t *ctx);
typedef struct _xmpp_sha1_t xmpp_sha1_t; typedef struct _xmpp_sha1_t xmpp_sha1_t;
char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len); 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); xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx);
void xmpp_sha1_free(xmpp_sha1_t *sha1); void xmpp_sha1_free(xmpp_sha1_t *sha1);
@@ -420,10 +493,14 @@ void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
/* Base64 */ /* Base64 */
char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len); 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); 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, void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
unsigned char **out, size_t *outlen); const char *base64,
size_t len,
unsigned char **out,
size_t *outlen);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -1,133 +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 program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @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__ */

View File

@@ -10,31 +10,42 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <check.h> #include "strophe.h"
#include <strophe.h>
#include "parser.h" #include "parser.h"
#include "test.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; xmpp_ctx_t *ctx;
parser_t *parser; parser_t *parser;
ctx = xmpp_ctx_new(NULL, NULL); ctx = xmpp_ctx_new(NULL, NULL);
parser = parser_new(ctx, NULL, NULL, NULL, NULL); parser = parser_new(ctx, NULL, NULL, NULL, NULL);
fail_unless(parser != NULL, "Parser creation failed."); fail_unless(parser != NULL);
parser_free(parser); parser_free(parser);
xmpp_ctx_free(ctx); xmpp_ctx_free(ctx);
} }
END_TEST
int cbtest_got_start = 0; int cbtest_got_start = 0;
void cbtest_handle_start(char *name, char **attrs, void *userdata) void cbtest_handle_start(char *name, char **attrs, void *userdata)
{ {
(void)attrs;
(void)userdata;
if (strcmp(name, "stream") == 0) if (strcmp(name, "stream") == 0)
cbtest_got_start = 1; cbtest_got_start = 1;
} }
@@ -42,6 +53,8 @@ void cbtest_handle_start(char *name, char **attrs, void *userdata)
int cbtest_got_end = 0; int cbtest_got_end = 0;
void cbtest_handle_end(char *name, void *userdata) void cbtest_handle_end(char *name, void *userdata)
{ {
(void)userdata;
if (strcmp(name, "stream") == 0) if (strcmp(name, "stream") == 0)
cbtest_got_end = 1; cbtest_got_end = 1;
} }
@@ -49,20 +62,20 @@ void cbtest_handle_end(char *name, void *userdata)
int cbtest_got_stanza = 0; int cbtest_got_stanza = 0;
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata) void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
{ {
(void)userdata;
if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0) if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0)
cbtest_got_stanza = 1; cbtest_got_stanza = 1;
} }
START_TEST(callbacks) static void callbacks(void)
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
parser_t *parser; parser_t *parser;
int ret; int ret;
ctx = xmpp_ctx_new(NULL, NULL); ctx = xmpp_ctx_new(NULL, NULL);
parser = parser_new(ctx, parser = parser_new(ctx, cbtest_handle_start, cbtest_handle_end,
cbtest_handle_start,
cbtest_handle_end,
cbtest_handle_stanza, NULL); cbtest_handle_stanza, NULL);
ret = parser_feed(parser, "<stream>", 8); ret = parser_feed(parser, "<stream>", 8);
@@ -79,16 +92,18 @@ START_TEST(callbacks)
parser_free(parser); parser_free(parser);
xmpp_ctx_free(ctx); xmpp_ctx_free(ctx);
} }
END_TEST
Suite *parser_suite(void) int main()
{ {
Suite *s = suite_create("Parser"); printf("XML parser tests.\n");
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;
}
TEST_MAIN printf("create-destroy: ");
create_destroy();
printf("ok\n");
printf("callbacks: ");
callbacks();
printf("ok\n");
return 0;
}

View File

@@ -55,13 +55,13 @@ int main(int argc, char **argv)
} }
domain = argv[1]; domain = argv[1];
snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service, proto,
service, proto, domain); domain);
errno = 0; errno = 0;
len = res_query(fulldomain, C_IN, T_SRV, buf, sizeof(buf)); len = res_query(fulldomain, C_IN, T_SRV, buf, sizeof(buf));
if (len < 0) { if (len < 0) {
fprintf(stderr, "res_query(): Error occured (errno=%d)\n", errno); fprintf(stderr, "res_query(): Error occurred (errno=%d)\n", errno);
} }
if (len == 0) { if (len == 0) {
fprintf(stderr, "res_query(): Empty result\n"); fprintf(stderr, "res_query(): Empty result\n");

View File

@@ -16,9 +16,9 @@
static uint8_t char_to_bin(char c) static uint8_t char_to_bin(char c)
{ {
return c <= '9' ? (uint8_t)(c - '0') : return c <= '9'
c <= 'Z' ? (uint8_t)(c - 'A' + 10) : ? (uint8_t)(c - '0')
(uint8_t)(c - 'a' + 10); : 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) void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len)

View File

@@ -18,7 +18,8 @@
#include "ostypes.h" #include "ostypes.h"
#define TEST_MAIN \ #define TEST_MAIN \
int main(int argc, char **argv) { \ int main(int argc, char **argv) \
{ \
int num_failed; \ int num_failed; \
Suite *s = parser_suite(); \ Suite *s = parser_suite(); \
SRunner *sr = srunner_create(s); \ SRunner *sr = srunner_create(s); \
@@ -51,14 +52,10 @@ do { \
const uint8_t *__v2 = (uint8_t *)(v2); \ const uint8_t *__v2 = (uint8_t *)(v2); \
size_t __len1 = len1; \ size_t __len1 = len1; \
size_t __len2 = len2; \ size_t __len2 = len2; \
if (__len1 != __len2 || \ if (__len1 != __len2 || memcmp(__v1, __v2, __len1) != 0) { \
memcmp(__v1, __v2, __len1) != 0) \
{ \
printf("%s differs!\n", #v1); \ printf("%s differs!\n", #v1); \
printf("expected: 0x%s\n", \ printf("expected: 0x%s\n", test_bin_to_hex(__v1, __len1)); \
test_bin_to_hex(__v1, __len1)); \ printf("got: 0x%s\n", test_bin_to_hex(__v2, __len2)); \
printf("got: 0x%s\n", \
test_bin_to_hex(__v2, __len2)); \
exit(1); \ exit(1); \
} \ } \
} while (0) } while (0)

View File

@@ -17,11 +17,11 @@
#include "test.h" #include "test.h"
static const unsigned char test_2_raw[] = static const unsigned char test_2_raw[] = {0x14, 0xfb, 0x9c, 0x03,
{0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e, 0x00}; 0xd9, 0x7e, 0x00};
static const unsigned char test_4_raw[] = static const unsigned char test_4_raw[] = {0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd,
{0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd, 0xf3, 0xd5, 0xf3, 0xd5, 0x41, 0xbc, 0x1b, 0xe9,
0x41, 0xbc, 0x1b, 0xe9, 0xdf, 0xe9, 0xb3, 0x08, 0x00}; 0xdf, 0xe9, 0xb3, 0x08, 0x00};
static const struct { static const struct {
char *raw; char *raw;
@@ -36,13 +36,11 @@ static const struct {
.base64 = "FPucA9l+", .base64 = "FPucA9l+",
}, },
{ {
.raw = .raw = "From rest and sleep, which but thy pictures be, "
"From rest and sleep, which but thy pictures be, "
"Much pleasure; then from thee much more must flow, " "Much pleasure; then from thee much more must flow, "
"And soonest our best men with thee do go, " "And soonest our best men with thee do go, "
"Rest of their bones, and soul's delivery.", "Rest of their bones, and soul's delivery.",
.base64 = .base64 = "RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
"RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt" "cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp" "b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv" "dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
@@ -53,11 +51,9 @@ static const struct {
.base64 = "1i8nSX7d89VBvBvp3+mzCA==", .base64 = "1i8nSX7d89VBvBvp3+mzCA==",
}, },
{ {
.raw = .raw = "realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83"
"realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83"
"a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess", "a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
.base64 = .base64 = "cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
"cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo" "YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz", "YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz",
}, },
@@ -94,48 +90,36 @@ static const struct {
}; };
static const unsigned char bin_data[] = { static const unsigned char bin_data[] = {
0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x6d, 0x61, 0x69, 0x6c,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00,
0x6d, 0x61, 0x69, 0x6c, 0x03, 0x63, 0x6f, 0x6d, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70,
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c,
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x74, 0x34, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f,
0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00,
0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x04, 0x78, 0x6d, 0x70,
0x66, 0x04, 0x61, 0x6c, 0x74, 0x34, 0x04, 0x78, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x43, 0x00, 0x19, 0x00, 0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d,
0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61,
0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x6c, 0x74, 0x33, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
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,
}; };
int main(int argc, char *argv[]) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
unsigned char *udec; unsigned char *udec;
char *dec; char *dec;
char *enc; char *enc;
size_t len; size_t len, i;
int i;
printf("BASE64 tests.\n"); printf("BASE64 tests.\n");

View File

@@ -1,5 +1,6 @@
/* test_ctx.c /* test_ctx.c
** libstrophe XMPP client library -- test routines for the library run-time context ** libstrophe XMPP client library -- test routines for the library run-time
*context
** **
** Copyright (C) 2005-2009 Collecta, Inc. ** Copyright (C) 2005-2009 Collecta, Inc.
** **
@@ -23,31 +24,39 @@ static int mem_realloc_called = 0;
void *my_alloc(const size_t size, void *const userdata) void *my_alloc(const size_t size, void *const userdata)
{ {
(void)userdata;
mem_alloc_called++; mem_alloc_called++;
return malloc(size); return malloc(size);
} }
void my_free(void *p, void *const userdata) void my_free(void *p, void *const userdata)
{ {
(void)userdata;
mem_free_called++; mem_free_called++;
return free(p); return free(p);
} }
void *my_realloc(void *p, const size_t size, void *const userdata) void *my_realloc(void *p, const size_t size, void *const userdata)
{ {
(void)userdata;
mem_realloc_called++; mem_realloc_called++;
return realloc(p, size); return realloc(p, size);
} }
void my_logger(void * const userdata, const xmpp_log_level_t level, void my_logger(void *const userdata,
const char * const area, const char * const msg) const xmpp_log_level_t level,
const char *const area,
const char *const msg)
{ {
if (strcmp((char *)userdata, "asdf") == 0 && level == XMPP_LEVEL_DEBUG if (strcmp((char *)userdata, "asdf") == 0 && level == XMPP_LEVEL_DEBUG &&
&& strcmp(area, "test") == 0 && strcmp(msg, "hello") == 0) strcmp(area, "test") == 0 && strcmp(msg, "hello") == 0)
log_called++; log_called++;
} }
int main(int argc, char **argv) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
xmpp_mem_t mymem; xmpp_mem_t mymem;
@@ -56,7 +65,8 @@ int main(int argc, char **argv)
void *testptr1, *testptr2; void *testptr1, *testptr2;
ctx = xmpp_ctx_new(NULL, NULL); ctx = xmpp_ctx_new(NULL, NULL);
if (ctx == NULL) return 1; if (ctx == NULL)
return 1;
/* destroy context */ /* destroy context */
xmpp_ctx_free(ctx); xmpp_ctx_free(ctx);

View File

@@ -16,18 +16,15 @@
#include "strophe.h" #include "strophe.h"
#include "common.h" #include "common.h"
#include "hash.h" #include "hash.h"
#include "test.h"
#define TABLESIZE 100 #define TABLESIZE 100
#define TESTSIZE 500 #define TESTSIZE 500
/* static test data */ /* static test data */
const int nkeys = 5; const char *keys[] = {"foo", "bar", "baz", "quux", "xyzzy"};
const char *keys[] = { const char *values[] = {"wuzzle", "mug", "canonical", "rosebud", "lottery"};
"foo", "bar", "baz", "quux", "xyzzy" const int nkeys = ARRAY_SIZE(keys);
};
const char *values[] = {
"wuzzle", "mug", "canonical", "rosebud", "lottery"
};
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -58,7 +55,7 @@ int main(int argc, char **argv)
} }
/* allocate a hash table */ /* allocate a hash table */
table = hash_new(ctx, TABLESIZE, NULL); table = hash_new(ctx, TABLESIZE, xmpp_free);
if (table == NULL) { if (table == NULL) {
/* table allocation failed! */ /* table allocation failed! */
return 1; return 1;
@@ -66,8 +63,9 @@ int main(int argc, char **argv)
/* test insertion */ /* test insertion */
for (i = 0; i < nkeys; i++) { for (i = 0; i < nkeys; i++) {
err = hash_add(table, keys[i], (void*)values[i]); err = hash_add(table, keys[i], xmpp_strdup(ctx, values[i]));
if (err) return err; if (err)
return err;
} }
/* test key count */ /* test key count */
@@ -76,6 +74,22 @@ int main(int argc, char **argv)
return 1; return 1;
} }
/* test replacing old values */
for (i = 0; i < nkeys; i++) {
err = hash_add(table, keys[0], xmpp_strdup(ctx, values[i]));
if (err)
return err;
if (hash_num_keys(table) != nkeys)
return 1;
result = hash_get(table, keys[0]);
if (result == NULL)
return 1;
if (strcmp(result, values[i]) != 0)
return 1;
}
/* restore value for the 1st key */
hash_add(table, keys[0], xmpp_strdup(ctx, values[0]));
/* test cloning */ /* test cloning */
clone = hash_clone(table); clone = hash_clone(table);
@@ -119,14 +133,19 @@ int main(int argc, char **argv)
/* test drops */ /* test drops */
hash_drop(clone, keys[2]); hash_drop(clone, keys[2]);
if (hash_get(clone, keys[2]) != NULL) return 1; if (hash_get(clone, keys[2]) != NULL)
return 1;
hash_drop(clone, keys[1]); hash_drop(clone, keys[1]);
hash_drop(clone, keys[4]); hash_drop(clone, keys[4]);
if (hash_get(clone, keys[4]) != NULL) return 1; if (hash_get(clone, keys[4]) != NULL)
if (hash_get(clone, keys[1]) != NULL) return 1; return 1;
if (hash_get(clone, keys[1]) != NULL)
return 1;
/* keys 0,3 should still be available */ /* keys 0,3 should still be available */
if (hash_get(clone, keys[0]) == NULL) return 1; if (hash_get(clone, keys[0]) == NULL)
if (hash_get(clone, keys[3]) == NULL) return 1; return 1;
if (hash_get(clone, keys[3]) == NULL)
return 1;
/* release our clone */ /* release our clone */
hash_release(clone); hash_release(clone);

View File

@@ -27,57 +27,102 @@ static const char *_s(const char *s)
int test_jid(xmpp_ctx_t *ctx) int test_jid(xmpp_ctx_t *ctx)
{ {
char *bare;
char *node; char *node;
char *domain; char *domain;
char *resource; char *resource;
bare = xmpp_jid_bare(ctx, jid1);
node = xmpp_jid_node(ctx, jid1); node = xmpp_jid_node(ctx, jid1);
domain = xmpp_jid_domain(ctx, jid1); domain = xmpp_jid_domain(ctx, jid1);
resource = xmpp_jid_resource(ctx, jid1); resource = xmpp_jid_resource(ctx, jid1);
printf("jid '%s' parsed to %s, %s, %s\n", printf("jid '%s' parsed to %s, %s, %s\n", jid1, _s(node), _s(domain),
jid1, _s(node), _s(domain), _s(resource)); _s(resource));
if (node == NULL || strcmp(node, "foo")) return 1; if (bare == NULL || strcmp(bare, "foo@bar.com"))
if (domain == NULL || strcmp(domain, "bar.com")) return 1; return 1;
if (resource != NULL) return 1; if (node == NULL || strcmp(node, "foo"))
if (node) xmpp_free(ctx, node); return 1;
if (domain) xmpp_free(ctx, domain); if (domain == NULL || strcmp(domain, "bar.com"))
if (resource) xmpp_free(ctx, resource); return 1;
if (resource != NULL)
return 1;
if (bare)
xmpp_free(ctx, bare);
if (node)
xmpp_free(ctx, node);
if (domain)
xmpp_free(ctx, domain);
if (resource)
xmpp_free(ctx, resource);
bare = xmpp_jid_bare(ctx, jid2);
node = xmpp_jid_node(ctx, jid2); node = xmpp_jid_node(ctx, jid2);
domain = xmpp_jid_domain(ctx, jid2); domain = xmpp_jid_domain(ctx, jid2);
resource = xmpp_jid_resource(ctx, jid2); resource = xmpp_jid_resource(ctx, jid2);
printf("jid '%s' parsed to %s, %s, %s\n", printf("jid '%s' parsed to %s, %s, %s\n", jid2, _s(node), _s(domain),
jid2, _s(node), _s(domain), _s(resource)); _s(resource));
if (node == NULL || strcmp(node, "anyone")) return 1; if (bare == NULL || strcmp(bare, "anyone@example.com"))
if (domain == NULL || strcmp(domain, "example.com")) return 1; return 1;
if (resource == NULL || strcmp(resource, "hullo")) return 1; if (node == NULL || strcmp(node, "anyone"))
if (node) xmpp_free(ctx, node); return 1;
if (domain) xmpp_free(ctx, domain); if (domain == NULL || strcmp(domain, "example.com"))
if (resource) xmpp_free(ctx, resource); return 1;
if (resource == NULL || strcmp(resource, "hullo"))
return 1;
if (bare)
xmpp_free(ctx, bare);
if (node)
xmpp_free(ctx, node);
if (domain)
xmpp_free(ctx, domain);
if (resource)
xmpp_free(ctx, resource);
bare = xmpp_jid_bare(ctx, jid3);
node = xmpp_jid_node(ctx, jid3); node = xmpp_jid_node(ctx, jid3);
domain = xmpp_jid_domain(ctx, jid3); domain = xmpp_jid_domain(ctx, jid3);
resource = xmpp_jid_resource(ctx, jid3); resource = xmpp_jid_resource(ctx, jid3);
printf("jid '%s' parsed to %s, %s, %s\n", printf("jid '%s' parsed to %s, %s, %s\n", jid3, _s(node), _s(domain),
jid3, _s(node), _s(domain), _s(resource)); _s(resource));
if (node == NULL || strcmp(node, "manic.porter")) return 1; if (bare == NULL || strcmp(bare, "manic.porter@xyz.net"))
if (domain == NULL || strcmp(domain, "xyz.net")) return 1; return 1;
if (resource == NULL || strcmp(resource, "frob")) return 1; if (node == NULL || strcmp(node, "manic.porter"))
if (node) xmpp_free(ctx, node); return 1;
if (domain) xmpp_free(ctx, domain); if (domain == NULL || strcmp(domain, "xyz.net"))
if (resource) xmpp_free(ctx, resource); return 1;
if (resource == NULL || strcmp(resource, "frob"))
return 1;
if (bare)
xmpp_free(ctx, bare);
if (node)
xmpp_free(ctx, node);
if (domain)
xmpp_free(ctx, domain);
if (resource)
xmpp_free(ctx, resource);
bare = xmpp_jid_bare(ctx, jid4);
node = xmpp_jid_node(ctx, jid4); node = xmpp_jid_node(ctx, jid4);
domain = xmpp_jid_domain(ctx, jid4); domain = xmpp_jid_domain(ctx, jid4);
resource = xmpp_jid_resource(ctx, jid4); resource = xmpp_jid_resource(ctx, jid4);
printf("jid '%s' parsed to %s, %s, %s\n", printf("jid '%s' parsed to %s, %s, %s\n", jid4, _s(node), _s(domain),
jid4, _s(node), _s(domain), _s(resource)); _s(resource));
if (node != NULL) return 1; if (bare == NULL || strcmp(bare, "domain.tld"))
if (domain == NULL || strcmp(domain, "domain.tld")) return 1; return 1;
if (resource != NULL) return 1; if (node != NULL)
if (node) xmpp_free(ctx, node); return 1;
if (domain) xmpp_free(ctx, domain); if (domain == NULL || strcmp(domain, "domain.tld"))
if (resource) xmpp_free(ctx, resource); return 1;
if (resource != NULL)
return 1;
if (bare)
xmpp_free(ctx, bare);
if (node)
xmpp_free(ctx, node);
if (domain)
xmpp_free(ctx, domain);
if (resource)
xmpp_free(ctx, resource);
return 0; return 0;
} }
@@ -88,38 +133,46 @@ int test_jid_new(xmpp_ctx_t *ctx)
jid = xmpp_jid_new(ctx, "node", "domain", "resource"); jid = xmpp_jid_new(ctx, "node", "domain", "resource");
printf("new jid: '%s'\n", jid); printf("new jid: '%s'\n", jid);
if (strcmp(jid, "node@domain/resource")) return 1; if (strcmp(jid, "node@domain/resource"))
return 1;
xmpp_free(ctx, jid); xmpp_free(ctx, jid);
jid = xmpp_jid_new(ctx, "foo", "bar.com", NULL); jid = xmpp_jid_new(ctx, "foo", "bar.com", NULL);
printf("new jid: '%s'\n", jid); printf("new jid: '%s'\n", jid);
if (strcmp(jid, "foo@bar.com")) return 1; if (strcmp(jid, "foo@bar.com"))
return 1;
xmpp_free(ctx, jid); xmpp_free(ctx, jid);
return 0; return 0;
} }
int main(int argc, char *argv[]) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
int ret; int ret;
printf("allocating context... "); printf("allocating context... ");
ctx = xmpp_ctx_new(NULL, NULL); ctx = xmpp_ctx_new(NULL, NULL);
if (ctx == NULL) printf("failed to create context\n"); if (ctx == NULL)
if (ctx == NULL) return -1; printf("failed to create context\n");
if (ctx == NULL)
return -1;
printf("ok.\n"); printf("ok.\n");
printf("testing jid routines...\n"); printf("testing jid routines...\n");
ret = test_jid(ctx); ret = test_jid(ctx);
if (ret) printf("testing jid routines... failed!\n"); if (ret)
if (ret) return ret; printf("testing jid routines... failed!\n");
if (ret)
return ret;
printf("testing jid routines... ok.\n"); printf("testing jid routines... ok.\n");
printf("testing jid new routines...\n"); printf("testing jid new routines...\n");
ret = test_jid_new(ctx); ret = test_jid_new(ctx);
if (ret) printf("testing jid new routines... failed!\n"); if (ret)
if (ret) return ret; printf("testing jid new routines... failed!\n");
if (ret)
return ret;
printf("testing jid new routines... ok.\n"); printf("testing jid new routines... ok.\n");
printf("freeing context... "); printf("freeing context... ");

View File

@@ -57,7 +57,7 @@ static const struct {
}, },
}; };
int main(int argc, char **argv) int main()
{ {
struct MD5Context ctx; struct MD5Context ctx;
unsigned char digest[16]; unsigned char digest[16];

View File

@@ -21,14 +21,29 @@
#include "rand.c" #include "rand.c"
/* stubs to build test without whole libstrophe */ /* stubs to build test without whole libstrophe */
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size) { void *xmpp_alloc(const xmpp_ctx_t *const ctx, const size_t size)
{
(void)ctx;
(void)size;
return NULL; return NULL;
} }
void xmpp_free(const xmpp_ctx_t * const ctx, void *p) { }
int xmpp_snprintf (char *str, size_t count, const char *fmt, ...) { void xmpp_free(const xmpp_ctx_t *const ctx, void *p)
{
(void)ctx;
(void)p;
}
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
{
(void)str;
(void)count;
(void)fmt;
return 0; return 0;
} }
uint64_t time_stamp(void) {
uint64_t time_stamp(void)
{
return 0; return 0;
} }
@@ -121,8 +136,8 @@ int main()
&entropy_input_len); &entropy_input_len);
test_hex_to_bin(test_vectors[i].nonce, nonce, &nonce_len); test_hex_to_bin(test_vectors[i].nonce, nonce, &nonce_len);
Hash_DRBG_Instantiate(&ctx, entropy_input, entropy_input_len, Hash_DRBG_Instantiate(&ctx, entropy_input, entropy_input_len, nonce,
nonce, nonce_len); nonce_len);
COMPARE(test_vectors[i].V1, test_bin_to_hex(ctx.V, sizeof(ctx.V))); COMPARE(test_vectors[i].V1, test_bin_to_hex(ctx.V, sizeof(ctx.V)));
COMPARE(test_vectors[i].C1, test_bin_to_hex(ctx.C, sizeof(ctx.C))); COMPARE(test_vectors[i].C1, test_bin_to_hex(ctx.C, sizeof(ctx.C)));
assert(ctx.reseed_counter == 1); assert(ctx.reseed_counter == 1);

View File

@@ -13,78 +13,58 @@
#include <stdio.h> #include <stdio.h>
#include "strophe.h" #include "strophe.h"
#include "rand.h"
#include "resolver.h" #include "resolver.h"
#include "test.h" #include "test.h"
/* res_query("_xmpp-client._tcp.jabber.kiev.ua", C_IN, T_SRV, ...) */ /* res_query("_xmpp-client._tcp.jabber.kiev.ua", C_IN, T_SRV, ...) */
static const unsigned char data1[] = { static const unsigned char data1[] = {
0x95, 0xf3, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x95, 0xf3, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x72, 0x04, 0x6b, 0x69, 0x65, 0x76, 0x02, 0x75, 0x61, 0x00, 0x00, 0x21,
0x61, 0x62, 0x62, 0x65, 0x72, 0x04, 0x6b, 0x69, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b,
0x65, 0x76, 0x02, 0x75, 0x61, 0x00, 0x00, 0x21, 0x00, 0x16, 0x00, 0x01, 0x00, 0x00, 0x14, 0x66, 0x06, 0x6a, 0x61, 0x62,
0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x62, 0x65, 0x72, 0x04, 0x6b, 0x69, 0x65, 0x76, 0x02, 0x75, 0x61, 0x00,
0x00, 0x00, 0x00, 0x3b, 0x00, 0x16, 0x00, 0x01,
0x00, 0x00, 0x14, 0x66, 0x06, 0x6a, 0x61, 0x62,
0x62, 0x65, 0x72, 0x04, 0x6b, 0x69, 0x65, 0x76,
0x02, 0x75, 0x61, 0x00,
}; };
/* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) */ /* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) */
static const unsigned char data2[] = { static const unsigned char data2[] = {
0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x72, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c,
0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1a, 0x00, 0x1e,
0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x1e, 0x14, 0x66, 0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32,
0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, 0x67, 0x00,
0x00, 0x1a, 0x00, 0x1e, 0x00, 0x1e, 0x14, 0x66, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1c,
0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, 0x73, 0x32, 0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0x09, 0x68, 0x65, 0x72, 0x6d, 0x65,
0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x73, 0x32, 0x76, 0x36, 0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03,
0x6f, 0x72, 0x67, 0x00, 0xc0, 0x0c, 0x00, 0x21,
0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x1c,
0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0x09, 0x68,
0x65, 0x72, 0x6d, 0x65, 0x73, 0x32, 0x76, 0x36,
0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03,
0x6f, 0x72, 0x67, 0x00, 0x6f, 0x72, 0x67, 0x00,
}; };
/* res_query("_xmpp-client._tcp.gmail.com", C_IN, T_SRV, ...) */ /* res_query("_xmpp-client._tcp.gmail.com", C_IN, T_SRV, ...) */
static const unsigned char data3[] = { static const unsigned char data3[] = {
0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x6d, 0x61, 0x69, 0x6c,
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00,
0x6d, 0x61, 0x69, 0x6c, 0x03, 0x63, 0x6f, 0x6d, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70,
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c,
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x74, 0x34, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f,
0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00,
0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x04, 0x78, 0x6d, 0x70,
0x66, 0x04, 0x61, 0x6c, 0x74, 0x34, 0x04, 0x78, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x43, 0x00, 0x19, 0x00, 0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d,
0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61,
0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x6c, 0x74, 0x33, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
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,
}; };
/* res_query("_xmpp-client._tcp.jabber.calyxinstitute.org", C_IN, T_SRV, ...) */ /* res_query("_xmpp-client._tcp.jabber.calyxinstitute.org", C_IN, T_SRV, ...) */
static const unsigned char data4[] = { static const unsigned char data4[] = {
0x8d, 0x58, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, // .X........ 0x8d, 0x58, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, // .X........
@@ -103,6 +83,31 @@ static const unsigned char data4[] = {
0x61, 0x6c, 0x79, 0x78, 0x69, 0x6e, 0x73, 0x74, 0x69, 0x74, // alyxinstit 0x61, 0x6c, 0x79, 0x78, 0x69, 0x6e, 0x73, 0x74, 0x69, 0x74, // alyxinstit
0x75, 0x74, 0x65, 0x03, 0x6f, 0x72, 0x67, 0x00, // ute.org. 0x75, 0x74, 0x65, 0x03, 0x6f, 0x72, 0x67, 0x00, // ute.org.
}; };
/* res_query("_xmpp-client._tcp.jabber.org", C_IN, T_SRV, ...) with pointers */
static const unsigned char data5[] = {
0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, // ..........
0x00, 0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, // ..._xmpp-c
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, // lient._tcp
0x06, 0x6a, 0x61, 0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, // .jabber.or
0x67, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, // g..!.....!
0x00, 0x01, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x12, 0x00, 0x1f, // ..........
0x00, 0x1e, 0x14, 0x66, 0x09, 0x68, 0x65, 0x72, 0x6d, 0x65, // ...f.herme
0x73, 0x32, 0x76, 0x36, 0xc0, 0x1e, 0xc0, 0x0c, 0x00, 0x21, // s2v6.....!
0x00, 0x01, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x10, 0x00, 0x1e, // ..........
0x00, 0x1e, 0x14, 0x66, 0x07, 0x68, 0x65, 0x72, 0x6d, 0x65, // ...f.herme
0x73, 0x32, 0xc0, 0x1e, // s2..
};
/* hacked data2 with two empty-string targets. */
static const unsigned char data6[] = {
0xf2, 0x98, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x06, 0x6a, 0x61,
0x62, 0x62, 0x65, 0x72, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x21,
0x00, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03,
0x83, 0x00, 0x07, 0x00, 0x1e, 0x00, 0x1e, 0x14, 0x66, 0x00, 0xc0,
0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x08,
0x00, 0x1f, 0x00, 0x1e, 0x14, 0x66, 0xc0, 0x40,
};
static const struct { static const struct {
const unsigned char *data; const unsigned char *data;
@@ -139,22 +144,39 @@ static const struct {
.port = 5222, .port = 5222,
.target_nr = 2, .target_nr = 2,
}, },
{
.data = data5,
.len = sizeof(data5),
.target = "hermes2.jabber.org",
.port = 5222,
.target_nr = 2,
},
{
.data = data6,
.len = sizeof(data6),
.target = "",
.port = 5222,
.target_nr = 2,
},
}; };
static int srv_rr_list_len(resolver_srv_rr_t *list) static int srv_rr_list_len(resolver_srv_rr_t *list)
{ {
int nr; int nr;
for(nr = 0; list != NULL; ++nr, list = list->next); for (nr = 0; list != NULL; ++nr, list = list->next)
;
return nr; return nr;
} }
int main(int argc, char **argv) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
xmpp_rand_t *rand;
resolver_srv_rr_t *srv_rr_list; resolver_srv_rr_t *srv_rr_list;
char *domain; char *domain;
unsigned char *buf;
unsigned short port; unsigned short port;
size_t i; size_t i;
int ret; int ret;
@@ -179,14 +201,33 @@ int main(int argc, char **argv)
port = srv_rr_list->port; port = srv_rr_list->port;
COMPARE(tests[i].target, domain); COMPARE(tests[i].target, domain);
if (tests[i].port != port) { if (tests[i].port != port) {
printf("fail! got port=%u, but should be %u\n", printf("fail! got port=%u, but should be %u\n", (unsigned)port,
(unsigned)port, (unsigned)tests[i].port); (unsigned)tests[i].port);
return 1; return 1;
} }
printf("ok\n"); printf("ok\n");
resolver_srv_free(ctx, srv_rr_list); resolver_srv_free(ctx, srv_rr_list);
} }
/*
* The next test case must not crash and is supposed to be checked
* under valgrind.
*/
printf("Test of a broken message: ");
rand = xmpp_rand_new(ctx);
assert(rand != NULL);
assert(sizeof(data2) > 64);
buf = xmpp_alloc(ctx, sizeof(data2));
assert(buf != NULL);
memcpy(buf, data2, 64);
xmpp_rand_bytes(rand, &buf[64], sizeof(data2) - 64);
ret = resolver_srv_lookup_buf(ctx, buf, sizeof(data2), &srv_rr_list);
if (ret == XMPP_DOMAIN_FOUND && srv_rr_list != NULL)
resolver_srv_free(ctx, srv_rr_list);
xmpp_free(ctx, buf);
xmpp_rand_free(ctx, rand);
printf("ok\n");
xmpp_ctx_free(ctx); xmpp_ctx_free(ctx);
return 0; return 0;

View File

@@ -69,8 +69,8 @@ int test_digest_md5(xmpp_ctx_t *ctx)
{ {
char *result; char *result;
result = sasl_digest_md5(ctx, challenge_md5, result =
"somenode@somerealm", "secret"); sasl_digest_md5(ctx, challenge_md5, "somenode@somerealm", "secret");
printf("response:\n%s\n", result); printf("response:\n%s\n", result);
if (strcmp(response_md5, result)) { if (strcmp(response_md5, result)) {
/* generated incorrect response to challenge */ /* generated incorrect response to challenge */
@@ -80,27 +80,33 @@ int test_digest_md5(xmpp_ctx_t *ctx)
return 0; return 0;
} }
int main(int argc, char *argv[]) int main()
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
int ret; int ret;
printf("allocating context... "); printf("allocating context... ");
ctx = xmpp_ctx_new(NULL, NULL); ctx = xmpp_ctx_new(NULL, NULL);
if (ctx == NULL) printf("failed to create context\n"); if (ctx == NULL)
if (ctx == NULL) return -1; printf("failed to create context\n");
if (ctx == NULL)
return -1;
printf("ok.\n"); printf("ok.\n");
printf("testing SASL PLAIN... "); printf("testing SASL PLAIN... ");
ret = test_plain(ctx); ret = test_plain(ctx);
if (ret) printf("failed!\n"); if (ret)
if (ret) return ret; printf("failed!\n");
if (ret)
return ret;
printf("ok.\n"); printf("ok.\n");
printf("testing SASL DIGEST-MD5... "); printf("testing SASL DIGEST-MD5... ");
ret = test_digest_md5(ctx); ret = test_digest_md5(ctx);
if (ret) printf("failed!\n"); if (ret)
if (ret) return ret; printf("failed!\n");
if (ret)
return ret;
printf("ok.\n"); printf("ok.\n");
printf("freeing context... "); printf("freeing context... ");

View File

@@ -58,19 +58,19 @@ static const struct {
}, },
}; };
static void test_df(void) static void test_df(const struct hash_alg *alg)
{ {
size_t i; size_t i;
const char *s; const char *s;
uint8_t dk[SHA1_DIGEST_SIZE]; uint8_t dk[SCRAM_DIGEST_SIZE];
printf("Derivation function tests (SCRAM_SHA1_Hi).\n"); printf("Derivation function SCRAM_Hi tests for %s.\n", alg->scram_name);
for (i = 0; i < ARRAY_SIZE(df_vectors); ++i) { for (i = 0; i < ARRAY_SIZE(df_vectors); ++i) {
printf("Test #%d: ", (int)i + 1); printf("Test #%d: ", (int)i + 1);
SCRAM_SHA1_Hi((uint8_t *)df_vectors[i].P, df_vectors[i].P_len, SCRAM_Hi(alg, (uint8_t *)df_vectors[i].P, df_vectors[i].P_len,
(uint8_t *)df_vectors[i].S, df_vectors[i].S_len, (uint8_t *)df_vectors[i].S, df_vectors[i].S_len,
df_vectors[i].c, dk); df_vectors[i].c, dk);
s = test_bin_to_hex(dk, sizeof(dk)); s = test_bin_to_hex(dk, alg->digest_size);
COMPARE(df_vectors[i].DK, s); COMPARE(df_vectors[i].DK, s);
printf("ok\n"); printf("ok\n");
} }
@@ -101,42 +101,115 @@ static const struct {
}, },
}; };
static void test_scram(void) static void test_scram(const struct hash_alg *alg)
{ {
uint8_t key[SHA1_DIGEST_SIZE]; uint8_t key[SCRAM_DIGEST_SIZE];
uint8_t sign[SHA1_DIGEST_SIZE]; uint8_t sign[SCRAM_DIGEST_SIZE];
uint8_t salt[256]; uint8_t salt[256];
size_t salt_len; size_t salt_len;
char auth[512]; char auth[512];
const char *s; const char *s;
size_t i; size_t i;
int j; size_t j;
printf("SCRAM_SHA1_ClientKey and SCRAM_SHA1_ClientSignature tests.\n"); printf("SCRAM_ClientKey and SCRAM_ClientSignature tests for %s.\n",
alg->scram_name);
for (i = 0; i < ARRAY_SIZE(scram_vectors); ++i) { for (i = 0; i < ARRAY_SIZE(scram_vectors); ++i) {
printf("Test #%d: ", (int)i + 1); printf("Test #%d: ", (int)i + 1);
snprintf(auth, sizeof(auth), "%s,%s,%s", snprintf(auth, sizeof(auth), "%s,%s,%s", scram_vectors[i].initial + 3,
scram_vectors[i].initial + 3, scram_vectors[i].challenge, scram_vectors[i].challenge, scram_vectors[i].response);
scram_vectors[i].response);
test_hex_to_bin(scram_vectors[i].salt, salt, &salt_len); test_hex_to_bin(scram_vectors[i].salt, salt, &salt_len);
SCRAM_SHA1_ClientKey((uint8_t *)scram_vectors[i].password, SCRAM_ClientKey(alg, (uint8_t *)scram_vectors[i].password,
strlen(scram_vectors[i].password), strlen(scram_vectors[i].password), salt, salt_len,
salt, salt_len, scram_vectors[i].i, key); scram_vectors[i].i, key);
SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign);
for (j = 0; j < SHA1_DIGEST_SIZE; j++) { for (j = 0; j < alg->digest_size; j++) {
sign[j] ^= key[j]; sign[j] ^= key[j];
} }
s = test_bin_to_hex(sign, SHA1_DIGEST_SIZE); s = test_bin_to_hex(sign, alg->digest_size);
COMPARE(scram_vectors[i].sign, s); COMPARE(scram_vectors[i].sign, s);
printf("ok\n"); printf("ok\n");
} }
} }
int main(int argc, char **argv) /*
* Test vectors for HMAC (RFC2202, RFC4231).
*/
static const uint8_t hmac_key1[] = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b};
static const uint8_t hmac_data1[] = {0x48, 0x69, 0x20, 0x54,
0x68, 0x65, 0x72, 0x65};
static const struct {
const uint8_t *key;
size_t key_len;
const uint8_t *data;
size_t data_len;
const uint8_t sha1[SHA1_DIGEST_SIZE];
const uint8_t sha256[SHA256_DIGEST_SIZE];
const uint8_t sha512[SHA512_DIGEST_SIZE];
} hmac_vectors[] = {
{ {
test_df(); .key = hmac_key1,
test_scram(); .key_len = sizeof(hmac_key1),
.data = hmac_data1,
.data_len = sizeof(hmac_data1),
.sha1 = {0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b,
0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00},
.sha256 = {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7},
.sha512 = {0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 0x4f, 0xf0,
0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0, 0x23, 0x79, 0xf4, 0xe2,
0xce, 0x4e, 0xc2, 0x78, 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1,
0x7c, 0xde, 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, 0x02,
0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, 0xbe, 0x9d,
0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70, 0x2e, 0x69, 0x6c, 0x20,
0x3a, 0x12, 0x68, 0x54},
},
};
static void test_hmac(const struct hash_alg *alg)
{
size_t i;
uint8_t digest[SCRAM_DIGEST_SIZE];
printf("HMAC tests for %s.\n", alg->scram_name);
for (i = 0; i < ARRAY_SIZE(hmac_vectors); ++i) {
printf("Test #%d: ", (int)i + 1);
memset(digest, 0, sizeof(digest));
crypto_HMAC(alg, hmac_vectors[i].key, hmac_vectors[i].key_len,
hmac_vectors[i].data, hmac_vectors[i].data_len, digest);
switch (alg->digest_size) {
case SHA1_DIGEST_SIZE:
COMPARE_BUF(hmac_vectors[i].sha1, alg->digest_size, digest,
alg->digest_size);
break;
case SHA256_DIGEST_SIZE:
COMPARE_BUF(hmac_vectors[i].sha256, alg->digest_size, digest,
alg->digest_size);
break;
case SHA512_DIGEST_SIZE:
COMPARE_BUF(hmac_vectors[i].sha512, alg->digest_size, digest,
alg->digest_size);
break;
default:
printf("Unknown digest size: %zu\n", alg->digest_size);
exit(1);
}
printf("ok\n");
}
}
int main()
{
test_df(&scram_sha1);
test_scram(&scram_sha1);
test_hmac(&scram_sha1);
test_hmac(&scram_sha256);
test_hmac(&scram_sha512);
return 0; return 0;
} }

View File

@@ -12,13 +12,11 @@
/* Test Vectors (from FIPS PUB 180-1) */ /* Test Vectors (from FIPS PUB 180-1) */
static char *test_data[] = { static char *test_data[] = {
"abc", "abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"A million repetitions of 'a'", "A million repetitions of 'a'",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}; "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"};
static char *test_results[] = { static char *test_results[] = {"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
"84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1", "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F", "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F",
"AD5B3FDB CB526778 C2839D2F 151EA753 995E26A0"}; "AD5B3FDB CB526778 C2839D2F 151EA753 995E26A0"};
@@ -39,9 +37,9 @@ static void digest_to_hex(const uint8_t *digest, char *output)
*(c - 1) = '\0'; *(c - 1) = '\0';
} }
int main(int argc, char** argv) int main()
{ {
int k; size_t k;
SHA1_CTX context; SHA1_CTX context;
uint8_t digest[20]; uint8_t digest[20];
char output[80]; char output[80];

44
tests/test_sha256.c Normal file
View File

@@ -0,0 +1,44 @@
/* Tests for SHA-256 from LibTomCrypt */
/* This file is in the public domain */
/* gcc -o test_sha256 -I./src tests/test_sha256.c tests/test.c src/sha256.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sha256.h"
#include "test.h"
int main()
{
static const struct {
const char *msg;
unsigned char hash[SHA256_DIGEST_SIZE];
} tests[] = {
{"abc",
{0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}},
{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
{0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}},
};
int i;
unsigned char tmp[SHA256_DIGEST_SIZE];
sha256_context md;
for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
sha256_init(&md);
sha256_process(&md, (unsigned char *)tests[i].msg,
(unsigned long)strlen(tests[i].msg));
sha256_done(&md, tmp);
COMPARE_BUF(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash));
}
/* success */
fprintf(stdout, "ok\n");
return (0);
}

65
tests/test_sha512.c Normal file
View File

@@ -0,0 +1,65 @@
/* Tests for SHA-512 from LibTomCrypt */
/* This file is in the public domain */
/* gcc -o test_sha512 -I./src tests/test_sha512.c tests/test.c src/sha512.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sha512.h"
#include "test.h"
static const uint8_t hash_1m_a[SHA512_DIGEST_SIZE] = {
0xe7, 0x18, 0x48, 0x3d, 0x0c, 0xe7, 0x69, 0x64, 0x4e, 0x2e, 0x42,
0xc7, 0xbc, 0x15, 0xb4, 0x63, 0x8e, 0x1f, 0x98, 0xb1, 0x3b, 0x20,
0x44, 0x28, 0x56, 0x32, 0xa8, 0x03, 0xaf, 0xa9, 0x73, 0xeb, 0xde,
0x0f, 0xf2, 0x44, 0x87, 0x7e, 0xa6, 0x0a, 0x4c, 0xb0, 0x43, 0x2c,
0xe5, 0x77, 0xc3, 0x1b, 0xeb, 0x00, 0x9c, 0x5c, 0x2c, 0x49, 0xaa,
0x2e, 0x4e, 0xad, 0xb2, 0x17, 0xad, 0x8c, 0xc0, 0x9b};
int main()
{
static const struct {
const char *msg;
uint8_t hash[SHA512_DIGEST_SIZE];
} tests[] = {
{"abc",
{0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73,
0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9,
0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21,
0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23,
0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8,
0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}},
{"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklm"
"nopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
{0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7,
0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f,
0x7f, 0xa1, 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50,
0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde,
0xc4, 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26,
0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09}},
};
size_t i;
uint8_t tmp[SHA512_DIGEST_SIZE];
sha512_context md;
for (i = 0; i < ARRAY_SIZE(tests); i++) {
sha512_init(&md);
sha512_process(&md, (uint8_t *)tests[i].msg, strlen(tests[i].msg));
sha512_done(&md, tmp);
COMPARE_BUF(tests[i].hash, sizeof(tests[i].hash), tmp, sizeof(tmp));
}
/* special case: one million repetitions of the character 'a' */
sha512_init(&md);
for (i = 0; i < 1000000U; ++i)
sha512_process(&md, (uint8_t *)"a", 1);
sha512_done(&md, tmp);
COMPARE_BUF(hash_1m_a, sizeof(hash_1m_a), tmp, sizeof(tmp));
/* success */
fprintf(stdout, "ok\n");
return 0;
}

View File

@@ -20,38 +20,14 @@ int main (void)
{ {
char buf1[LONG_STRING]; char buf1[LONG_STRING];
char buf2[LONG_STRING]; char buf2[LONG_STRING];
char *fp_fmt[] = { char *fp_fmt[] = {"%-1.5f", "%1.5f", "%123.9f", "%10.5f", "% 10.5f",
"%-1.5f", "%+22.9f", "%+4.9f", "%01.3f", "%4f", "%3.1f",
"%1.5f", "%3.2f", "%.0f", "%.1f", NULL};
"%123.9f", double fp_nums[] = {-1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96,
"%10.5f", 0.996, 0.9996, 1.996, 4.136, 0};
"% 10.5f", char *int_fmt[] = {"%-1.5d", "%1.5d", "%123.9d", "%5.5d",
"%+22.9f", "%10.5d", "% 10.5d", "%+22.33d", "%01.3d",
"%+4.9f", "%4d", "0x%x", "0x%04x", NULL};
"%01.3f",
"%4f",
"%3.1f",
"%3.2f",
"%.0f",
"%.1f",
NULL
};
double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
0.9996, 1.996, 4.136, 0};
char *int_fmt[] = {
"%-1.5d",
"%1.5d",
"%123.9d",
"%5.5d",
"%10.5d",
"% 10.5d",
"%+22.33d",
"%01.3d",
"%4d",
"0x%x",
"0x%04x",
NULL
};
long int_nums[] = {-1, 134, 91340, 341, 0203, 0x76543210, 0}; long int_nums[] = {-1, 134, 91340, 341, 0203, 0x76543210, 0};
int x, y; int x, y;
int fail = 0; int fail = 0;
@@ -60,13 +36,12 @@ int main (void)
printf("Testing xmpp_snprintf format codes against system sprintf...\n"); printf("Testing xmpp_snprintf format codes against system sprintf...\n");
for (x = 0; fp_fmt[x] != NULL; x++) for (x = 0; fp_fmt[x] != NULL; x++)
for (y = 0; fp_nums[y] != 0 ; y++) for (y = 0; fp_nums[y] != 0; y++) {
{
xmpp_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); xmpp_snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
sprintf(buf2, fp_fmt[x], fp_nums[y]); sprintf(buf2, fp_fmt[x], fp_nums[y]);
if (strcmp (buf1, buf2)) if (strcmp(buf1, buf2)) {
{ printf("xmpp_snprintf doesn't match Format: "
printf("xmpp_snprintf doesn't match Format: %s\n\txmpp_snprintf = %s\n\tsprintf = %s\n", "%s\n\txmpp_snprintf = %s\n\tsprintf = %s\n",
fp_fmt[x], buf1, buf2); fp_fmt[x], buf1, buf2);
fail++; fail++;
} }
@@ -74,13 +49,12 @@ int main (void)
} }
for (x = 0; int_fmt[x] != NULL; x++) for (x = 0; int_fmt[x] != NULL; x++)
for (y = 0; int_nums[y] != 0 ; y++) for (y = 0; int_nums[y] != 0; y++) {
{
xmpp_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); xmpp_snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
sprintf(buf2, int_fmt[x], int_nums[y]); sprintf(buf2, int_fmt[x], int_nums[y]);
if (strcmp (buf1, buf2)) if (strcmp(buf1, buf2)) {
{ printf("xmpp_snprintf doesn't match Format: "
printf("xmpp_snprintf doesn't match Format: %s\n\txmpp_snprintf = %s\n\tsprintf = %s\n", "%s\n\txmpp_snprintf = %s\n\tsprintf = %s\n",
int_fmt[x], buf1, buf2); int_fmt[x], buf1, buf2);
fail++; fail++;
} }

View File

@@ -23,19 +23,24 @@ int wait_for_connect(sock_t sock)
fd_set wfds, efds; fd_set wfds, efds;
int ret; int ret;
FD_ZERO(&wfds); FD_SET(sock, &wfds); FD_ZERO(&wfds);
FD_ZERO(&efds); FD_SET(sock, &efds); FD_SET(sock, &wfds);
FD_ZERO(&efds);
FD_SET(sock, &efds);
ret = select(sock + 1, NULL, &wfds, &efds, NULL); ret = select(sock + 1, NULL, &wfds, &efds, NULL);
if (ret <= 0) return -1; if (ret <= 0)
return -1;
if (FD_ISSET(sock, &efds)) return 0; if (FD_ISSET(sock, &efds))
if (FD_ISSET(sock, &wfds)) return 1; return 0;
if (FD_ISSET(sock, &wfds))
return 1;
return -1; return -1;
} }
int main(int argc, char **argv) int main()
{ {
sock_t sock; sock_t sock;
int err; int err;

131
tests/test_stanza.c Normal file
View File

@@ -0,0 +1,131 @@
/* test_stanza.c
* libstrophe XMPP client library -- test routines for stanza functions
*
* 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 and GPLv3 licenses.
*/
/* gcc -o test_stanza -I./src tests/test_stanza.c -lstrophe */
#include <strophe.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define MAGICPTR ((void *)0xfeedbeef)
static unsigned long used_blocks = 0;
static void *stanza_alloc(size_t size, void *userdata)
{
assert(userdata == MAGICPTR);
++used_blocks;
return malloc(size);
}
static void stanza_free(void *ptr, void *userdata)
{
assert(userdata == MAGICPTR);
if (ptr != NULL) {
assert(used_blocks > 0);
--used_blocks;
}
free(ptr);
}
static void *stanza_realloc(void *ptr, size_t size, void *userdata)
{
assert(userdata == MAGICPTR);
if (ptr != NULL && size == 0) {
/* equivalent to free(ptr) */
assert(used_blocks > 0);
--used_blocks;
} else if (ptr == NULL) {
/* equivalent to malloc(size) */
++used_blocks;
}
return realloc(ptr, size);
}
static const xmpp_mem_t stanza_mem = {
.alloc = &stanza_alloc,
.free = &stanza_free,
.realloc = &stanza_realloc,
.userdata = MAGICPTR,
};
static void test_stanza_add_child(xmpp_ctx_t *ctx)
{
xmpp_stanza_t *stanza;
xmpp_stanza_t *child;
unsigned long baseline = used_blocks;
/* xmpp_stanza_add_child */
stanza = xmpp_stanza_new(ctx);
child = xmpp_stanza_new(ctx);
assert(stanza != NULL);
assert(child != NULL);
xmpp_stanza_add_child(stanza, child);
xmpp_stanza_release(stanza);
assert(used_blocks > baseline);
xmpp_stanza_release(child);
assert(used_blocks == baseline);
/* xmpp_stanza_add_child_ex */
stanza = xmpp_stanza_new(ctx);
child = xmpp_stanza_new(ctx);
assert(stanza != NULL);
assert(child != NULL);
xmpp_stanza_add_child_ex(stanza, child, 0);
xmpp_stanza_release(stanza);
assert(used_blocks == baseline);
}
static void test_stanza_from_string(xmpp_ctx_t *ctx)
{
xmpp_stanza_t *stanza;
char *buf;
size_t buflen;
int ret;
static const char *str =
"<signcrypt xmlns=\"urn:xmpp:openpgp:0\"><to "
"jid=\"user@domain.com\"/><time "
"stamp=\"2020-06-03T21:26:24+0200\"/><rpad/><payload><body "
"xmlns=\"jabber:client\">Hello World!</body></payload></signcrypt>";
stanza = xmpp_stanza_new_from_string(ctx, str);
assert(stanza != NULL);
ret = xmpp_stanza_to_text(stanza, &buf, &buflen);
assert(ret == XMPP_EOK);
assert(strcmp(buf, str) == 0);
xmpp_free(ctx, buf);
xmpp_stanza_release(stanza);
}
int main()
{
xmpp_ctx_t *ctx;
xmpp_initialize();
ctx = xmpp_ctx_new(&stanza_mem, NULL);
assert(ctx != NULL);
test_stanza_add_child(ctx);
test_stanza_from_string(ctx);
xmpp_ctx_free(ctx);
xmpp_shutdown();
/* All allocated blocks must be freed. */
assert(used_blocks == 0);
}

View File

@@ -20,6 +20,16 @@
#include "test.h" /* ARRAY_SIZE */ #include "test.h" /* ARRAY_SIZE */
/* 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
#define strtok_r xmpp_strtok_r
#endif
#endif /* _MSC_VER */
static int test_strtok_r(void) static int test_strtok_r(void)
{ {
const char *test = "-abc-=-def--"; const char *test = "-abc-=-def--";
@@ -65,8 +75,8 @@ static int test_strdup_one(xmpp_ctx_t *ctx, const char *s)
if (!s1 || !s2 || strcmp(s1, s2) != 0) { if (!s1 || !s2 || strcmp(s1, s2) != 0) {
rc = -1; rc = -1;
printf("strdup: '%s', xmpp_strdup: '%s'\n", printf("strdup: '%s', xmpp_strdup: '%s'\n", s1 ? s1 : "<NULL>",
s1 ? s1 : "<NULL>", s2 ? s2 : "<NULL>"); s2 ? s2 : "<NULL>");
} }
free(s1); free(s1);
@@ -79,7 +89,7 @@ static int test_strdup_one(xmpp_ctx_t *ctx, const char *s)
static int test_strdup(void) static int test_strdup(void)
{ {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
int i; size_t i;
int rc = 0; int rc = 0;
static const char *tests[] = {"", "\0", "test", "s p a c e", "\n\r"}; static const char *tests[] = {"", "\0", "test", "s p a c e", "\n\r"};