46 Commits

Author SHA1 Message Date
Dmitry Podgorny
87d40d23db WIP add xsock 2019-10-30 23:29:56 +02: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
29 changed files with 1033 additions and 493 deletions

View File

@@ -1,15 +1,15 @@
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
before_script: before_script:
- ./bootstrap.sh - ./bootstrap.sh
script: script:
- ./configure ${CONFIGURE_OPT} && make && make check-TESTS - ./configure ${CONFIGURE_OPT} CFLAGS="-Werror" && make && make check
env: env:
- CONFIGURE_OPT="--without-libxml2" - CONFIGURE_OPT="--without-libxml2"
- CONFIGURE_OPT="--with-libxml2" - CONFIGURE_OPT="--with-libxml2"
- CONFIGURE_OPT="--disable-tls --without-libxml2" - CONFIGURE_OPT="--disable-tls"
- CONFIGURE_OPT="--disable-tls --with-libxml2" - CONFIGURE_OPT="--enable-cares"
matrix: matrix:
fast_finish: true fast_finish: true

View File

@@ -1,3 +1,12 @@
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 0.9.2
- OpenSSL tls module verifies certificate by default. Set flag - OpenSSL tls module verifies certificate by default. Set flag
XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification
@@ -8,7 +17,12 @@
userdata userdata
- System handlers are deleted on xmpp_conn_t reconnection. Old system - System handlers are deleted on xmpp_conn_t reconnection. Old system
handlers could cause problems 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: - New functions:
- xmpp_error_new()
- xmpp_send_error()
- xmpp_ctx_set_timeout() - xmpp_ctx_set_timeout()
- xmpp_sha1_digest() - xmpp_sha1_digest()

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,15 +8,16 @@ 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) -Wall -Wextra -Werror -Wno-unused-parameter STROPHE_FLAGS = -I$(top_srcdir) -Wall -Wextra -Wno-unused-parameter
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_'

View File

