Re-factor into separate compression module

Introduce a `conn_interface` to simplify the decision logic which API
we must call.

This also fixes some bugs of the previous commit.

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
This commit is contained in:
Steffen Jaeckel
2024-01-26 20:43:59 +01:00
parent c7d410f38b
commit 3f03de031c
19 changed files with 555 additions and 359 deletions

View File

@@ -85,6 +85,12 @@ if NEED_SNPRINTF
libstrophe_la_SOURCES += src/snprintf.c libstrophe_la_SOURCES += src/snprintf.c
endif endif
if DISABLE_COMPRESSION
libstrophe_la_SOURCES += src/compression_dummy.c
else
libstrophe_la_SOURCES += src/compression.c
endif
if DISABLE_TLS if DISABLE_TLS
libstrophe_la_SOURCES += src/tls_dummy.c libstrophe_la_SOURCES += src/tls_dummy.c
else else

View File

@@ -252,7 +252,7 @@ fi
if test "x$enable_zlib" != xno; then if test "x$enable_zlib" != xno; then
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0], PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
[ [
PC_REQUIRES="libzlib ${PC_REQUIRES}" PC_REQUIRES="zlib ${PC_REQUIRES}"
ZLIB_CFLAGS=$zlib_CFLAGS ZLIB_CFLAGS=$zlib_CFLAGS
ZLIB_LIBS=$zlib_LIBS ZLIB_LIBS=$zlib_LIBS
AC_DEFINE([HAVE_ZLIB]) AC_DEFINE([HAVE_ZLIB])
@@ -269,6 +269,7 @@ m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])]) AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2]) AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
AM_CONDITIONAL([DISABLE_COMPRESSION], [test x$enable_zlib = xno])
AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno]) AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno])
AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno]) AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno])
AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno]) AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno])

View File

@@ -77,13 +77,19 @@ int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
return 1; return 1;
} }
static int _quit_handler(xmpp_conn_t *conn, void *userdata)
{
(void)userdata;
xmpp_disconnect(conn);
return 0;
}
int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
xmpp_stanza_t *body, *reply; xmpp_stanza_t *body, *reply;
const char *type; const char *type;
char *intext, *replytext; char *intext, *replytext;
int quit = 0;
body = xmpp_stanza_get_child_by_name(stanza, "body"); body = xmpp_stanza_get_child_by_name(stanza, "body");
if (body == NULL) if (body == NULL)
@@ -103,11 +109,11 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
if (strcmp(intext, "quit") == 0) { if (strcmp(intext, "quit") == 0) {
replytext = strdup("bye!"); replytext = strdup("bye!");
quit = 1; xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
} else if (strcmp(intext, "reconnect") == 0) { } else if (strcmp(intext, "reconnect") == 0) {
replytext = strdup("alright, let's see what happens!"); replytext = strdup("alright, let's see what happens!");
reconnect = 1; reconnect = 1;
quit = 1; xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
} else { } else {
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1); replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
strcpy(replytext, intext); strcpy(replytext, intext);
@@ -120,9 +126,6 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_stanza_release(reply); xmpp_stanza_release(reply);
free(replytext); free(replytext);
if (quit)
xmpp_disconnect(conn);
return 1; return 1;
} }
@@ -216,8 +219,8 @@ static void usage(int exit_code)
"Note: --disable-tls conflicts with --mandatory-tls or " "Note: --disable-tls conflicts with --mandatory-tls or "
"--legacy-ssl\n" "--legacy-ssl\n"
" --zlib Enable compression via zlib.\n" " --zlib Enable compression via zlib.\n"
" --dont-flush When using zlib, don't flush after " " --dont-reset When using zlib, don't do a full-flush "
"compression.\n"); "after compression.\n");
exit(exit_code); exit(exit_code);
} }
@@ -249,8 +252,8 @@ int main(int argc, char **argv)
flags |= XMPP_CONN_FLAG_LEGACY_AUTH; flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
else if (strcmp(argv[i], "--zlib") == 0) else if (strcmp(argv[i], "--zlib") == 0)
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION; flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
else if (strcmp(argv[i], "--dont-flush") == 0) else if (strcmp(argv[i], "--dont-reset") == 0)
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH; flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc)) else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
jid = argv[i]; jid = argv[i];
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc)) else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))

View File

@@ -240,8 +240,8 @@ static void usage(int exit_code)
" --legacy-ssl Use old style SSL.\n" " --legacy-ssl Use old style SSL.\n"
" --legacy-auth Allow legacy authentication.\n" " --legacy-auth Allow legacy authentication.\n"
" --zlib Enable compression via zlib.\n" " --zlib Enable compression via zlib.\n"
" --dont-flush When using zlib, don't flush after " " --dont-reset When using zlib, don't do a full-flush "
"compression.\n" "after compression.\n"
" --verbose Increase the verbosity level.\n" " --verbose Increase the verbosity level.\n"
" --tcp-keepalive Configure TCP keepalive.\n\n" " --tcp-keepalive Configure TCP keepalive.\n\n"
"Note: --disable-tls conflicts with --mandatory-tls or " "Note: --disable-tls conflicts with --mandatory-tls or "
@@ -278,8 +278,8 @@ int main(int argc, char **argv)
flags |= XMPP_CONN_FLAG_LEGACY_AUTH; flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
else if (strcmp(argv[i], "--zlib") == 0) else if (strcmp(argv[i], "--zlib") == 0)
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION; flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
else if (strcmp(argv[i], "--dont-flush") == 0) else if (strcmp(argv[i], "--dont-reset") == 0)
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH; flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
else if (strcmp(argv[i], "--verbose") == 0) else if (strcmp(argv[i], "--verbose") == 0)
verbosity++; verbosity++;
else if (strcmp(argv[i], "--tcp-keepalive") == 0) else if (strcmp(argv[i], "--tcp-keepalive") == 0)

