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:
@@ -85,6 +85,12 @@ if NEED_SNPRINTF
|
||||
libstrophe_la_SOURCES += src/snprintf.c
|
||||
endif
|
||||
|
||||
if DISABLE_COMPRESSION
|
||||
libstrophe_la_SOURCES += src/compression_dummy.c
|
||||
else
|
||||
libstrophe_la_SOURCES += src/compression.c
|
||||
endif
|
||||
|
||||
if DISABLE_TLS
|
||||
libstrophe_la_SOURCES += src/tls_dummy.c
|
||||
else
|
||||
|
||||
@@ -252,7 +252,7 @@ fi
|
||||
if test "x$enable_zlib" != xno; then
|
||||
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
|
||||
[
|
||||
PC_REQUIRES="libzlib ${PC_REQUIRES}"
|
||||
PC_REQUIRES="zlib ${PC_REQUIRES}"
|
||||
ZLIB_CFLAGS=$zlib_CFLAGS
|
||||
ZLIB_LIBS=$zlib_LIBS
|
||||
AC_DEFINE([HAVE_ZLIB])
|
||||
@@ -269,6 +269,7 @@ m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
|
||||
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
|
||||
|
||||
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
|
||||
AM_CONDITIONAL([DISABLE_COMPRESSION], [test x$enable_zlib = xno])
|
||||
AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno])
|
||||
AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno])
|
||||
AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno])
|
||||
|
||||
@@ -77,13 +77,19 @@ int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
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)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *body, *reply;
|
||||
const char *type;
|
||||
char *intext, *replytext;
|
||||
int quit = 0;
|
||||
|
||||
body = xmpp_stanza_get_child_by_name(stanza, "body");
|
||||
if (body == NULL)
|
||||
@@ -103,11 +109,11 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
|
||||
if (strcmp(intext, "quit") == 0) {
|
||||
replytext = strdup("bye!");
|
||||
quit = 1;
|
||||
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||
} else if (strcmp(intext, "reconnect") == 0) {
|
||||
replytext = strdup("alright, let's see what happens!");
|
||||
reconnect = 1;
|
||||
quit = 1;
|
||||
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||
} else {
|
||||
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
|
||||
strcpy(replytext, intext);
|
||||
@@ -120,9 +126,6 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
xmpp_stanza_release(reply);
|
||||
free(replytext);
|
||||
|
||||
if (quit)
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -216,8 +219,8 @@ static void usage(int exit_code)
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
"--legacy-ssl\n"
|
||||
" --zlib Enable compression via zlib.\n"
|
||||
" --dont-flush When using zlib, don't flush after "
|
||||
"compression.\n");
|
||||
" --dont-reset When using zlib, don't do a full-flush "
|
||||
"after compression.\n");
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
@@ -249,8 +252,8 @@ int main(int argc, char **argv)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||
else if (strcmp(argv[i], "--zlib") == 0)
|
||||
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||
else if (strcmp(argv[i], "--dont-flush") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH;
|
||||
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||
jid = argv[i];
|
||||
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||
|
||||
@@ -240,8 +240,8 @@ static void usage(int exit_code)
|
||||
" --legacy-ssl Use old style SSL.\n"
|
||||
" --legacy-auth Allow legacy authentication.\n"
|
||||
" --zlib Enable compression via zlib.\n"
|
||||
" --dont-flush When using zlib, don't flush after "
|
||||
"compression.\n"
|
||||
" --dont-reset When using zlib, don't do a full-flush "
|
||||
"after compression.\n"
|
||||
" --verbose Increase the verbosity level.\n"
|
||||
" --tcp-keepalive Configure TCP keepalive.\n\n"
|
||||
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||
@@ -278,8 +278,8 @@ int main(int argc, char **argv)
|
||||
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||
else if (strcmp(argv[i], "--zlib") == 0)
|
||||
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||
else if (strcmp(argv[i], "--dont-flush") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH;
|
||||
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||
else if (strcmp(argv[i], "--verbose") == 0)
|
||||
verbosity++;
|
||||
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||
|
||||
45
src/auth.c
45
src/auth.c
@@ -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
|
||||
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
{
|
||||
xmpp_stanza_t *child, *children;
|
||||
const char *ns;
|
||||
char *text;
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
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))
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@@ -371,7 +355,7 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||
(char *)userdata);
|
||||
|
||||
/* reset parser */
|
||||
conn_prepare_reset(conn, conn->compression_allowed
|
||||
conn_prepare_reset(conn, conn->compression.allowed
|
||||
? _handle_open_compress
|
||||
: _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);
|
||||
|
||||
UNUSED(userdata);
|
||||
|
||||
if (!name)
|
||||
return 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);
|
||||
|
||||
/* make compression effective */
|
||||
conn->compress = 1;
|
||||
compression_init(conn);
|
||||
|
||||
/* send stream tag */
|
||||
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
|
||||
"'><method>zlib</method></compress>";
|
||||
|
||||
UNUSED(userdata);
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
/* remove missing features handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_features);
|
||||
|
||||
send_raw(conn, compress, strlen(compress), XMPP_QUEUE_STROPHE, NULL);
|
||||
handler_add(conn, _handle_compress_result, XMPP_NS_COMPRESSION, NULL, NULL,
|
||||
NULL);
|
||||
/* check for compression */
|
||||
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "compression",
|
||||
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;
|
||||
}
|
||||
|
||||
32
src/common.h
32
src/common.h
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "strophe.h"
|
||||
#include "ostypes.h"
|
||||
@@ -208,7 +207,28 @@ struct _xmpp_sm_t {
|
||||
xmpp_stanza_t *bind;
|
||||
};
|
||||
|
||||
struct conn_interface {
|
||||
int (*read)(struct conn_interface *intf, void *buff, size_t len);
|
||||
int (*write)(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int (*flush)(struct conn_interface *intf);
|
||||
int (*pending)(struct conn_interface *intf);
|
||||
int (*get_error)(struct conn_interface *intf);
|
||||
int (*error_is_recoverable)(struct conn_interface *intf, int err);
|
||||
xmpp_conn_t *conn;
|
||||
};
|
||||
|
||||
int conn_interface_write(struct conn_interface *intf,
|
||||
const void *buff,
|
||||
size_t len);
|
||||
int conn_int_nop(struct conn_interface *intf);
|
||||
|
||||
int compression_init(xmpp_conn_t *conn);
|
||||
void compression_free(xmpp_conn_t *conn);
|
||||
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
|
||||
|
||||
struct _xmpp_conn_t {
|
||||
struct conn_interface intf;
|
||||
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_type_t type;
|
||||
@@ -256,12 +276,10 @@ struct _xmpp_conn_t {
|
||||
int sm_disable;
|
||||
xmpp_sm_state_t *sm_state;
|
||||
|
||||
int compression_allowed, compression_supported;
|
||||
int compress, compression_dont_flush;
|
||||
struct zlib_compression {
|
||||
void *buffer, *buffer_end;
|
||||
z_stream stream;
|
||||
} compression, decompression;
|
||||
struct {
|
||||
struct xmpp_compression *state;
|
||||
int allowed, supported, dont_reset;
|
||||
} compression;
|
||||
|
||||
char *lang;
|
||||
char *domain;
|
||||
|
||||
270
src/compression.c
Normal file
270
src/compression.c
Normal 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
36
src/compression_dummy.c
Normal 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;
|
||||
}
|
||||
48
src/conn.c
48
src/conn.c
@@ -1062,6 +1062,23 @@ void conn_open_stream(xmpp_conn_t *conn)
|
||||
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 rc;
|
||||
@@ -1075,11 +1092,13 @@ int conn_tls_start(xmpp_conn_t *conn)
|
||||
}
|
||||
|
||||
if (conn->tls != NULL) {
|
||||
conn->intf = tls_intf;
|
||||
conn->intf.conn = conn;
|
||||
if (tls_start(conn->tls)) {
|
||||
conn->secured = 1;
|
||||
} else {
|
||||
rc = XMPP_EINT;
|
||||
conn->error = tls_error(conn->tls);
|
||||
conn->error = tls_error(&conn->intf);
|
||||
tls_free(conn->tls);
|
||||
conn->tls = NULL;
|
||||
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_TRUST_TLS * conn->tls_trust |
|
||||
XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
|
||||
XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression_allowed |
|
||||
XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH * conn->compression_dont_flush |
|
||||
XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
|
||||
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
|
||||
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
|
||||
|
||||
return flags;
|
||||
@@ -1134,6 +1153,8 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
|
||||
* - XMPP_CONN_FLAG_TRUST_TLS
|
||||
* - XMPP_CONN_FLAG_LEGACY_AUTH
|
||||
* - XMPP_CONN_FLAG_DISABLE_SM
|
||||
* - XMPP_CONN_FLAG_ENABLE_COMPRESSION
|
||||
* - XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
|
||||
*
|
||||
* @param conn a Strophe connection object
|
||||
* @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->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 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;
|
||||
conn->compression_dont_flush =
|
||||
(flags & XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH) ? 1 : 0;
|
||||
conn->compression.dont_reset =
|
||||
(flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
|
||||
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_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
|
||||
XMPP_CONN_FLAG_ENABLE_COMPRESSION |
|
||||
XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH);
|
||||
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
|
||||
if (flags) {
|
||||
strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
|
||||
return XMPP_EINVOP;
|
||||
@@ -1768,14 +1789,11 @@ static void _conn_reset(xmpp_conn_t *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn->compression.buffer) {
|
||||
deflateEnd(&conn->compression.stream);
|
||||
strophe_free_and_null(ctx, conn->compression.buffer);
|
||||
}
|
||||
if (conn->decompression.buffer) {
|
||||
inflateEnd(&conn->decompression.stream);
|
||||
strophe_free_and_null(ctx, conn->decompression.buffer);
|
||||
}
|
||||
compression_free(conn);
|
||||
|
||||
conn->intf = sock_intf;
|
||||
conn->intf.conn = conn;
|
||||
|
||||
/* free queued */
|
||||
sq = conn->send_queue_head;
|
||||
while (sq) {
|
||||
|
||||
238
src/event.c
238
src/event.c
@@ -75,202 +75,6 @@ static int _connect_next(xmpp_conn_t *conn)
|
||||
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.
|
||||
* This function will run send any data that has been queued by
|
||||
* 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_conn_t *conn;
|
||||
struct conn_interface *intf;
|
||||
fd_set rfds, wfds;
|
||||
sock_t max = 0;
|
||||
int ret;
|
||||
@@ -310,13 +115,14 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
intf = &conn->intf;
|
||||
|
||||
/* if we're running tls, there may be some remaining data waiting to
|
||||
* be sent, so push that out */
|
||||
if (conn->tls) {
|
||||
ret = tls_clear_pending_write(conn->tls);
|
||||
ret = tls_clear_pending_write(intf);
|
||||
|
||||
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
|
||||
if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
|
||||
/* an error occurred */
|
||||
strophe_debug(
|
||||
ctx, "xmpp",
|
||||
@@ -332,7 +138,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||
while (sq) {
|
||||
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)
|
||||
sq->written += ret; /* not all data could be sent now */
|
||||
sq->wip = 1;
|
||||
@@ -369,11 +175,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||
if (!sq)
|
||||
conn->send_queue_tail = NULL;
|
||||
}
|
||||
if (conn->compress) {
|
||||
_conn_compress(conn, conn->compression.buffer, 0,
|
||||
conn->compression_dont_flush ? Z_SYNC_FLUSH
|
||||
: Z_FULL_FLUSH);
|
||||
}
|
||||
intf->flush(intf);
|
||||
|
||||
/* tear down connection on error */
|
||||
if (conn->error) {
|
||||
@@ -408,6 +210,7 @@ next_item:
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
intf = &conn->intf;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
@@ -442,7 +245,7 @@ next_item:
|
||||
|
||||
/* Check if there is something in the SSL buffer. */
|
||||
if (conn->tls)
|
||||
tls_read_bytes += tls_pending(conn->tls);
|
||||
tls_read_bytes += tls_pending(intf);
|
||||
|
||||
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
|
||||
max = conn->sock;
|
||||
@@ -461,9 +264,9 @@ next_item:
|
||||
|
||||
/* select errored */
|
||||
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",
|
||||
sock_error());
|
||||
sock_error(NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -475,6 +278,7 @@ next_item:
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
intf = &conn->intf;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
@@ -502,9 +306,9 @@ next_item:
|
||||
|
||||
break;
|
||||
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) {
|
||||
ret = parser_feed(conn->parser, buf, ret);
|
||||
@@ -514,15 +318,13 @@ next_item:
|
||||
"parse error");
|
||||
}
|
||||
} else {
|
||||
if (conn->tls) {
|
||||
if (!tls_is_recoverable(tls_error(conn->tls))) {
|
||||
strophe_debug(ctx, "xmpp",
|
||||
"Unrecoverable TLS error, %d.",
|
||||
tls_error(conn->tls));
|
||||
conn->error = tls_error(conn->tls);
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
} else {
|
||||
int err = intf->get_error(intf);
|
||||
if (!intf->error_is_recoverable(intf, err)) {
|
||||
strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
|
||||
err);
|
||||
conn->error = err;
|
||||
conn_disconnect(conn);
|
||||
} else if (!conn->tls) {
|
||||
/* return of 0 means socket closed by server */
|
||||
strophe_debug(ctx, "xmpp",
|
||||
"Socket closed by remote host.");
|
||||
|
||||
38
src/sock.c
38
src/sock.c
@@ -38,6 +38,18 @@
|
||||
#include "common.h"
|
||||
#include "resolver.h"
|
||||
|
||||
const struct conn_interface sock_intf = {
|
||||
sock_read,
|
||||
sock_write,
|
||||
/* no flush */
|
||||
conn_int_nop,
|
||||
/* no pending */
|
||||
conn_int_nop,
|
||||
sock_error,
|
||||
sock_is_recoverable,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct _xmpp_sock_t {
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
@@ -64,8 +76,9 @@ void sock_shutdown(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_error(void)
|
||||
int sock_error(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(intf);
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
@@ -231,7 +244,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
|
||||
if (rc == 0)
|
||||
rc = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
/* Assume only connect() can cause "in progress" error. */
|
||||
if (rc != 0 && !_in_progress(sock_error())) {
|
||||
if (rc != 0 && !_in_progress(sock_error(NULL))) {
|
||||
sock_close(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
@@ -376,18 +389,19 @@ int sock_set_nonblocking(sock_t sock)
|
||||
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
|
||||
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
|
||||
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,
|
||||
* return that */
|
||||
#ifdef _WIN32
|
||||
if (sock_error() != WSAENOTCONN)
|
||||
return sock_error();
|
||||
if (sock_error(NULL) != WSAENOTCONN)
|
||||
return sock_error(NULL);
|
||||
#else
|
||||
if (sock_error() != ENOTCONN)
|
||||
return sock_error();
|
||||
if (sock_error(NULL) != ENOTCONN)
|
||||
return sock_error(NULL);
|
||||
#endif
|
||||
|
||||
/* load the correct error into errno through error slippage */
|
||||
recv(sock, &temp, 1, 0);
|
||||
|
||||
return sock_error();
|
||||
return sock_error(NULL);
|
||||
}
|
||||
|
||||
10
src/sock.h
10
src/sock.h
@@ -30,12 +30,14 @@ typedef int sock_t;
|
||||
typedef SOCKET sock_t;
|
||||
#endif
|
||||
|
||||
extern const struct conn_interface sock_intf;
|
||||
|
||||
typedef struct _xmpp_sock_t xmpp_sock_t;
|
||||
|
||||
void sock_initialize(void);
|
||||
void sock_shutdown(void);
|
||||
|
||||
int sock_error(void);
|
||||
int sock_error(struct conn_interface *intf);
|
||||
|
||||
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
|
||||
const char *domain,
|
||||
@@ -47,9 +49,9 @@ int sock_close(sock_t sock);
|
||||
|
||||
int sock_set_blocking(sock_t sock);
|
||||
int sock_set_nonblocking(sock_t sock);
|
||||
int sock_read(sock_t sock, void *buff, size_t len);
|
||||
int sock_write(sock_t sock, const void *buff, size_t len);
|
||||
int sock_is_recoverable(int error);
|
||||
int sock_read(struct conn_interface *intf, void *buff, size_t len);
|
||||
int sock_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int sock_is_recoverable(struct conn_interface *intf, int error);
|
||||
/* checks for an error after connect, return 0 if connect successful */
|
||||
int sock_connect_error(sock_t sock);
|
||||
int sock_set_keepalive(sock_t sock,
|
||||
|
||||
11
src/tls.c
11
src/tls.c
@@ -30,6 +30,17 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
const struct conn_interface tls_intf = {
|
||||
tls_read,
|
||||
tls_write,
|
||||
tls_clear_pending_write,
|
||||
tls_pending,
|
||||
tls_error,
|
||||
tls_is_recoverable,
|
||||
/* init conn */
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct _dnsname_t {
|
||||
char **data;
|
||||
size_t cur, max;
|
||||
|
||||
14
src/tls.h
14
src/tls.h
@@ -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_stop(tls_t *tls);
|
||||
|
||||
int tls_error(tls_t *tls);
|
||||
int tls_pending(struct conn_interface *intf);
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len);
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||
int tls_clear_pending_write(struct conn_interface *intf);
|
||||
|
||||
int tls_pending(tls_t *tls);
|
||||
int tls_read(tls_t *tls, void *buff, size_t len);
|
||||
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);
|
||||
int tls_error(struct conn_interface *intf);
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error);
|
||||
|
||||
/* provided by tls.c */
|
||||
extern const struct conn_interface tls_intf;
|
||||
|
||||
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
|
||||
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);
|
||||
|
||||
@@ -104,43 +104,43 @@ int tls_stop(tls_t *tls)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
/* todo: some kind of error polling/dump */
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
UNUSED(buff);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_write(tls_t *tls, const void *buff, size_t len)
|
||||
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
UNUSED(buff);
|
||||
UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
UNUSED(error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -633,24 +633,26 @@ int tls_stop(tls_t *tls)
|
||||
return tls->lasterror == GNUTLS_E_SUCCESS;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
return tls->lasterror;
|
||||
return intf->conn->tls->lasterror;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
return !gnutls_error_is_fatal(error);
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
return gnutls_record_check_pending(tls->session);
|
||||
return gnutls_record_check_pending(intf->conn->tls->session);
|
||||
}
|
||||
|
||||
int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||
{
|
||||
int ret;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = gnutls_record_recv(tls->session, buff, len);
|
||||
tls->lasterror = ret < 0 ? ret : 0;
|
||||
@@ -658,9 +660,10 @@ int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
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;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = gnutls_record_send(tls->session, buff, len);
|
||||
tls->lasterror = ret < 0 ? ret : 0;
|
||||
@@ -668,8 +671,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -283,9 +283,9 @@ void tls_shutdown(void)
|
||||
#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
|
||||
@@ -815,7 +815,7 @@ int tls_start(tls_t *tls)
|
||||
ret = SSL_connect(tls->ssl);
|
||||
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 */
|
||||
_tls_sock_wait(tls, error);
|
||||
continue;
|
||||
@@ -856,7 +856,7 @@ int tls_stop(tls_t *tls)
|
||||
++retries;
|
||||
ret = SSL_shutdown(tls->ssl);
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
@@ -875,21 +875,23 @@ int tls_stop(tls_t *tls)
|
||||
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 ||
|
||||
error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_CONNECT ||
|
||||
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;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = SSL_read(tls->ssl, buff, len);
|
||||
_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;
|
||||
}
|
||||
|
||||
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;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = SSL_write(tls->ssl, buff, len);
|
||||
_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;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
UNUSED(tls);
|
||||
UNUSED(intf);
|
||||
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)
|
||||
{
|
||||
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",
|
||||
TLS_ERROR_STR(error, tls_errors), error, errno,
|
||||
tls->lasterror);
|
||||
|
||||
@@ -262,9 +262,11 @@ int tls_start(tls_t *tls)
|
||||
SECURITY_STATUS ret;
|
||||
int sent;
|
||||
char *name;
|
||||
struct conn_interface *intf;
|
||||
|
||||
/* use the domain there as our name */
|
||||
name = tls->conn->domain;
|
||||
intf = tls->conn->intf;
|
||||
|
||||
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
||||
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
|
||||
@@ -313,9 +315,9 @@ int tls_start(tls_t *tls)
|
||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||
|
||||
sent = sock_write(tls->sock, writebuff, writelen);
|
||||
sent = sock_write(intf, writebuff, writelen);
|
||||
if (sent == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
} else {
|
||||
writebuff += sent;
|
||||
writelen -= sent;
|
||||
@@ -353,13 +355,13 @@ int tls_start(tls_t *tls)
|
||||
|
||||
select(tls->sock, &fds, NULL, NULL, &tv);
|
||||
|
||||
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len);
|
||||
inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
|
||||
|
||||
if (inbytes > 0) {
|
||||
len += inbytes;
|
||||
p += inbytes;
|
||||
} else {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,9 +376,9 @@ int tls_start(tls_t *tls)
|
||||
if (sbdout.pBuffers[0].cbBuffer) {
|
||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||
sent = sock_write(tls->sock, writebuff, writelen);
|
||||
sent = sock_write(intf, writebuff, writelen);
|
||||
if (sent == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
} else {
|
||||
writebuff += sent;
|
||||
writelen -= sent;
|
||||
@@ -423,20 +425,22 @@ int tls_stop(tls_t *tls)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_error(tls_t *tls)
|
||||
int tls_error(struct conn_interface *intf)
|
||||
{
|
||||
return tls->lasterror;
|
||||
return intf->conn->tls->lasterror;
|
||||
}
|
||||
|
||||
int tls_is_recoverable(int error)
|
||||
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||
{
|
||||
UNUSED(intf);
|
||||
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
|
||||
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
|
||||
error == WSAEINPROGRESS);
|
||||
}
|
||||
|
||||
int tls_pending(tls_t *tls)
|
||||
int tls_pending(struct conn_interface *intf)
|
||||
{
|
||||
tls_t *tls = intf->conn->tls;
|
||||
// There are 3 cases:
|
||||
// - there is data in ready buffer, so it is by default pending
|
||||
// - there is data in recv buffer. If it is not decrypted yet, means it
|
||||
@@ -452,9 +456,10 @@ int tls_pending(tls_t *tls)
|
||||
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;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
/* first, if we've got some ready data, put that in the buffer */
|
||||
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);
|
||||
|
||||
if (read == -1) {
|
||||
if (tls_is_recoverable(tls->lasterror)) {
|
||||
if (tls_is_recoverable(intf, tls->lasterror)) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -489,7 +494,7 @@ int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
}
|
||||
|
||||
/* next, top up our recv buffer */
|
||||
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos,
|
||||
bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
|
||||
tls->recvbuffermaxlen - tls->recvbufferpos);
|
||||
|
||||
if (bytes == 0) {
|
||||
@@ -498,8 +503,8 @@ int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
}
|
||||
|
||||
if (bytes == -1) {
|
||||
if (!tls_is_recoverable(sock_error())) {
|
||||
tls->lasterror = sock_error();
|
||||
if (!tls_is_recoverable(intf, sock_error(intf))) {
|
||||
tls->lasterror = sock_error(intf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -574,16 +579,17 @@ int tls_read(tls_t *tls, void *buff, size_t len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tls_clear_pending_write(tls_t *tls)
|
||||
int tls_clear_pending_write(struct conn_interface *intf)
|
||||
{
|
||||
tls_t *tls = intf->conn->tls;
|
||||
if (tls->sendbufferpos < tls->sendbufferlen) {
|
||||
int bytes;
|
||||
|
||||
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
|
||||
bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
|
||||
tls->sendbufferlen - tls->sendbufferpos);
|
||||
|
||||
if (bytes == -1) {
|
||||
tls->lasterror = sock_error();
|
||||
tls->lasterror = sock_error(intf);
|
||||
return -1;
|
||||
} else if (bytes > 0) {
|
||||
tls->sendbufferpos += bytes;
|
||||
@@ -597,12 +603,13 @@ int tls_clear_pending_write(tls_t *tls)
|
||||
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;
|
||||
SecBuffer sbenc[4];
|
||||
const unsigned char *p = buff;
|
||||
int sent = 0, ret, remain = len;
|
||||
tls_t *tls = intf->conn->tls;
|
||||
|
||||
ret = tls_clear_pending_write(tls);
|
||||
if (ret <= 0) {
|
||||
@@ -662,7 +669,7 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
|
||||
|
||||
ret = tls_clear_pending_write(tls);
|
||||
|
||||
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) {
|
||||
if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -674,7 +681,8 @@ int tls_write(tls_t *tls, const void *buff, size_t len)
|
||||
remain = 0;
|
||||
}
|
||||
|
||||
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
|
||||
if (ret == 0 ||
|
||||
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
|
||||
return sent;
|
||||
}
|
||||
}
|
||||
|
||||
10
strophe.h
10
strophe.h
@@ -88,6 +88,10 @@ extern "C" {
|
||||
* Namespace definition for Stream Compression.
|
||||
*/
|
||||
#define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress"
|
||||
/** @def XMPP_NS_FEATURE_COMPRESSION
|
||||
* Namespace definition for Stream Compression.
|
||||
*/
|
||||
#define XMPP_NS_FEATURE_COMPRESSION "http://jabber.org/features/compress"
|
||||
|
||||
/* error defines */
|
||||
/** @def XMPP_EOK
|
||||
@@ -199,11 +203,11 @@ typedef struct _xmpp_sm_t xmpp_sm_state_t;
|
||||
* Enable Stream-Compression XEP-0138.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6)
|
||||
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH
|
||||
* Don't flush the compressed stream after each stanza.
|
||||
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
|
||||
* Don't reset the compressed stream after each stanza.
|
||||
* Only enable this flag if you know what you're doing.
|
||||
*/
|
||||
#define XMPP_CONN_FLAG_COMPRESSION_DONT_FLUSH (1UL << 7)
|
||||
#define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
|
||||
|
||||
/* connect callback */
|
||||
typedef enum {
|
||||
|
||||
Reference in New Issue
Block a user