@@ -1,4 +1,4 @@
AC_INIT([libstrophe], [0.9.1], [jack@metajack.im]) AC_INIT([libstrophe], [0.9.3], [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])
@@ -21,6 +21,8 @@ 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])])
if test "x$enable_tls" != xno; then if test "x$enable_tls" != xno; then
PKG_CHECK_MODULES([openssl], [openssl], PKG_CHECK_MODULES([openssl], [openssl],
@@ -84,6 +86,18 @@ AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
AC_SEARCH_LIBS([socket], [socket]) 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"],
@@ -110,6 +124,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],
@@ -126,6 +142,7 @@ 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_CONFIG_FILES([Makefile libstrophe.pc]) AC_CONFIG_FILES([Makefile libstrophe.pc])
AC_OUTPUT AC_OUTPUT

View File

@@ -62,7 +62,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

@@ -61,6 +61,7 @@
#endif #endif
static void _auth(xmpp_conn_t * const conn); static void _auth(xmpp_conn_t * const conn);
static void _auth_legacy(xmpp_conn_t *conn);
static void _handle_open_sasl(xmpp_conn_t * const conn); static void _handle_open_sasl(xmpp_conn_t * const conn);
static void _handle_open_tls(xmpp_conn_t * const conn); static void _handle_open_tls(xmpp_conn_t * const conn);
@@ -69,11 +70,6 @@ static int _handle_component_hs_response(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza, xmpp_stanza_t * const stanza,
void * const userdata); void * const userdata);
static int _handle_missing_legacy(xmpp_conn_t * const conn,
void * const userdata);
static int _handle_legacy(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza,
void * const userdata);
static int _handle_features_sasl(xmpp_conn_t * const conn, static int _handle_features_sasl(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza, xmpp_stanza_t * const stanza,
void * const userdata); void * const userdata);
@@ -219,6 +215,7 @@ static int _handle_features(xmpp_conn_t * const conn,
void * const userdata) void * const userdata)
{ {
xmpp_stanza_t *child, *mech; xmpp_stanza_t *child, *mech;
const char *ns;
char *text; char *text;
/* remove the handler that detects missing stream:features */ /* remove the handler that detects missing stream:features */
@@ -228,8 +225,10 @@ static int _handle_features(xmpp_conn_t * const conn,
if (!conn->secured) { if (!conn->secured) {
if (!conn->tls_disabled) { if (!conn->tls_disabled) {
child = xmpp_stanza_get_child_by_name(stanza, "starttls"); child = xmpp_stanza_get_child_by_name(stanza, "starttls");
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0)) if (child) {
conn->tls_support = 1; ns = xmpp_stanza_get_ns(child);
conn->tls_support = ns != NULL && strcmp(ns, XMPP_NS_TLS) == 0;
}
} else { } else {
conn->tls_support = 0; conn->tls_support = 0;
} }
@@ -237,11 +236,15 @@ static int _handle_features(xmpp_conn_t * const conn,
/* check for SASL */ /* check for SASL */
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms"); child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_SASL) == 0)) { ns = child ? xmpp_stanza_get_ns(child) : NULL;
if (child && ns && strcmp(ns, XMPP_NS_SASL) == 0) {
for (mech = xmpp_stanza_get_children(child); mech; for (mech = xmpp_stanza_get_children(child); mech;
mech = xmpp_stanza_get_next(mech)) { mech = xmpp_stanza_get_next(mech)) {
if (xmpp_stanza_get_name(mech) && strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) { if (xmpp_stanza_get_name(mech) && strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
text = xmpp_stanza_get_text(mech); text = xmpp_stanza_get_text(mech);
if (text == NULL)
continue;
if (strcasecmp(text, "PLAIN") == 0) if (strcasecmp(text, "PLAIN") == 0)
conn->sasl_support |= SASL_MASK_PLAIN; conn->sasl_support |= SASL_MASK_PLAIN;
else if (strcasecmp(text, "DIGEST-MD5") == 0) else if (strcasecmp(text, "DIGEST-MD5") == 0)
@@ -256,6 +259,10 @@ static int _handle_features(xmpp_conn_t * const conn,
} }
} }
/* Disable PLAIN when other secure mechanisms are supported */
if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS))
conn->sasl_support &= ~SASL_MASK_PLAIN;
_auth(conn); _auth(conn);
return 0; return 0;
@@ -550,9 +557,11 @@ static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
*/ */
static void _auth(xmpp_conn_t * const conn) static void _auth(xmpp_conn_t * const conn)
{ {
xmpp_stanza_t *auth, *authdata, *query, *child, *iq; xmpp_stanza_t *auth;
char *str, *authid; xmpp_stanza_t *authdata;
char *authid;
char *scram_init; char *scram_init;
char *str;
int anonjid; int anonjid;
/* if there is no node in conn->jid, we assume anonymous connect */ /* if there is no node in conn->jid, we assume anonymous connect */
@@ -719,105 +728,12 @@ static void _auth(xmpp_conn_t * const conn)
/* SASL PLAIN was tried */ /* SASL PLAIN was tried */
conn->sasl_support &= ~SASL_MASK_PLAIN; conn->sasl_support &= ~SASL_MASK_PLAIN;
} else if (conn->type == XMPP_CLIENT) { } else if (conn->type == XMPP_CLIENT && conn->auth_legacy_enabled) {
/* legacy client authentication */ /* legacy client authentication */
_auth_legacy(conn);
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1"); } else {
if (!iq) { xmpp_error(conn->ctx, "auth", "Cannot authenticate with known methods");
disconnect_mem_error(conn); xmpp_disconnect(conn);
return;
}
query = xmpp_stanza_new(conn->ctx);
if (!query) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
xmpp_stanza_add_child(iq, query);
xmpp_stanza_release(query);
child = xmpp_stanza_new(conn->ctx);
if (!child) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(child, "username");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
str = xmpp_jid_node(conn->ctx, conn->jid);
xmpp_stanza_set_text(authdata, str);
xmpp_free(conn->ctx, str);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
child = xmpp_stanza_new(conn->ctx);
if (!child) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(child, "password");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_text(authdata, conn->pass);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
child = xmpp_stanza_new(conn->ctx);
if (!child) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(child, "resource");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
str = xmpp_jid_resource(conn->ctx, conn->jid);
if (str) {
xmpp_stanza_set_text(authdata, str);
xmpp_free(conn->ctx, str);
} else {
xmpp_stanza_release(authdata);
xmpp_stanza_release(iq);
xmpp_error(conn->ctx, "auth",
"Cannot authenticate without resource");
xmpp_disconnect(conn);
return;
}
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
handler_add_timed(conn, _handle_missing_legacy,
LEGACY_TIMEOUT, NULL);
xmpp_send(conn, iq);
xmpp_stanza_release(iq);
} }
} }
@@ -871,7 +787,8 @@ static int _handle_features_sasl(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza, xmpp_stanza_t * const stanza,
void * const userdata) void * const userdata)
{ {
xmpp_stanza_t *bind, *session, *iq, *res, *text; xmpp_stanza_t *bind, *session, *iq, *res, *text, *opt;
const char *ns;
char *resource; char *resource;
/* remove missing features handler */ /* remove missing features handler */
@@ -880,16 +797,21 @@ static int _handle_features_sasl(xmpp_conn_t * const conn,
/* we are expecting <bind/> and <session/> since this is a /* we are expecting <bind/> and <session/> since this is a
XMPP style connection */ XMPP style connection */
/* check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name(stanza, "bind"); bind = xmpp_stanza_get_child_by_name(stanza, "bind");
if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) { if (bind) {
/* resource binding is required */ ns = xmpp_stanza_get_ns(bind);
conn->bind_required = 1; conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
} }
/* check whether session establishment is required */
session = xmpp_stanza_get_child_by_name(stanza, "session"); session = xmpp_stanza_get_child_by_name(stanza, "session");
if (session && strcmp(xmpp_stanza_get_ns(session), XMPP_NS_SESSION) == 0) { if (session) {
/* session establishment required */ ns = xmpp_stanza_get_ns(session);
conn->session_required = 1; opt = xmpp_stanza_get_child_by_name(session, "optional");
if (!opt)
conn->session_required = ns != NULL &&
strcmp(ns, XMPP_NS_SESSION) == 0;
} }
/* if bind is required, go ahead and start it */ /* if bind is required, go ahead and start it */
@@ -1092,6 +1014,15 @@ static int _handle_missing_session(xmpp_conn_t * const conn,
return 0; return 0;
} }
static int _handle_missing_legacy(xmpp_conn_t * const conn,
void * const userdata)
{
xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
"authentication request.");
xmpp_disconnect(conn);
return 0;
}
static int _handle_legacy(xmpp_conn_t * const conn, static int _handle_legacy(xmpp_conn_t * const conn,
xmpp_stanza_t * const stanza, xmpp_stanza_t * const stanza,
void * const userdata) void * const userdata)
@@ -1128,13 +1059,97 @@ static int _handle_legacy(xmpp_conn_t * const conn,
return 0; return 0;
} }
static int _handle_missing_legacy(xmpp_conn_t * const conn, static void _auth_legacy(xmpp_conn_t *conn)
void * const userdata)
{ {
xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\ xmpp_stanza_t *iq;
"authentication request."); xmpp_stanza_t *authdata;
xmpp_disconnect(conn); xmpp_stanza_t *query;
return 0; xmpp_stanza_t *child;
char *str;
xmpp_debug(conn->ctx, "auth", "Legacy authentication request");
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1");
if (!iq)
goto err;
query = xmpp_stanza_new(conn->ctx);
if (!query)
goto err_free;
xmpp_stanza_set_name(query, "query");
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
xmpp_stanza_add_child(iq, query);
xmpp_stanza_release(query);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "username");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
goto err_free;
str = xmpp_jid_node(conn->ctx, conn->jid);
if (!str) {
xmpp_stanza_release(authdata);
goto err_free;
}
xmpp_stanza_set_text(authdata, str);
xmpp_free(conn->ctx, str);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "password");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
goto err_free;
xmpp_stanza_set_text(authdata, conn->pass);
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
child = xmpp_stanza_new(conn->ctx);
if (!child)
goto err_free;
xmpp_stanza_set_name(child, "resource");
xmpp_stanza_add_child(query, child);
xmpp_stanza_release(child);
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
goto err_free;
str = xmpp_jid_resource(conn->ctx, conn->jid);
if (str) {
xmpp_stanza_set_text(authdata, str);
xmpp_free(conn->ctx, str);
} else {
xmpp_stanza_release(authdata);
xmpp_stanza_release(iq);
xmpp_error(conn->ctx, "auth", "Cannot authenticate without resource");
xmpp_disconnect(conn);
return;
}
xmpp_stanza_add_child(child, authdata);
xmpp_stanza_release(authdata);
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL);
xmpp_send(conn, iq);
xmpp_stanza_release(iq);
return;
err_free:
xmpp_stanza_release(iq);
err:
disconnect_mem_error(conn);
} }
void auth_handle_component_open(xmpp_conn_t * const conn) void auth_handle_component_open(xmpp_conn_t * const conn)

View File