View File

@@ -255,19 +255,10 @@ static void _handle_sasl_children(xmpp_conn_t *conn, const char *text)
} }
} }
static void _handle_compression_children(xmpp_conn_t *conn, const char *text)
{
if (strcasecmp(text, "zlib") == 0) {
conn->compression_supported = 1;
}
}
static int static int
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
xmpp_stanza_t *child, *children; xmpp_stanza_t *child;
const char *ns;
char *text;
UNUSED(userdata); UNUSED(userdata);
@@ -297,13 +288,6 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS)) if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS))
conn->sasl_support &= ~SASL_MASK_PLAIN; conn->sasl_support &= ~SASL_MASK_PLAIN;
/* check for compression */
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "compression",
XMPP_NS_COMPRESSION);
if (conn->compression_allowed && child) {
_foreach_child(conn, child, "method", _handle_compression_children);
}
_auth(conn); _auth(conn);
return 0; return 0;
@@ -371,7 +355,7 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
(char *)userdata); (char *)userdata);
/* reset parser */ /* reset parser */
conn_prepare_reset(conn, conn->compression_allowed conn_prepare_reset(conn, conn->compression.allowed
? _handle_open_compress ? _handle_open_compress
: _handle_open_sasl); : _handle_open_sasl);
@@ -1058,6 +1042,8 @@ static int _handle_compress_result(xmpp_conn_t *const conn,
{ {
const char *name = xmpp_stanza_get_name(stanza); const char *name = xmpp_stanza_get_name(stanza);
UNUSED(userdata);
if (!name) if (!name)
return 0; return 0;
if (strcmp(name, "compressed") == 0) { if (strcmp(name, "compressed") == 0) {
@@ -1068,7 +1054,7 @@ static int _handle_compress_result(xmpp_conn_t *const conn,
conn_prepare_reset(conn, _handle_open_sasl); conn_prepare_reset(conn, _handle_open_sasl);
/* make compression effective */ /* make compression effective */
conn->compress = 1; compression_init(conn);
/* send stream tag */ /* send stream tag */
conn_open_stream(conn); conn_open_stream(conn);
@@ -1082,15 +1068,26 @@ static int _handle_features_compress(xmpp_conn_t *conn,
{ {
const char *compress = "<compress xmlns='" XMPP_NS_COMPRESSION const char *compress = "<compress xmlns='" XMPP_NS_COMPRESSION
"'><method>zlib</method></compress>"; "'><method>zlib</method></compress>";
xmpp_stanza_t *child;
UNUSED(userdata);
/* remove missing features handler */ /* remove missing features handler */
xmpp_timed_handler_delete(conn, _handle_missing_features); xmpp_timed_handler_delete(conn, _handle_missing_features);
send_raw(conn, compress, strlen(compress), XMPP_QUEUE_STROPHE, NULL); /* check for compression */
handler_add(conn, _handle_compress_result, XMPP_NS_COMPRESSION, NULL, NULL, child = xmpp_stanza_get_child_by_name_and_ns(stanza, "compression",
NULL); XMPP_NS_FEATURE_COMPRESSION);
if (conn->compression.allowed && child) {
_foreach_child(conn, child, "method",
compression_handle_feature_children);
}
if (conn->compression.supported) {
send_raw(conn, compress, strlen(compress), XMPP_QUEUE_STROPHE, NULL);
handler_add(conn, _handle_compress_result, XMPP_NS_COMPRESSION, NULL,
NULL, NULL);
} else {
return _handle_features_sasl(conn, stanza, userdata);
}
return 0; return 0;
} }

View File

@@ -19,7 +19,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <zlib.h>
#include "strophe.h" #include "strophe.h"
#include "ostypes.h" #include "ostypes.h"
@@ -208,7 +207,28 @@ struct _xmpp_sm_t {
xmpp_stanza_t *bind; xmpp_stanza_t *bind;
}; };
struct conn_interface {
int (*read)(struct conn_interface *intf, void *buff, size_t len);
int (*write)(struct conn_interface *intf, const void *buff, size_t len);
int (*flush)(struct conn_interface *intf);
int (*pending)(struct conn_interface *intf);
int (*get_error)(struct conn_interface *intf);
int (*error_is_recoverable)(struct conn_interface *intf, int err);
xmpp_conn_t *conn;
};
int conn_interface_write(struct conn_interface *intf,
const void *buff,
size_t len);
int conn_int_nop(struct conn_interface *intf);
int compression_init(xmpp_conn_t *conn);
void compression_free(xmpp_conn_t *conn);
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
struct _xmpp_conn_t { struct _xmpp_conn_t {
struct conn_interface intf;
unsigned int ref; unsigned int ref;
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
xmpp_conn_type_t type; xmpp_conn_type_t type;
@@ -256,12 +276,10 @@ struct _xmpp_conn_t {
int sm_disable; int sm_disable;
xmpp_sm_state_t *sm_state; xmpp_sm_state_t *sm_state;
int compression_allowed, compression_supported; struct {
int compress, compression_dont_flush; struct xmpp_compression *state;
struct zlib_compression { int allowed, supported, dont_reset;
void *buffer, *buffer_end; } compression;
z_stream stream;
} compression, decompression;
char *lang; char *lang;
char *domain; char *domain;

270
src/compression.c Normal file
View File

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

36
src/compression_dummy.c Normal file
View File

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

View File

@@ -1062,6 +1062,23 @@ void conn_open_stream(xmpp_conn_t *conn)
strophe_free(conn->ctx, from); strophe_free(conn->ctx, from);
} }
int conn_interface_write(struct conn_interface *intf,
const void *buff,
size_t len)
{
int ret = intf->write(intf, buff, len);
if (ret < 0 && !intf->error_is_recoverable(intf, intf->get_error(intf))) {
intf->conn->error = intf->get_error(intf);
}
return ret;
}
int conn_int_nop(struct conn_interface *intf)
{
UNUSED(intf);
return 0;
}
int conn_tls_start(xmpp_conn_t *conn) int conn_tls_start(xmpp_conn_t *conn)
{ {
int rc; int rc;
@@ -1075,11 +1092,13 @@ int conn_tls_start(xmpp_conn_t *conn)
} }
if (conn->tls != NULL) { if (conn->tls != NULL) {
conn->intf = tls_intf;
conn->intf.conn = conn;
if (tls_start(conn->tls)) { if (tls_start(conn->tls)) {
conn->secured = 1; conn->secured = 1;
} else { } else {
rc = XMPP_EINT; rc = XMPP_EINT;
conn->error = tls_error(conn->tls); conn->error = tls_error(&conn->intf);
tls_free(conn->tls); tls_free(conn->tls);
conn->tls = NULL; conn->tls = NULL;
conn->tls_failed = 1; conn->tls_failed = 1;
@@ -1112,8 +1131,8 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
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_DISABLE_SM * conn->sm_disable | XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression_allowed | XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH * conn->compression_dont_flush | XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled; XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
return flags; return flags;
@@ -1134,6 +1153,8 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
* - XMPP_CONN_FLAG_TRUST_TLS * - XMPP_CONN_FLAG_TRUST_TLS
* - XMPP_CONN_FLAG_LEGACY_AUTH * - XMPP_CONN_FLAG_LEGACY_AUTH
* - XMPP_CONN_FLAG_DISABLE_SM * - XMPP_CONN_FLAG_DISABLE_SM
* - XMPP_CONN_FLAG_ENABLE_COMPRESSION
* - XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
* *
* @param conn a Strophe connection object * @param conn a Strophe connection object
* @param flags ORed connection flags * @param flags ORed connection flags
@@ -1163,15 +1184,15 @@ int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
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; conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0; conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
conn->compression_allowed = conn->compression.allowed =
(flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0; (flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
conn->compression_dont_flush = conn->compression.dont_reset =
(flags & XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH) ? 1 : 0; (flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS | flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS | XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM | XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
XMPP_CONN_FLAG_ENABLE_COMPRESSION | XMPP_CONN_FLAG_ENABLE_COMPRESSION |
XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH); XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
if (flags) { if (flags) {
strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags); strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
return XMPP_EINVOP; return XMPP_EINVOP;
@@ -1768,14 +1789,11 @@ static void _conn_reset(xmpp_conn_t *conn)
return; return;
} }
if (conn->compression.buffer) { compression_free(conn);
deflateEnd(&conn->compression.stream);
strophe_free_and_null(ctx, conn->compression.buffer); conn->intf = sock_intf;
} conn->intf.conn = conn;
if (conn->decompression.buffer) {
inflateEnd(&conn->decompression.stream);
strophe_free_and_null(ctx, conn->decompression.buffer);
}
/* free queued */ /* free queued */
sq = conn->send_queue_head; sq = conn->send_queue_head;
while (sq) { while (sq) {

View File

@@ -75,202 +75,6 @@ static int _connect_next(xmpp_conn_t *conn)
return 0; return 0;
} }
static int
_conn_write_to_network(xmpp_conn_t *conn, const void *buff, size_t len)
{
int ret;
if (conn->tls) {
ret = tls_write(conn->tls, buff, len);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls)))
conn->error = tls_error(conn->tls);
} else {
ret = sock_write(conn->sock, buff, len);
if (ret < 0 && !sock_is_recoverable(sock_error()))
conn->error = sock_error();
}
return ret;
}
static int _try_compressed_write_to_network(xmpp_conn_t *conn, int force)
{
int ret = 0;
size_t len =
conn->compression.stream.next_out - (Bytef *)conn->compression.buffer;
int buffer_full =
conn->compression.stream.next_out == conn->compression.buffer_end;
if ((buffer_full || force) && len) {
ret = _conn_write_to_network(conn, conn->compression.buffer, len);
if (ret < 0)
return ret;
// print_hex(xmpp_base64_encode(conn->ctx,
// conn->compression.buffer, len),
// conn->compression.buffer, len);
char *b = xmpp_base64_encode(conn->ctx, conn->compression.buffer, len);
printf("Sent: %s\n", b);
xmpp_free(conn->ctx, b);
conn->compression.stream.next_out = conn->compression.buffer;
conn->compression.stream.avail_out = STROPHE_MESSAGE_BUFFER_SIZE;
}
return ret;
}
static int _conn_compress(xmpp_conn_t *conn, void *buff, size_t len, int flush)
{
int ret;
void *buff_end = buff + len;
conn->compression.stream.next_in = buff;
conn->compression.stream.avail_in = len;
do {
ret = _try_compressed_write_to_network(conn, 0);
if (ret < 0) {
return ret;
}
ret = deflate(&conn->compression.stream, flush);
if (ret == Z_STREAM_END) {
break;
}
if (flush && ret == Z_BUF_ERROR) {
break;
}
if (ret != Z_OK) {
strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
conn->error = EBADFD;
conn_disconnect(conn);
return ret;
}
ret = conn->compression.stream.next_in - (const Bytef *)buff;
} while (conn->compression.stream.next_in < (const Bytef *)buff_end);
if (flush) {
ret = _try_compressed_write_to_network(conn, 1);
if (ret < 0) {
return ret;
}
}
return ret;
}
static void *_zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
size_t sz = items * size;
if (sz < items || sz < size)
return NULL;
return strophe_alloc(opaque, sz);
}
static void _init_zlib_compression(xmpp_ctx_t *ctx, struct zlib_compression *s)
{
s->buffer = strophe_alloc(ctx, STROPHE_MESSAGE_BUFFER_SIZE);
s->buffer_end = s->buffer + STROPHE_MESSAGE_BUFFER_SIZE;
s->stream.opaque = ctx;
s->stream.zalloc = _zlib_alloc;
s->stream.zfree = (free_func)strophe_free;
}
static int _conn_write(xmpp_conn_t *conn, void *buff, size_t len)
{
if (conn->compress) {
if (conn->compression.buffer == NULL) {
_init_zlib_compression(conn->ctx, &conn->compression);
conn->compression.stream.next_out = conn->compression.buffer;
conn->compression.stream.avail_out = STROPHE_MESSAGE_BUFFER_SIZE;
int err =
deflateInit(&conn->compression.stream, Z_DEFAULT_COMPRESSION);
if (err != Z_OK) {
strophe_free_and_null(conn->ctx, conn->compression.buffer);
conn->error = EBADFD;
conn_disconnect(conn);
return err;
}
}
return _conn_compress(conn, buff, len, Z_NO_FLUSH);
} else {
return _conn_write_to_network(conn, buff, len);
}
}
static int _conn_read_from_network(xmpp_conn_t *conn, void *buff, size_t len)
{
if (conn->tls) {
return tls_read(conn->tls, buff, len);
} else {
return sock_read(conn->sock, buff, len);
}
}
static int
_conn_decompress(xmpp_conn_t *conn, size_t c_len, void *buff, size_t len)
{
if (conn->decompression.stream.next_in == NULL) {
conn->decompression.stream.next_in = conn->decompression.buffer;
conn->decompression.buffer_end =
conn->decompression.stream.next_in + c_len;
conn->decompression.stream.avail_in = c_len;
} else if (c_len) {
strophe_error(conn->ctx, "zlib",
"_conn_decompress() called with c_len=%zu", c_len);
}
conn->decompression.stream.next_out = buff;
conn->decompression.stream.avail_out = len;
int ret = inflate(&conn->decompression.stream, Z_SYNC_FLUSH);
switch (ret) {
case Z_STREAM_END:
case Z_OK:
if (conn->decompression.buffer_end ==
conn->decompression.stream.next_in)
conn->decompression.stream.next_in = NULL;
/* -fallthrough */
return conn->decompression.stream.next_out - (Bytef *)buff;
case Z_BUF_ERROR:
break;
default:
strophe_error(conn->ctx, "zlib", "inflate error %d", ret);
conn->error = EBADFD;
conn_disconnect(conn);
break;
}
return 0;
}
static int _conn_read(xmpp_conn_t *conn, void *buff, size_t len)
{
void *dbuff = buff;
size_t dlen = len;
if (conn->compress) {
if (conn->decompression.buffer == NULL) {
_init_zlib_compression(conn->ctx, &conn->decompression);
int err = inflateInit(&conn->decompression.stream);
if (err != Z_OK) {
strophe_free_and_null(conn->ctx, conn->decompression.buffer);
return err;
}
}
if (conn->decompression.stream.next_in != NULL) {
return _conn_decompress(conn, 0, buff, len);
}
dbuff = conn->decompression.buffer;
dlen = STROPHE_MESSAGE_BUFFER_SIZE;
}
int ret = _conn_read_from_network(conn, dbuff, dlen);
if (ret > 0 && conn->compress) {
char *b = xmpp_base64_encode(conn->ctx, dbuff, ret);
printf("Read: %s\n", b);
xmpp_free(conn->ctx, b);
return _conn_decompress(conn, ret, buff, len);
}
return ret;
}
static int _conn_pending(xmpp_conn_t *conn)
{
return (conn->compress && conn->decompression.stream.next_in != NULL) ||
(conn->tls && tls_pending(conn->tls));
}
/** 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
@@ -288,6 +92,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
{ {
xmpp_connlist_t *connitem; xmpp_connlist_t *connitem;
xmpp_conn_t *conn; xmpp_conn_t *conn;
struct conn_interface *intf;
fd_set rfds, wfds; fd_set rfds, wfds;
sock_t max = 0; sock_t max = 0;
int ret; int ret;
@@ -310,13 +115,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
connitem = connitem->next; connitem = connitem->next;
continue; continue;
} }
intf = &conn->intf;
/* if we're running tls, there may be some remaining data waiting to /* if we're running tls, there may be some remaining data waiting to
* be sent, so push that out */ * be sent, so push that out */
if (conn->tls) { if (conn->tls) {
ret = tls_clear_pending_write(conn->tls); ret = tls_clear_pending_write(intf);
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) { if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
/* an error occurred */ /* an error occurred */
strophe_debug( strophe_debug(
ctx, "xmpp", ctx, "xmpp",
@@ -332,7 +138,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
while (sq) { while (sq) {
towrite = sq->len - sq->written; towrite = sq->len - sq->written;
ret = _conn_write(conn, &sq->data[sq->written], towrite); ret = conn_interface_write(intf, &sq->data[sq->written], towrite);
if (ret > 0 && ret < towrite) if (ret > 0 && ret < towrite)
sq->written += ret; /* not all data could be sent now */ sq->written += ret; /* not all data could be sent now */
sq->wip = 1; sq->wip = 1;
@@ -369,11 +175,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
if (!sq) if (!sq)
conn->send_queue_tail = NULL; conn->send_queue_tail = NULL;
} }
if (conn->compress) { intf->flush(intf);
_conn_compress(conn, conn->compression.buffer, 0,
conn->compression_dont_flush ? Z_SYNC_FLUSH
: Z_FULL_FLUSH);
}
/* tear down connection on error */ /* tear down connection on error */
if (conn->error) { if (conn->error) {
@@ -408,6 +210,7 @@ next_item:
connitem = ctx->connlist; connitem = ctx->connlist;
while (connitem) { while (connitem) {
conn = connitem->conn; conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) { switch (conn->state) {
case XMPP_STATE_CONNECTING: case XMPP_STATE_CONNECTING:
@@ -442,7 +245,7 @@ next_item:
/* Check if there is something in the SSL buffer. */ /* Check if there is something in the SSL buffer. */
if (conn->tls) if (conn->tls)
tls_read_bytes += tls_pending(conn->tls); tls_read_bytes += tls_pending(intf);
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max) if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
max = conn->sock; max = conn->sock;
@@ -461,9 +264,9 @@ next_item:
/* select errored */ /* select errored */
if (ret < 0) { if (ret < 0) {
if (!sock_is_recoverable(sock_error())) if (!sock_is_recoverable(NULL, sock_error(NULL)))
strophe_error(ctx, "xmpp", "event watcher internal error %d", strophe_error(ctx, "xmpp", "event watcher internal error %d",
sock_error()); sock_error(NULL));
return; return;
} }
@@ -475,6 +278,7 @@ next_item:
connitem = ctx->connlist; connitem = ctx->connlist;
while (connitem) { while (connitem) {
conn = connitem->conn; conn = connitem->conn;
intf = &conn->intf;
switch (conn->state) { switch (conn->state) {
case XMPP_STATE_CONNECTING: case XMPP_STATE_CONNECTING:
@@ -502,9 +306,9 @@ next_item:
break; break;
case XMPP_STATE_CONNECTED: case XMPP_STATE_CONNECTED:
if (FD_ISSET(conn->sock, &rfds) || _conn_pending(conn)) { if (FD_ISSET(conn->sock, &rfds) || intf->pending(intf)) {
ret = _conn_read(conn, buf, STROPHE_MESSAGE_BUFFER_SIZE); ret = intf->read(intf, buf, STROPHE_MESSAGE_BUFFER_SIZE);
if (ret > 0) { if (ret > 0) {
ret = parser_feed(conn->parser, buf, ret); ret = parser_feed(conn->parser, buf, ret);
@@ -514,15 +318,13 @@ next_item:
"parse error"); "parse error");
} }
} else { } else {
if (conn->tls) { int err = intf->get_error(intf);
if (!tls_is_recoverable(tls_error(conn->tls))) { if (!intf->error_is_recoverable(intf, err)) {
strophe_debug(ctx, "xmpp", strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
"Unrecoverable TLS error, %d.", err);
tls_error(conn->tls)); conn->error = err;
conn->error = tls_error(conn->tls); conn_disconnect(conn);
conn_disconnect(conn); } else if (!conn->tls) {
}
} else {
/* return of 0 means socket closed by server */ /* return of 0 means socket closed by server */
strophe_debug(ctx, "xmpp", strophe_debug(ctx, "xmpp",
"Socket closed by remote host."); "Socket closed by remote host.");

View File

@@ -38,6 +38,18 @@
#include "common.h" #include "common.h"
#include "resolver.h" #include "resolver.h"
const struct conn_interface sock_intf = {
sock_read,
sock_write,
/* no flush */
conn_int_nop,
/* no pending */
conn_int_nop,
sock_error,
sock_is_recoverable,
NULL,
};
struct _xmpp_sock_t { struct _xmpp_sock_t {
xmpp_ctx_t *ctx; xmpp_ctx_t *ctx;
xmpp_conn_t *conn; xmpp_conn_t *conn;
@@ -64,8 +76,9 @@ void sock_shutdown(void)
#endif #endif
} }
int sock_error(void) int sock_error(struct conn_interface *intf)
{ {
UNUSED(intf);
#ifdef _WIN32 #ifdef _WIN32
return WSAGetLastError(); return WSAGetLastError();
#else #else
@@ -231,7 +244,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
if (rc == 0) if (rc == 0)
rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen); rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
/* Assume only connect() can cause "in progress" error. */ /* Assume only connect() can cause "in progress" error. */
if (rc != 0 && !_in_progress(sock_error())) { if (rc != 0 && !_in_progress(sock_error(NULL))) {
sock_close(sock); sock_close(sock);
sock = INVALID_SOCKET; sock = INVALID_SOCKET;
} }
@@ -376,18 +389,19 @@ int sock_set_nonblocking(sock_t sock)
return _sock_set_blocking_mode(sock, 0); return _sock_set_blocking_mode(sock, 0);
} }
int sock_read(sock_t sock, void *buff, size_t len) int sock_read(struct conn_interface *intf, void *buff, size_t len)
{ {
return recv(sock, buff, len, 0); return recv(intf->conn->sock, buff, len, 0);
} }
int sock_write(sock_t sock, const void *buff, size_t len) int sock_write(struct conn_interface *intf, const void *buff, size_t len)
{ {
return send(sock, buff, len, 0); return send(intf->conn->sock, buff, len, 0);
} }
int sock_is_recoverable(int error) int sock_is_recoverable(struct conn_interface *intf, int error)
{ {
UNUSED(intf);
#ifdef _WIN32 #ifdef _WIN32
return (error == WSAEINTR || error == WSAEWOULDBLOCK || return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
error == WSAEINPROGRESS); error == WSAEINPROGRESS);
@@ -416,15 +430,15 @@ int sock_connect_error(sock_t sock)
/* it's possible that the error wasn't ENOTCONN, so if it wasn't, /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
* return that */ * return that */
#ifdef _WIN32 #ifdef _WIN32
if (sock_error() != WSAENOTCONN) if (sock_error(NULL) != WSAENOTCONN)
return sock_error(); return sock_error(NULL);
#else #else
if (sock_error() != ENOTCONN) if (sock_error(NULL) != ENOTCONN)
return sock_error(); return sock_error(NULL);
#endif #endif
/* load the correct error into errno through error slippage */ /* load the correct error into errno through error slippage */
recv(sock, &temp, 1, 0); recv(sock, &temp, 1, 0);
return sock_error(); return sock_error(NULL);
} }

View File

@@ -30,12 +30,14 @@ typedef int sock_t;
typedef SOCKET sock_t; typedef SOCKET sock_t;
#endif #endif
extern const struct conn_interface sock_intf;
typedef struct _xmpp_sock_t xmpp_sock_t; 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(struct conn_interface *intf);
xmpp_sock_t *sock_new(xmpp_conn_t *conn, xmpp_sock_t *sock_new(xmpp_conn_t *conn,
const char *domain, const char *domain,
@@ -47,9 +49,9 @@ int sock_close(sock_t sock);
int sock_set_blocking(sock_t sock); int sock_set_blocking(sock_t sock);
int sock_set_nonblocking(sock_t sock); int sock_set_nonblocking(sock_t sock);
int sock_read(sock_t sock, void *buff, size_t len); int sock_read(struct conn_interface *intf, void *buff, size_t len);
int sock_write(sock_t sock, const void *buff, size_t len); int sock_write(struct conn_interface *intf, const void *buff, size_t len);
int sock_is_recoverable(int error); int sock_is_recoverable(struct conn_interface *intf, int error);
/* checks for an error after connect, return 0 if connect successful */ /* checks for an error after connect, return 0 if connect successful */
int sock_connect_error(sock_t sock); int sock_connect_error(sock_t sock);
int sock_set_keepalive(sock_t sock, int sock_set_keepalive(sock_t sock,

View File

@@ -30,6 +30,17 @@
#include "common.h" #include "common.h"
const struct conn_interface tls_intf = {
tls_read,
tls_write,
tls_clear_pending_write,
tls_pending,
tls_error,
tls_is_recoverable,
/* init conn */
NULL,
};
struct _dnsname_t { struct _dnsname_t {
char **data; char **data;
size_t cur, max; size_t cur, max;

View File

@@ -53,16 +53,16 @@ const void *tls_get_channel_binding_data(tls_t *tls, size_t *size);
int tls_start(tls_t *tls); int tls_start(tls_t *tls);
int tls_stop(tls_t *tls); int tls_stop(tls_t *tls);
int tls_error(tls_t *tls); int tls_pending(struct conn_interface *intf);
int tls_read(struct conn_interface *intf, void *buff, size_t len);
int tls_write(struct conn_interface *intf, const void *buff, size_t len);
int tls_clear_pending_write(struct conn_interface *intf);
int tls_pending(tls_t *tls); int tls_error(struct conn_interface *intf);
int tls_read(tls_t *tls, void *buff, size_t len); int tls_is_recoverable(struct conn_interface *intf, int error);
int tls_write(tls_t *tls, const void *buff, size_t len);
int tls_clear_pending_write(tls_t *tls);
int tls_is_recoverable(int error);
/* provided by tls.c */ /* provided by tls.c */
extern const struct conn_interface tls_intf;
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx); xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname); int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);

View File

@@ -104,43 +104,43 @@ int tls_stop(tls_t *tls)
return -1; return -1;
} }
int tls_error(tls_t *tls) int tls_error(struct conn_interface *intf)
{ {
UNUSED(tls); UNUSED(intf);
/* todo: some kind of error polling/dump */
return 0; return 0;
} }
int tls_pending(tls_t *tls) int tls_pending(struct conn_interface *intf)
{ {
UNUSED(tls); UNUSED(intf);
return 0; return 0;
} }
int tls_read(tls_t *tls, void *buff, size_t len) int tls_read(struct conn_interface *intf, void *buff, size_t len)
{ {
UNUSED(tls); UNUSED(intf);
UNUSED(buff); UNUSED(buff);
UNUSED(len); UNUSED(len);
return -1; return -1;
} }
int tls_write(tls_t *tls, const void *buff, size_t len) int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{ {
UNUSED(tls); UNUSED(intf);
UNUSED(buff); UNUSED(buff);
UNUSED(len); UNUSED(len);
return -1; return -1;
} }
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(struct conn_interface *intf)
{ {
UNUSED(tls); UNUSED(intf);
return -1; return -1;
} }
int tls_is_recoverable(int error) int tls_is_recoverable(struct conn_interface *intf, int error)
{ {
UNUSED(intf);
UNUSED(error); UNUSED(error);
return 0; return 0;
} }

View File

@@ -633,24 +633,26 @@ int tls_stop(tls_t *tls)
return tls->lasterror == GNUTLS_E_SUCCESS; return tls->lasterror == GNUTLS_E_SUCCESS;
} }
int tls_error(tls_t *tls) int tls_error(struct conn_interface *intf)
{ {
return tls->lasterror; return intf->conn->tls->lasterror;
} }
int tls_is_recoverable(int error) int tls_is_recoverable(struct conn_interface *intf, int error)
{ {
UNUSED(intf);
return !gnutls_error_is_fatal(error); return !gnutls_error_is_fatal(error);
} }
int tls_pending(tls_t *tls) int tls_pending(struct conn_interface *intf)
{ {
return gnutls_record_check_pending(tls->session); return gnutls_record_check_pending(intf->conn->tls->session);
} }
int tls_read(tls_t *tls, void *buff, size_t len) int tls_read(struct conn_interface *intf, void *buff, size_t len)
{ {
int ret; int ret;
tls_t *tls = intf->conn->tls;
ret = gnutls_record_recv(tls->session, buff, len); ret = gnutls_record_recv(tls->session, buff, len);
tls->lasterror = ret < 0 ? ret : 0; tls->lasterror = ret < 0 ? ret : 0;
@@ -658,9 +660,10 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return ret; return ret;
} }
int tls_write(tls_t *tls, const void *buff, size_t len) int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{ {
int ret; int ret;
tls_t *tls = intf->conn->tls;
ret = gnutls_record_send(tls->session, buff, len); ret = gnutls_record_send(tls->session, buff, len);
tls->lasterror = ret < 0 ? ret : 0; tls->lasterror = ret < 0 ? ret : 0;
@@ -668,8 +671,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
return ret; return ret;
} }
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(struct conn_interface *intf)
{ {
UNUSED(tls); UNUSED(intf);
return 0; return 0;
} }

View File

@@ -283,9 +283,9 @@ void tls_shutdown(void)
#endif #endif
} }
int tls_error(tls_t *tls) int tls_error(struct conn_interface *intf)
{ {
return tls->lasterror; return intf->conn->tls->lasterror;
} }
/** Search through the SubjectAlternativeNames and return the next /** Search through the SubjectAlternativeNames and return the next
@@ -815,7 +815,7 @@ int tls_start(tls_t *tls)
ret = SSL_connect(tls->ssl); ret = SSL_connect(tls->ssl);
error = ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0; error = ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0;
if (ret == -1 && tls_is_recoverable(error)) { if (ret == -1 && tls_is_recoverable(NULL, error)) {
/* wait for something to happen on the sock before looping back */ /* wait for something to happen on the sock before looping back */
_tls_sock_wait(tls, error); _tls_sock_wait(tls, error);
continue; continue;
@@ -856,7 +856,7 @@ int tls_stop(tls_t *tls)
++retries; ++retries;
ret = SSL_shutdown(tls->ssl); ret = SSL_shutdown(tls->ssl);
error = ret < 0 ? SSL_get_error(tls->ssl, ret) : 0; error = ret < 0 ? SSL_get_error(tls->ssl, ret) : 0;
if (ret == 1 || !tls_is_recoverable(error) || if (ret == 1 || !tls_is_recoverable(NULL, error) ||
retries >= TLS_SHUTDOWN_MAX_RETRIES) { retries >= TLS_SHUTDOWN_MAX_RETRIES) {
break; break;
} }
@@ -875,21 +875,23 @@ int tls_stop(tls_t *tls)
return ret <= 0 ? 0 : 1; return ret <= 0 ? 0 : 1;
} }
int tls_is_recoverable(int error) int tls_is_recoverable(struct conn_interface *intf, int error)
{ {
UNUSED(intf);
return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ || return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ ||
error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_CONNECT || error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_CONNECT ||
error == SSL_ERROR_WANT_ACCEPT); error == SSL_ERROR_WANT_ACCEPT);
} }
int tls_pending(tls_t *tls) int tls_pending(struct conn_interface *intf)
{ {
return SSL_pending(tls->ssl); return SSL_pending(intf->conn->tls->ssl);
} }
int tls_read(tls_t *tls, void *buff, size_t len) int tls_read(struct conn_interface *intf, void *buff, size_t len)
{ {
int ret; int ret;
tls_t *tls = intf->conn->tls;
ret = SSL_read(tls->ssl, buff, len); ret = SSL_read(tls->ssl, buff, len);
_tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0); _tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
@@ -897,9 +899,10 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return ret; return ret;
} }
int tls_write(tls_t *tls, const void *buff, size_t len) int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{ {
int ret; int ret;
tls_t *tls = intf->conn->tls;
ret = SSL_write(tls->ssl, buff, len); ret = SSL_write(tls->ssl, buff, len);
_tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0); _tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
@@ -907,9 +910,9 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
return ret; return ret;
} }
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(struct conn_interface *intf)
{ {
UNUSED(tls); UNUSED(intf);
return 0; return 0;
} }
@@ -947,7 +950,7 @@ static const char *_tls_error_str(int error, const char **tbl, size_t tbl_size)
static void _tls_set_error(tls_t *tls, int error) static void _tls_set_error(tls_t *tls, int error)
{ {
if (error != 0 && !tls_is_recoverable(error)) { if (error != 0 && !tls_is_recoverable(NULL, error)) {
strophe_debug(tls->ctx, "tls", "error=%s(%d) errno=%d lasterror=%d", strophe_debug(tls->ctx, "tls", "error=%s(%d) errno=%d lasterror=%d",
TLS_ERROR_STR(error, tls_errors), error, errno, TLS_ERROR_STR(error, tls_errors), error, errno,
tls->lasterror); tls->lasterror);

View File

@@ -262,9 +262,11 @@ int tls_start(tls_t *tls)
SECURITY_STATUS ret; SECURITY_STATUS ret;
int sent; int sent;
char *name; char *name;
struct conn_interface *intf;
/* use the domain there as our name */ /* use the domain there as our name */
name = tls->conn->domain; name = tls->conn->domain;
intf = tls->conn->intf;
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
@@ -313,9 +315,9 @@ int tls_start(tls_t *tls)
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer; unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer; unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
sent = sock_write(tls->sock, writebuff, writelen); sent = sock_write(intf, writebuff, writelen);
if (sent == -1) { if (sent == -1) {
tls->lasterror = sock_error(); tls->lasterror = sock_error(intf);
} else { } else {
writebuff += sent; writebuff += sent;
writelen -= sent; writelen -= sent;
@@ -353,13 +355,13 @@ int tls_start(tls_t *tls)
select(tls->sock, &fds, NULL, NULL, &tv); select(tls->sock, &fds, NULL, NULL, &tv);
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len); inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
if (inbytes > 0) { if (inbytes > 0) {
len += inbytes; len += inbytes;
p += inbytes; p += inbytes;
} else { } else {
tls->lasterror = sock_error(); tls->lasterror = sock_error(intf);
} }
} }
@@ -374,9 +376,9 @@ int tls_start(tls_t *tls)
if (sbdout.pBuffers[0].cbBuffer) { if (sbdout.pBuffers[0].cbBuffer) {
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer; unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
unsigned int writelen = sbdout.pBuffers[0].cbBuffer; unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
sent = sock_write(tls->sock, writebuff, writelen); sent = sock_write(intf, writebuff, writelen);
if (sent == -1) { if (sent == -1) {
tls->lasterror = sock_error(); tls->lasterror = sock_error(intf);
} else { } else {
writebuff += sent; writebuff += sent;
writelen -= sent; writelen -= sent;
@@ -423,20 +425,22 @@ int tls_stop(tls_t *tls)
return -1; return -1;
} }
int tls_error(tls_t *tls) int tls_error(struct conn_interface *intf)
{ {
return tls->lasterror; return intf->conn->tls->lasterror;
} }
int tls_is_recoverable(int error) int tls_is_recoverable(struct conn_interface *intf, int error)
{ {
UNUSED(intf);
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE || return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE || error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
error == WSAEINPROGRESS); error == WSAEINPROGRESS);
} }
int tls_pending(tls_t *tls) int tls_pending(struct conn_interface *intf)
{ {
tls_t *tls = intf->conn->tls;
// There are 3 cases: // There are 3 cases:
// - there is data in ready buffer, so it is by default pending // - there is data in ready buffer, so it is by default pending
// - there is data in recv buffer. If it is not decrypted yet, means it // - there is data in recv buffer. If it is not decrypted yet, means it
@@ -452,9 +456,10 @@ int tls_pending(tls_t *tls)
return 0; return 0;
} }
int tls_read(tls_t *tls, void *buff, size_t len) int tls_read(struct conn_interface *intf, void *buff, size_t len)
{ {
int bytes; int bytes;
tls_t *tls = intf->conn->tls;
/* first, if we've got some ready data, put that in the buffer */ /* first, if we've got some ready data, put that in the buffer */
if (tls->readybufferpos < tls->readybufferlen) { if (tls->readybufferpos < tls->readybufferlen) {
@@ -477,7 +482,7 @@ int tls_read(tls_t *tls, void *buff, size_t len)
read = tls_read(tls, newbuff, len - bytes); read = tls_read(tls, newbuff, len - bytes);
if (read == -1) { if (read == -1) {
if (tls_is_recoverable(tls->lasterror)) { if (tls_is_recoverable(intf, tls->lasterror)) {
return bytes; return bytes;
} }
@@ -489,7 +494,7 @@ int tls_read(tls_t *tls, void *buff, size_t len)
} }
/* next, top up our recv buffer */ /* next, top up our recv buffer */
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos, bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
tls->recvbuffermaxlen - tls->recvbufferpos); tls->recvbuffermaxlen - tls->recvbufferpos);
if (bytes == 0) { if (bytes == 0) {
@@ -498,8 +503,8 @@ int tls_read(tls_t *tls, void *buff, size_t len)
} }
if (bytes == -1) { if (bytes == -1) {
if (!tls_is_recoverable(sock_error())) { if (!tls_is_recoverable(intf, sock_error(intf))) {
tls->lasterror = sock_error(); tls->lasterror = sock_error(intf);
return -1; return -1;
} }
} }
@@ -574,16 +579,17 @@ int tls_read(tls_t *tls, void *buff, size_t len)
return -1; return -1;
} }
int tls_clear_pending_write(tls_t *tls) int tls_clear_pending_write(struct conn_interface *intf)
{ {
tls_t *tls = intf->conn->tls;
if (tls->sendbufferpos < tls->sendbufferlen) { if (tls->sendbufferpos < tls->sendbufferlen) {
int bytes; int bytes;
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos, bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
tls->sendbufferlen - tls->sendbufferpos); tls->sendbufferlen - tls->sendbufferpos);
if (bytes == -1) { if (bytes == -1) {
tls->lasterror = sock_error(); tls->lasterror = sock_error(intf);
return -1; return -1;
} else if (bytes > 0) { } else if (bytes > 0) {
tls->sendbufferpos += bytes; tls->sendbufferpos += bytes;
@@ -597,12 +603,13 @@ int tls_clear_pending_write(tls_t *tls)
return 1; return 1;
} }
int tls_write(tls_t *tls, const void *buff, size_t len) int tls_write(struct conn_interface *intf, const void *buff, size_t len)
{ {
SecBufferDesc sbdenc; SecBufferDesc sbdenc;
SecBuffer sbenc[4]; SecBuffer sbenc[4];
const unsigned char *p = buff; const unsigned char *p = buff;
int sent = 0, ret, remain = len; int sent = 0, ret, remain = len;
tls_t *tls = intf->conn->tls;
ret = tls_clear_pending_write(tls); ret = tls_clear_pending_write(tls);
if (ret <= 0) { if (ret <= 0) {
@@ -662,7 +669,7 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
ret = tls_clear_pending_write(tls); ret = tls_clear_pending_write(tls);
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) { if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
return -1; return -1;
} }
@@ -674,7 +681,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
remain = 0; remain = 0;
} }
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) { if (ret == 0 ||
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
return sent; return sent;
} }
} }

View File

@@ -88,6 +88,10 @@ extern "C" {
* Namespace definition for Stream Compression. * Namespace definition for Stream Compression.
*/ */
#define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress" #define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress"
/** @def XMPP_NS_FEATURE_COMPRESSION
* Namespace definition for Stream Compression.
*/
#define XMPP_NS_FEATURE_COMPRESSION "http://jabber.org/features/compress"
/* error defines */ /* error defines */
/** @def XMPP_EOK /** @def XMPP_EOK
@@ -199,11 +203,11 @@ typedef struct _xmpp_sm_t xmpp_sm_state_t;
* Enable Stream-Compression XEP-0138. * Enable Stream-Compression XEP-0138.
*/ */
#define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6) #define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6)
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH /** @def XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
* Don't flush the compressed stream after each stanza. * Don't reset the compressed stream after each stanza.
* Only enable this flag if you know what you're doing. * Only enable this flag if you know what you're doing.
*/ */
#define XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH (1UL << 7) #define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
/* connect callback */ /* connect callback */
typedef enum { typedef enum {