@@ -160,6 +160,7 @@ struct _xmpp_conn_t {
xmpp_stream_error_t *stream_error; xmpp_stream_error_t *stream_error;
sock_t sock; sock_t sock;
xmpp_sock_t *xsock;
int ka_timeout; /* TCP keepalive timeout */ int ka_timeout; /* TCP keepalive timeout */
int ka_interval; /* TCP keepalive interval */ int ka_interval; /* TCP keepalive interval */
@@ -172,6 +173,7 @@ struct _xmpp_conn_t {
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 */

View File

@@ -73,6 +73,15 @@ 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
@@ -131,6 +140,7 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
conn->tls_trust = 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;
@@ -679,7 +689,8 @@ void conn_disconnect(xmpp_conn_t * const conn)
tls_free(conn->tls); tls_free(conn->tls);
conn->tls = NULL; conn->tls = NULL;
} }
sock_close(conn->sock); if (conn->sock >= 0)
sock_close(conn->sock);
/* fire off connection handler */ /* fire off connection handler */
conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error, conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
@@ -704,7 +715,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
* *
@@ -746,10 +757,10 @@ 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 */
@@ -770,7 +781,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);
} }
} }
@@ -835,10 +845,9 @@ void xmpp_send(xmpp_conn_t * const conn,
{ {
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);
@@ -913,7 +922,8 @@ 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_TRUST_TLS * conn->tls_trust |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;;
return flags; return flags;
} }
@@ -931,6 +941,7 @@ long xmpp_conn_get_flags(const xmpp_conn_t * const conn)
* - 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_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
@@ -957,6 +968,7 @@ int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags)
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->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;
} }
@@ -1202,6 +1214,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);
@@ -1249,7 +1264,8 @@ static int _conn_connect(xmpp_conn_t * const conn,
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->xsock = sock_new(conn->ctx, host, port);
conn->sock = sock_connect(conn->xsock);
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, port, conn->sock); host, port, conn->sock);
if (conn->sock == -1) return XMPP_EINT; if (conn->sock == -1) return XMPP_EINT;

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,7 +81,7 @@ 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);
} }
/** Compute SHA1 message digest /** Compute SHA1 message digest.
* Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE * Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE
* bytes long. * bytes long.
* *
@@ -97,7 +97,7 @@ void xmpp_sha1_digest(const unsigned char *data, size_t len,
crypto_SHA1((const uint8_t *)data, len, digest); crypto_SHA1((const uint8_t *)data, len, digest);
} }
/** Create new SHA1 object /** Create new SHA1 object.
* SHA1 object is used to compute SHA1 digest of a buffer that is split * 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 * in multiple chunks or provided in stream mode. A single buffer can be
* processed by short functions xmpp_sha1() and xmpp_sha1_digest(). * processed by short functions xmpp_sha1() and xmpp_sha1_digest().
@@ -111,7 +111,7 @@ void xmpp_sha1_digest(const unsigned char *data, size_t len,
* xmpp_sha1_free(sha1); * xmpp_sha1_free(sha1);
* @endcode * @endcode
* *
* @param ctx a Strophe context onject * @param ctx a Strophe context object
* *
* @return new SHA1 object * @return new SHA1 object
* *
@@ -130,7 +130,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
* *
@@ -141,7 +141,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
@@ -155,7 +155,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().
* *
@@ -168,7 +168,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().
* *
@@ -185,7 +185,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().
@@ -201,7 +201,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
@@ -419,7 +419,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
@@ -435,7 +435,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.
@@ -471,7 +471,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

View File

@@ -47,6 +47,7 @@
#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 visual studio without va_copy support. */
@@ -63,6 +64,7 @@
void xmpp_initialize(void) void xmpp_initialize(void)
{ {
sock_initialize(); sock_initialize();
resolver_initialize();
tls_initialize(); tls_initialize();
} }
@@ -73,6 +75,7 @@
void xmpp_shutdown(void) void xmpp_shutdown(void)
{ {
tls_shutdown(); tls_shutdown();
resolver_shutdown();
sock_shutdown(); sock_shutdown();
} }

View File

@@ -51,6 +51,18 @@
#include "common.h" #include "common.h"
#include "parser.h" #include "parser.h"
static int _connect_next(xmpp_conn_t *conn)
{
sock_close(conn->sock);
conn->sock = sock_connect(conn->xsock);
if (conn->sock < 0)
return -1;
conn->timeout_stamp = time_stamp();
return 0;
}
/** 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
* xmpp_send and related functions and run through the Strophe even * xmpp_send and related functions and run through the Strophe even
@@ -180,6 +192,11 @@ 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 {
ret = _connect_next(conn);
if (ret == 0) {
FD_SET(conn->sock, &wfds);
break;
}
conn->error = ETIMEDOUT; conn->error = ETIMEDOUT;
xmpp_info(ctx, "xmpp", "Connection attempt timed out."); xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
conn_disconnect(conn); conn_disconnect(conn);
@@ -238,6 +255,11 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
/* check for error */ /* check for error */
ret = sock_connect_error(conn->sock); ret = sock_connect_error(conn->sock);
if (ret != 0) {
ret = _connect_next(conn);
if (ret == 0)
break;
}
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);
@@ -262,10 +284,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
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, "parse error");
xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
conn_disconnect(conn);
} }
} else { } else {
if (conn->tls) { if (conn->tls) {

View File

@@ -163,6 +163,8 @@ int hash_add(hash_t *table, const char * const key, void *data)
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; entry->value = data;
@@ -241,7 +243,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);
} }

View File

@@ -137,7 +137,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 */

View File

@@ -249,6 +249,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);
} }
@@ -257,18 +259,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->stanza = NULL;
parser->depth = 0;
parser->xmlctx = xmlCreatePushParserCtxt(&parser->handlers, parser->xmlctx = xmlCreatePushParserCtxt(&parser->handlers,
parser, NULL, 0, NULL); parser, NULL, 0, NULL);
if (!parser->xmlctx) return 0;
parser->depth = 0; return parser->xmlctx ? 1 : 0;
parser->stanza = NULL;
return 1;
} }
/* feed a chunk of data to the parser */ /* feed a chunk of data to the parser */

View File

@@ -127,9 +127,11 @@ 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);
memcpy(seed_material + entropy_input_len, nonce, nonce_len); if (nonce != NULL)
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;

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

View File

@@ -1,4 +1,4 @@
/* resolver.h /* resolver.c
* strophe XMPP client library -- DNS resolver * strophe XMPP client library -- DNS resolver
* *
* Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com> * Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
@@ -13,11 +13,15 @@
* DNS resolver. * DNS resolver.
*/ */
#ifndef _WIN32 #if !defined(_WIN32) && !defined(HAVE_CARES)
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/nameser.h> #include <arpa/nameser.h>
#include <resolv.h> /* res_query */ #include <resolv.h> /* res_query */
#endif /* _WIN32 */ #endif /* _WIN32 && HAVE_CARES */
#ifdef HAVE_CARES
#include <ares.h>
#endif /* HAVE_CARES */
#include <string.h> /* strncpy */ #include <string.h> /* strncpy */
@@ -31,6 +35,189 @@
#define MESSAGE_T_SRV 33 #define MESSAGE_T_SRV 33
#define MESSAGE_C_IN 1 #define MESSAGE_C_IN 1
/*******************************************************************************
* Forward declarations.
******************************************************************************/
#ifdef HAVE_CARES
static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
const unsigned char *buf,
size_t len,
resolver_srv_rr_t **srv_rr_list);
static int resolver_ares_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
resolver_srv_rr_t **srv_rr_list);
#endif /* HAVE_CARES */
#ifndef HAVE_CARES
static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
const unsigned char *buf,
size_t len,
resolver_srv_rr_t **srv_rr_list);
#endif /* !HAVE_CARES */
#ifdef _WIN32
static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
resolver_srv_rr_t **srv_rr_list);
static int resolver_win32_srv_query(const char *fulldomain,
unsigned char *buf, size_t len);
#endif /* _WIN32 */
/*******************************************************************************
* Implementation.
******************************************************************************/
void resolver_initialize(void)
{
#ifdef HAVE_CARES
ares_library_init(ARES_LIB_INIT_ALL);
#endif
}
void resolver_shutdown(void)
{
#ifdef HAVE_CARES
ares_library_cleanup();
#endif
}
static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
{
resolver_srv_rr_t *rr_head;
resolver_srv_rr_t *rr_current;
resolver_srv_rr_t *rr_next;
resolver_srv_rr_t *rr_prev;
int swap;
rr_head = *srv_rr_list;
if ((rr_head == NULL) || (rr_head->next == NULL)) {
/* Empty or single record list */
return;
}
do {
rr_prev = NULL;
rr_current = rr_head;
rr_next = rr_head->next;
swap = 0;
while (rr_next != NULL) {
/*
* RFC2052: A client MUST attempt to contact the target host
* with the lowest-numbered priority it can reach.
* RFC2052: When selecting a target host among the
* those that have the same priority, the chance of trying
* this one first SHOULD be proportional to its weight.
*/
if ((rr_current->priority > rr_next->priority) ||
(rr_current->priority == rr_next->priority &&
rr_current->weight < rr_next->weight))
{
/* Swap node */
swap = 1;
if (rr_prev != NULL) {
rr_prev->next = rr_next;
} else {
/* Swap head node */
rr_head = rr_next;
}
rr_current->next = rr_next->next;
rr_next->next = rr_current;
rr_prev = rr_next;
rr_next = rr_current->next;
} else {
/* Next node */
rr_prev = rr_current;
rr_current = rr_next;
rr_next = rr_next->next;
}
}
} while (swap != 0);
*srv_rr_list = rr_head;
}
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf,
size_t len, resolver_srv_rr_t **srv_rr_list)
{
int set;
#ifdef HAVE_CARES
set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
#else
set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list);
#endif
resolver_srv_list_sort(srv_rr_list);
return set;
}
int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
const char *domain, resolver_srv_rr_t **srv_rr_list)
{
#define RESOLVER_BUF_MAX 65536
unsigned char *buf;
char fulldomain[2048];
int len;
int set = XMPP_DOMAIN_NOT_FOUND;
(void)buf;
(void)len;
xmpp_snprintf(fulldomain, sizeof(fulldomain),
"_%s._%s.%s", service, proto, domain);
*srv_rr_list = NULL;
#ifdef HAVE_CARES
set = resolver_ares_srv_lookup(ctx, fulldomain, srv_rr_list);
#else /* HAVE_CARES */
#ifdef _WIN32
set = resolver_win32_srv_lookup(ctx, fulldomain, srv_rr_list);
if (set == XMPP_DOMAIN_FOUND)
return set;
#endif /* _WIN32 */
buf = xmpp_alloc(ctx, RESOLVER_BUF_MAX);
if (buf == NULL)
return XMPP_DOMAIN_NOT_FOUND;
#ifdef _WIN32
len = resolver_win32_srv_query(fulldomain, buf, RESOLVER_BUF_MAX);
#else /* _WIN32 */
len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf,
RESOLVER_BUF_MAX);
#endif /* _WIN32 */
if (len > 0)
set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
xmpp_free(ctx, buf);
#endif /* HAVE_CARES */
return set;
}
void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list)
{
resolver_srv_rr_t *rr;
while (srv_rr_list != NULL) {
rr = srv_rr_list->next;
xmpp_free(ctx, srv_rr_list);
srv_rr_list = rr;
}
}
#ifndef HAVE_CARES
/*******************************************************************************
* Resolver raw implementation.
*
* This code is common for both unix and win32.
******************************************************************************/
struct message_header { struct message_header {
uint16_t id; uint16_t id;
uint8_t octet2; uint8_t octet2;
@@ -41,13 +228,6 @@ struct message_header {
uint16_t arcount; uint16_t arcount;
}; };
#ifdef _WIN32
static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
resolver_srv_rr_t **srv_rr_list);
static int resolver_win32_srv_query(const char *fulldomain,
unsigned char *buf, size_t len);
#endif /* _WIN32 */
/* the same as ntohs(), but receives pointer to the value */ /* the same as ntohs(), but receives pointer to the value */
static uint16_t xmpp_ntohs_ptr(const void *ptr) static uint16_t xmpp_ntohs_ptr(const void *ptr)
{ {
@@ -80,7 +260,7 @@ static size_t message_name_append_safe(char *name, size_t name_len,
copy_len = name_max > name_len ? name_max - name_len : 0; copy_len = name_max > name_len ? name_max - name_len : 0;
copy_len = xmpp_min(tail_len, copy_len); copy_len = xmpp_min(tail_len, copy_len);
if (copy_len > 0) if (copy_len > 0)
strncpy(&name[name_len], tail, copy_len); memcpy(&name[name_len], tail, copy_len);
return name_len + tail_len; return name_len + tail_len;
} }
@@ -160,63 +340,6 @@ static unsigned message_name_len(const unsigned char *buf, size_t buf_len,
return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX); return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX);
} }
static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
{
resolver_srv_rr_t * rr_head;
resolver_srv_rr_t * rr_current;
resolver_srv_rr_t * rr_next;
resolver_srv_rr_t * rr_prev;
int swap;
rr_head = *srv_rr_list;
if ((rr_head == NULL) || (rr_head->next == NULL)) {
/* Empty or single record list */
return;
}
do {
rr_prev = NULL;
rr_current = rr_head;
rr_next = rr_head->next;
swap = 0;
while (rr_next != NULL) {
/*
* RFC2052: A client MUST attempt to contact the target host
* with the lowest-numbered priority it can reach.
* RFC2052: When selecting a target host among the
* those that have the same priority, the chance of trying
* this one first SHOULD be proportional to its weight.
*/
if ((rr_current->priority > rr_next->priority) ||
(rr_current->priority == rr_next->priority &&
rr_current->weight < rr_next->weight))
{
/* Swap node */
swap = 1;
if (rr_prev != NULL) {
rr_prev->next = rr_next;
} else {
/* Swap head node */
rr_head = rr_next;
}
rr_current->next = rr_next->next;
rr_next->next = rr_current;
rr_prev = rr_next;
rr_next = rr_current->next;
} else {
/* Next node */
rr_prev = rr_current;
rr_current = rr_next;
rr_next = rr_next->next;
}
}
} while (swap != 0);
*srv_rr_list = rr_head;
}
#define BUF_OVERFLOW_CHECK(ptr, len) do { \ #define BUF_OVERFLOW_CHECK(ptr, len) do { \
if ((ptr) >= (len)) { \ if ((ptr) >= (len)) { \
if (*srv_rr_list != NULL) \ if (*srv_rr_list != NULL) \
@@ -226,8 +349,10 @@ static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
} \ } \
} while (0) } while (0)
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf, static int resolver_raw_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)
{ {
unsigned i; unsigned i;
unsigned j; unsigned j;
@@ -292,52 +417,110 @@ int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf,
} }
j += rdlength; j += rdlength;
} }
resolver_srv_list_sort(srv_rr_list);
return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND; return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
} }
int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto, #endif /* !HAVE_CARES */
const char *domain, resolver_srv_rr_t **srv_rr_list)
{
char fulldomain[2048];
unsigned char buf[65535];
int len;
int set = XMPP_DOMAIN_NOT_FOUND;
xmpp_snprintf(fulldomain, sizeof(fulldomain), #ifdef HAVE_CARES
"_%s._%s.%s", service, proto, domain); /*******************************************************************************
* Resolver implementation using c-ares library.
******************************************************************************/
struct resolver_ares_ctx {
xmpp_ctx_t *ctx;
int result;
resolver_srv_rr_t *srv_rr_list;
};
static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
const unsigned char *buf,
size_t len,
resolver_srv_rr_t **srv_rr_list)
{
struct ares_srv_reply *srv;
struct ares_srv_reply *item;
resolver_srv_rr_t *rr;
int rc;
*srv_rr_list = NULL; *srv_rr_list = NULL;
#ifdef _WIN32 rc = ares_parse_srv_reply(buf, len, &srv);
set = resolver_win32_srv_lookup(ctx, fulldomain, srv_rr_list); if (rc != ARES_SUCCESS)
if (set == XMPP_DOMAIN_FOUND) return XMPP_DOMAIN_NOT_FOUND;
return set;
len = resolver_win32_srv_query(fulldomain, buf, sizeof(buf));
#else /* _WIN32 */
len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf, sizeof(buf));
#endif /* _WIN32 */
if (len > 0) item = srv;
set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list); while (item != NULL) {
rr = xmpp_alloc(ctx, sizeof(*rr));
return set; if (rr == NULL)
} break;
rr->next = *srv_rr_list;
void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list) rr->priority = item->priority;
{ rr->weight = item->weight;
resolver_srv_rr_t *rr; rr->port = item->port;
strncpy(rr->target, item->host, sizeof(rr->target) - 1);
while (srv_rr_list != NULL) { rr->target[sizeof(rr->target) - 1] = '\0';
rr = srv_rr_list->next; *srv_rr_list = rr;
xmpp_free(ctx, srv_rr_list); item = item->next;
srv_rr_list = rr;
} }
ares_free_data(srv);
return *srv_rr_list == NULL ? XMPP_DOMAIN_NOT_FOUND : XMPP_DOMAIN_FOUND;
} }
#ifdef _WIN32 static void ares_srv_lookup_callback(void *arg, int status, int timeouts,
unsigned char *buf, int len)
{
struct resolver_ares_ctx *actx = arg;
if (status != ARES_SUCCESS)
actx->result = XMPP_DOMAIN_NOT_FOUND;
else
actx->result = resolver_ares_srv_lookup_buf(actx->ctx, buf, len,
&actx->srv_rr_list);
}
static int resolver_ares_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
resolver_srv_rr_t **srv_rr_list)
{
struct resolver_ares_ctx actx;
ares_channel chan;
struct timeval tv;
struct timeval *tvp;
fd_set rfds;
fd_set wfds;
int nfds;
int rc;
actx.ctx = ctx;
actx.result = XMPP_DOMAIN_NOT_FOUND;
actx.srv_rr_list = NULL;
rc = ares_init(&chan);
if (rc == ARES_SUCCESS) {
ares_query(chan, fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV,
ares_srv_lookup_callback, &actx);
while (1) {
FD_ZERO(&rfds);
FD_ZERO(&wfds);
nfds = ares_fds(chan, &rfds, &wfds);
if (nfds == 0)
break;
tvp = ares_timeout(chan, NULL, &tv);
select(nfds, &rfds, &wfds, NULL, tvp);
ares_process(chan, &rfds, &wfds);
}
ares_destroy(chan);
}
*srv_rr_list = actx.srv_rr_list;
return actx.result;
}
#endif /* HAVE_CARES */
#ifdef _WIN32
/******************************************************************************* /*******************************************************************************
* Next part was copied from sock.c and contains old win32 code. * Next part was copied from sock.c and contains old win32 code.
* *
@@ -470,21 +653,21 @@ static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
resolver_srv_rr_t *rr; resolver_srv_rr_t *rr;
HINSTANCE hdnsapi = NULL; HINSTANCE hdnsapi = NULL;
DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, PDNS_RECORD*, PVOID*); DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, DNS_RECORDA**, PVOID*);
void (WINAPI * pDnsRecordListFree)(PDNS_RECORD, DNS_FREE_TYPE); void (WINAPI * pDnsRecordListFree)(DNS_RECORDA*, DNS_FREE_TYPE);
if (hdnsapi = LoadLibrary("dnsapi.dll")) { if (hdnsapi = LoadLibrary("dnsapi.dll")) {
pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A"); pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A");
pDnsRecordListFree = (void *)GetProcAddress(hdnsapi, "DnsRecordListFree"); pDnsRecordListFree = (void *)GetProcAddress(hdnsapi, "DnsRecordListFree");
if (pDnsQuery_A && pDnsRecordListFree) { if (pDnsQuery_A && pDnsRecordListFree) {
PDNS_RECORD dnsrecords = NULL; DNS_RECORDA *dnsrecords = NULL;
DNS_STATUS error; DNS_STATUS error;
error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL); error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL);
if (error == 0) { if (error == 0) {
PDNS_RECORD current = dnsrecords; DNS_RECORDA *current = dnsrecords;
while (current) { while (current) {
if (current->wType == DNS_TYPE_SRV) { if (current->wType == DNS_TYPE_SRV) {
@@ -517,7 +700,7 @@ static int resolver_win32_srv_query(const char *fulldomain,
unsigned char *buf, size_t len) unsigned char *buf, size_t len)
{ {
int set = 0; int set = 0;
int insize; int insize = 0;
/* if dnsapi didn't work/isn't there, try querying the dns server manually */ /* if dnsapi didn't work/isn't there, try querying the dns server manually */
if (!set) if (!set)
@@ -550,7 +733,7 @@ static int resolver_win32_srv_query(const char *fulldomain,
char buffer[65535]; char buffer[65535];
len = 65535; len = 65535;
fi = buffer; fi = (FIXED_INFO *)buffer;
if ((error = pGetNetworkParams(fi, &len)) == ERROR_SUCCESS) if ((error = pGetNetworkParams(fi, &len)) == ERROR_SUCCESS)
{ {

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

View File

@@ -264,7 +264,8 @@ 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"));
hash_add(table, "qop", xmpp_strdup(ctx, "auth")); if (hash_get(table, "qop") == NULL)
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);
memcpy(value+5, domain, strlen(domain)); memcpy(value+5, domain, strlen(domain));

View File

@@ -197,8 +197,9 @@ void crypto_SHA1_Update(SHA1_CTX* context, const uint8_t* data,
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);

View File

@@ -33,9 +33,17 @@
#include <fcntl.h> #include <fcntl.h>
#endif #endif
#include "common.h"
#include "sock.h" #include "sock.h"
#include "snprintf.h" #include "snprintf.h"
struct _xmpp_sock_t {
xmpp_ctx_t *ctx;
struct addrinfo *ainfo_list;
struct addrinfo *current;
sock_t sock;
};
void sock_initialize(void) void sock_initialize(void)
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -69,13 +77,23 @@ static int _in_progress(int error)
#endif #endif
} }
sock_t sock_connect(const char * const host, const unsigned short port) xmpp_sock_t *sock_new(xmpp_ctx_t *ctx, const char *host, unsigned short port)
{ {
sock_t sock; xmpp_sock_t *xsock;
char service[6]; char service[6];
struct addrinfo *res, *ainfo, hints; struct addrinfo hints;
int err; int err;
xsock = xmpp_alloc(ctx, sizeof *xsock);
if (xsock == NULL) {
xmpp_error(ctx, "sock", "Memory allocation error.");
return NULL;
}
xsock->ctx = ctx;
xsock->ainfo_list = NULL;
xsock->current = NULL;
xsock->sock = -1;
xmpp_snprintf(service, 6, "%u", port); xmpp_snprintf(service, 6, "%u", port);
memset(&hints, 0, sizeof(struct addrinfo)); memset(&hints, 0, sizeof(struct addrinfo));
@@ -86,11 +104,33 @@ sock_t sock_connect(const char * const host, const unsigned short port)
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(host, service, &hints, &res); err = getaddrinfo(host, service, &hints, &xsock->ainfo_list);
if (err != 0) if (err != 0) {
return -1; xmpp_free(ctx, xsock);
xsock = NULL;
}
return xsock;
}
for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) { void sock_free(xmpp_sock_t *xsock)
{
if (xsock->ainfo_list != NULL)
freeaddrinfo(xsock->ainfo_list);
xmpp_free(xsock->ctx, xsock);
}
sock_t sock_connect(xmpp_sock_t *xsock)
{
struct addrinfo *ainfo;
sock_t sock = -1;
int err;
if (xsock->current == NULL)
ainfo = xsock->ainfo_list;
else
ainfo = xsock->current->ai_next;
for (; ainfo != NULL; ainfo = ainfo->ai_next) {
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock < 0) if (sock < 0)
continue; continue;
@@ -103,9 +143,13 @@ sock_t sock_connect(const char * const host, const unsigned short port)
} }
sock_close(sock); sock_close(sock);
} }
freeaddrinfo(res);
sock = ainfo == NULL ? -1 : sock;
xsock->sock = sock;
xsock->current = ainfo;
if (ainfo == NULL) {
freeaddrinfo(xsock->ainfo_list);
xsock->ainfo_list = NULL;
}
return sock; return sock;
} }
@@ -158,38 +202,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);

View File

@@ -25,12 +25,16 @@ typedef int sock_t;
typedef SOCKET sock_t; typedef SOCKET sock_t;
#endif #endif
typedef struct _xmpp_sock_t xmpp_sock_t;
void sock_initialize(void); void sock_initialize(void);
void sock_shutdown(void); void sock_shutdown(void);
int sock_error(void); int sock_error(void);
sock_t sock_connect(const char * const host, const unsigned short port); xmpp_sock_t *sock_new(xmpp_ctx_t *ctx, const char *host, unsigned short port);
void sock_free(xmpp_sock_t *xsock);
sock_t sock_connect(xmpp_sock_t *xsock);
int sock_close(const sock_t sock); int sock_close(const sock_t sock);
int sock_set_blocking(const sock_t sock); int sock_set_blocking(const sock_t sock);

View File

@@ -1,7 +1,7 @@
/* stanza.c /* stanza.c
** strophe XMPP client library -- XMPP stanza object and utilities ** strophe XMPP client library -- XMPP stanza object and utilities
** **
** Copyright (C) 2005-2009 Collecta, Inc. ** Copyright (C) 2005-2009 Collecta, Inc.
** **
** This software is provided AS-IS with no warranty, either express ** This software is provided AS-IS with no warranty, either express
** or implied. ** or implied.
@@ -40,15 +40,15 @@ xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t)); stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t));
if (stanza != NULL) { if (stanza != NULL) {
stanza->ref = 1; stanza->ref = 1;
stanza->ctx = ctx; stanza->ctx = ctx;
stanza->type = XMPP_STANZA_UNKNOWN; stanza->type = XMPP_STANZA_UNKNOWN;
stanza->prev = NULL; stanza->prev = NULL;
stanza->next = NULL; stanza->next = NULL;
stanza->children = NULL; stanza->children = NULL;
stanza->parent = NULL; stanza->parent = NULL;
stanza->data = NULL; stanza->data = NULL;
stanza->attributes = NULL; stanza->attributes = NULL;
} }
return stanza; return stanza;
@@ -121,27 +121,27 @@ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
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) {
if (_stanza_copy_attributes(copy, stanza) == -1) if (_stanza_copy_attributes(copy, stanza) == -1)
goto copy_error; goto copy_error;
} }
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) {
copychild->prev = tail; copychild->prev = tail;
tail->next = copychild; tail->next = copychild;
} else } else
copy->children = copychild; copy->children = copychild;
tail = copychild; tail = copychild;
} }
return copy; return copy;
@@ -153,7 +153,7 @@ copy_error:
} }
/** Release a stanza object and all of its children. /** Release a stanza object and all of its children.
* This function releases a stanza object and potentially all of its * This function releases a stanza object and potentially all of its
* children, which may cause the object(s) to be freed. * children, which may cause the object(s) to be freed.
* *
* @param stanza a Strophe stanza object * @param stanza a Strophe stanza object
@@ -169,20 +169,20 @@ int xmpp_stanza_release(xmpp_stanza_t * const stanza)
/* release stanza */ /* release stanza */
if (stanza->ref > 1) if (stanza->ref > 1)
stanza->ref--; stanza->ref--;
else { else {
/* release all children */ /* release all children */
child = stanza->children; child = stanza->children;
while (child) { while (child) {
tchild = child; tchild = child;
child = child->next; child = child->next;
xmpp_stanza_release(tchild); xmpp_stanza_release(tchild);
} }
if (stanza->attributes) hash_release(stanza->attributes); if (stanza->attributes) hash_release(stanza->attributes);
if (stanza->data) xmpp_free(stanza->ctx, stanza->data); if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
xmpp_free(stanza->ctx, stanza); xmpp_free(stanza->ctx, stanza);
released = 1; released = 1;
} }
return released; return released;
@@ -274,17 +274,17 @@ 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;
if (*written >= length) { if (*written >= length) {
*left = 0; *left = 0;
*ptr = NULL; *ptr = NULL;
} else { } else {
*left -= lastwrite; *left -= lastwrite;
*ptr = &(*ptr)[lastwrite]; *ptr = &(*ptr)[lastwrite];
} }
} }
@@ -294,7 +294,7 @@ 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;
@@ -309,80 +309,86 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza,
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) {
iter = hash_iter_new(stanza->attributes); iter = hash_iter_new(stanza->attributes);
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 */
if (!stanza->parent && if (!stanza->parent &&
!strcmp((char*)hash_get(stanza->attributes, key), !strcmp((char*)hash_get(stanza->attributes, key),
XMPP_NS_CLIENT)) XMPP_NS_CLIENT))
continue; continue;
} }
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) {
ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); hash_iter_release(iter);
xmpp_free(stanza->ctx, tmp); return XMPP_EMEM;
if (ret < 0) return XMPP_EMEM; }
_render_update(&written, buflen, ret, &left, &ptr); ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
} xmpp_free(stanza->ctx, tmp);
hash_iter_release(iter); if (ret < 0) {
} hash_iter_release(iter);
return XMPP_EMEM;
}
_render_update(&written, buflen, ret, &left, &ptr);
}
hash_iter_release(iter);
}
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 */
child = stanza->children;
while (child) {
ret = _render_stanza_recursive(child, ptr, left);
if (ret < 0) return ret;
_render_update(&written, buflen, ret, &left, &ptr); /* iterate and recurse over child stanzas */
child = stanza->children;
while (child) {
ret = _render_stanza_recursive(child, ptr, left);
if (ret < 0) return ret;
child = child->next; _render_update(&written, buflen, ret, &left, &ptr);
}
/* write end tag */ child = child->next;
ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); }
if (ret < 0) return XMPP_EMEM;
/* write end tag */
_render_update(&written, buflen, ret, &left, &ptr); ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data);
} if (ret < 0) return XMPP_EMEM;
_render_update(&written, buflen, ret, &left, &ptr);
}
} }
return written; return written;
@@ -415,27 +421,37 @@ int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
length = 1024; length = 1024;
buffer = xmpp_alloc(stanza->ctx, length); buffer = xmpp_alloc(stanza->ctx, length);
if (!buffer) { if (!buffer) {
*buf = NULL; *buf = NULL;
*buflen = 0; *buflen = 0;
return XMPP_EMEM; return XMPP_EMEM;
} }
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 ((size_t)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);
*buf = NULL; *buf = NULL;
*buflen = 0; *buflen = 0;
return XMPP_EMEM; return XMPP_EMEM;
} }
length = ret + 1; length = ret + 1;
buffer = tmp; buffer = tmp;
ret = _render_stanza_recursive(stanza, buffer, length); ret = _render_stanza_recursive(stanza, buffer, length);
if ((size_t)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;
@@ -457,7 +473,7 @@ 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;
@@ -496,7 +512,7 @@ const char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza)
int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza) int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza)
{ {
if (stanza->attributes == NULL) { if (stanza->attributes == NULL) {
return 0; return 0;
} }
return hash_num_keys(stanza->attributes); return hash_num_keys(stanza->attributes);
@@ -517,30 +533,30 @@ 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;
int num = 0; int num = 0;
if (stanza->attributes == NULL) { if (stanza->attributes == NULL) {
return 0; return 0;
} }
iter = hash_iter_new(stanza->attributes); iter = hash_iter_new(stanza->attributes);
while ((key = hash_iter_next(iter)) != NULL && attrlen) { while ((key = hash_iter_next(iter)) != NULL && attrlen) {
attr[num++] = key; attr[num++] = key;
attrlen--; attrlen--;
if (attrlen == 0) { if (attrlen == 0) {
hash_iter_release(iter); hash_iter_release(iter);
return num; return num;
} }
attr[num++] = hash_get(stanza->attributes, key); attr[num++] = hash_get(stanza->attributes, key);
attrlen--; attrlen--;
if (attrlen == 0) { if (attrlen == 0) {
hash_iter_release(iter); hash_iter_release(iter);
return num; return num;
} }
} }
hash_iter_release(iter); hash_iter_release(iter);
@@ -558,8 +574,8 @@ int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza,
* @ingroup Stanza * @ingroup 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)
{ {
char *val; char *val;
int rc; int rc;
@@ -567,13 +583,12 @@ int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
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;
} }
@@ -598,7 +613,7 @@ 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);
} }
@@ -624,12 +639,12 @@ int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
child->parent = stanza; child->parent = stanza;
if (!stanza->children) if (!stanza->children)
stanza->children = child; stanza->children = child;
else { else {
s = stanza->children; s = stanza->children;
while (s->next) s = s->next; while (s->next) s = s->next;
s->next = child; s->next = child;
child->prev = s; child->prev = s;
} }
return XMPP_EOK; return XMPP_EOK;
@@ -649,7 +664,7 @@ 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;
@@ -676,8 +691,8 @@ int xmpp_stanza_set_text(xmpp_stanza_t *stanza,
* @ingroup Stanza * @ingroup Stanza
*/ */
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)
{ {
if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TAG) return XMPP_EINVOP;
@@ -780,14 +795,14 @@ const char *xmpp_stanza_get_from(xmpp_stanza_t * const stanza)
* @ingroup Stanza * @ingroup 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 *child; xmpp_stanza_t *child;
for (child = stanza->children; child; child = child->next) { for (child = stanza->children; child; child = child->next) {
if (child->type == XMPP_STANZA_TAG && if (child->type == XMPP_STANZA_TAG &&
(strcmp(name, xmpp_stanza_get_name(child)) == 0)) (strcmp(name, xmpp_stanza_get_name(child)) == 0))
break; break;
} }
return child; return child;
@@ -806,14 +821,15 @@ xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
* @ingroup Stanza * @ingroup Stanza
*/ */
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 *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;
@@ -866,16 +882,16 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
char *text; char *text;
if (stanza->type == XMPP_STANZA_TEXT) { if (stanza->type == XMPP_STANZA_TEXT) {
if (stanza->data) if (stanza->data)
return xmpp_strdup(stanza->ctx, stanza->data); return xmpp_strdup(stanza->ctx, stanza->data);
else else
return NULL; return NULL;
} }
len = 0; len = 0;
for (child = stanza->children; child; child = child->next) for (child = stanza->children; child; child = child->next)
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;
@@ -884,11 +900,11 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
len = 0; len = 0;
for (child = stanza->children; child; child = child->next) for (child = stanza->children; child; child = child->next)
if (child->type == XMPP_STANZA_TEXT) { if (child->type == XMPP_STANZA_TEXT) {
clen = strlen(child->data); clen = strlen(child->data);
memcpy(&text[len], child->data, clen); memcpy(&text[len], child->data, clen);
len += clen; len += clen;
} }
text[len] = 0; text[len] = 0;
@@ -911,7 +927,7 @@ char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza) const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza)
{ {
if (stanza->type == XMPP_STANZA_TEXT) if (stanza->type == XMPP_STANZA_TEXT)
return stanza->data; return stanza->data;
return NULL; return NULL;
} }
@@ -928,7 +944,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);
} }
@@ -945,7 +961,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);
} }
@@ -1001,10 +1017,10 @@ const char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
const char * const name) const char * const name)
{ {
if (stanza->type != XMPP_STANZA_TAG) if (stanza->type != XMPP_STANZA_TAG)
return NULL; return NULL;
if (!stanza->attributes) if (!stanza->attributes)
return NULL; return NULL;
return hash_get(stanza->attributes, name); return hash_get(stanza->attributes, name);
} }
@@ -1218,3 +1234,122 @@ 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;
}

View File

@@ -31,6 +31,22 @@
#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;
@@ -48,6 +64,7 @@ enum {
static void _tls_sock_wait(tls_t *tls, int error); static void _tls_sock_wait(tls_t *tls, int error);
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);
void tls_initialize(void) void tls_initialize(void)
{ {
@@ -188,6 +205,7 @@ int tls_start(tls_t *tls)
x509_res = SSL_get_verify_result(tls->ssl); x509_res = SSL_get_verify_result(tls->ssl);
xmpp_debug(tls->ctx, "tls", "Certificate verification %s", xmpp_debug(tls->ctx, "tls", "Certificate verification %s",
x509_res == X509_V_OK ? "passed" : "FAILED"); x509_res == X509_V_OK ? "passed" : "FAILED");
_tls_dump_cert_info(tls);
_tls_set_error(tls, error); _tls_set_error(tls, error);
return ret <= 0 ? 0 : 1; return ret <= 0 ? 0 : 1;
@@ -199,6 +217,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);
@@ -209,6 +232,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;
@@ -260,6 +291,8 @@ 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)
@@ -278,6 +311,7 @@ static void _tls_sock_wait(tls_t *tls, int error)
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=%d errno=%d", error, errno);
_tls_log_error(tls->ctx); _tls_log_error(tls->ctx);
} }
tls->lasterror = error; tls->lasterror = error;
@@ -296,3 +330,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

@@ -255,10 +255,18 @@ int tls_start(tls_t *tls)
NULL, 0, &(tls->hctxt), &sbdout, NULL, 0, &(tls->hctxt), &sbdout,
&ctxtattr, NULL); &ctxtattr, NULL);
unsigned char *p = sbin[0].pvBuffer;
int len = 0;
while (ret == SEC_I_CONTINUE_NEEDED while (ret == SEC_I_CONTINUE_NEEDED
|| ret == SEC_I_INCOMPLETE_CREDENTIALS) { || ret == SEC_I_INCOMPLETE_CREDENTIALS
unsigned char *p = sbin[0].pvBuffer; || ret == SEC_E_INCOMPLETE_MESSAGE) {
int len = 0, inbytes = 0; 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;

View File

@@ -169,6 +169,10 @@ typedef struct _xmpp_stanza_t xmpp_stanza_t;
* Trust server's certificate even if it is invalid. * Trust server's certificate even if it is invalid.
*/ */
#define XMPP_CONN_FLAG_TRUST_TLS (1UL << 3) #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 {
@@ -217,6 +221,7 @@ 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);
@@ -381,6 +386,8 @@ 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 *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type,
const char * const id); 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 */

View File

@@ -16,18 +16,19 @@
#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[] = { const char *keys[] = {
"foo", "bar", "baz", "quux", "xyzzy" "foo", "bar", "baz", "quux", "xyzzy"
}; };
const char *values[] = { const char *values[] = {
"wuzzle", "mug", "canonical", "rosebud", "lottery" "wuzzle", "mug", "canonical", "rosebud", "lottery"
}; };
const int nkeys = ARRAY_SIZE(keys);
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -58,7 +59,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,7 +67,7 @@ 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;
} }
@@ -78,7 +79,7 @@ int main(int argc, char **argv)
/* test replacing old values */ /* test replacing old values */
for (i = 0; i < nkeys; i++) { for (i = 0; i < nkeys; i++) {
err = hash_add(table, keys[0], (void*)values[i]); err = hash_add(table, keys[0], xmpp_strdup(ctx, values[i]));
if (err) return err; if (err) return err;
if (hash_num_keys(table) != nkeys) return 1; if (hash_num_keys(table) != nkeys) return 1;
result = hash_get(table, keys[0]); result = hash_get(table, keys[0]);
@@ -86,7 +87,7 @@ int main(int argc, char **argv)
if (strcmp(result, values[i]) != 0) return 1; if (strcmp(result, values[i]) != 0) return 1;
} }
/* restore value for the 1st key */ /* restore value for the 1st key */
hash_add(table, keys[0], (void*)values[0]); hash_add(table, keys[0], xmpp_strdup(ctx, values[0]));
/* test cloning */ /* test cloning */
clone = hash_clone(table); clone = hash_clone(table);

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