jabberd2  2.3.1
compress.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2007 Tomasz Sterna
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
17  */
18 
23 #include "sx.h"
24 
25 static void _sx_compress_notify_compress(sx_t s, void *arg) {
26 
27  _sx_debug(ZONE, "preparing for compress");
28 
29  _sx_reset(s);
30 
31  /* start listening */
33 }
34 
35 static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad) {
36  int flags;
37  char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
38  sx_error_t sxe;
39 
40  /* not interested if we're a server and we never offered it */
41  if(s->type == type_SERVER && !(s->flags & SX_COMPRESS_OFFER))
42  return 1;
43 
44  /* only want compress packets */
45  if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != sizeof(uri_COMPRESS)-1 || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_COMPRESS, sizeof(uri_COMPRESS)-1) != 0)
46  return 1;
47 
48  /* compress from client */
49  if(s->type == type_SERVER) {
50  if(NAD_ENAME_L(nad, 0) == 8 && strncmp(NAD_ENAME(nad, 0), "compress", 8) == 0) {
51  nad_free(nad);
52 
53  /* can't go on if we've been here before */
54  if(s->compressed) {
55  _sx_debug(ZONE, "compress requested on already compressed channel, dropping packet");
56  return 0;
57  }
58 
59  _sx_debug(ZONE, "compress requested, setting up");
60 
61  /* go ahead */
62  jqueue_push(s->wbufq, _sx_buffer_new("<compressed xmlns='" uri_COMPRESS "'/>", sizeof(uri_COMPRESS)-1 + 22, _sx_compress_notify_compress, NULL), 0);
63  s->want_write = 1;
64 
65  /* handled the packet */
66  return 0;
67  }
68  }
69 
70  else if(s->type == type_CLIENT) {
71  /* kick off the handshake */
72  if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "compressed", 7) == 0) {
73  nad_free(nad);
74 
75  /* save interesting bits */
76  flags = s->flags;
77 
78  if(s->ns != NULL) ns = strdup(s->ns);
79 
80  if(s->req_to != NULL) to = strdup(s->req_to);
81  if(s->req_from != NULL) from = strdup(s->req_from);
82  if(s->req_version != NULL) version = strdup(s->req_version);
83 
84  /* reset state */
85  _sx_reset(s);
86 
87  _sx_debug(ZONE, "server ready for compression, starting");
88 
89  /* second time round */
90  sx_client_init(s, flags | SX_COMPRESS_WRAPPER, ns, to, from, version);
91 
92  /* free bits */
93  if(ns != NULL) free(ns);
94  if(to != NULL) free(to);
95  if(from != NULL) free(from);
96  if(version != NULL) free(version);
97 
98  return 0;
99  }
100 
101  /* busted server */
102  if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) {
103  nad_free(nad);
104 
105  _sx_debug(ZONE, "server can't handle compression, business as usual");
106 
107  _sx_gen_error(sxe, SX_ERR_COMPRESS_FAILURE, "compress failure", "Server was unable to establish compression");
108  _sx_event(s, event_ERROR, (void *) &sxe);
109 
110  return 0;
111  }
112  }
113 
114  _sx_debug(ZONE, "unknown compress namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
115  nad_free(nad);
116  return 0;
117 }
118 
119 static void _sx_compress_features(sx_t s, sx_plugin_t p, nad_t nad) {
120  int ns;
121 
122  /* if the session is already compressed, or the app told us not to,
123  * or STARTTLS is required and stream is not encrypted yet, then we don't offer anything */
124  if(s->compressed || !(s->flags & SX_COMPRESS_OFFER) || ((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0))
125  return;
126 
127  _sx_debug(ZONE, "offering compression");
128 
129  ns = nad_add_namespace(nad, uri_COMPRESS_FEATURE, NULL);
130  nad_append_elem(nad, ns, "compression", 1);
131  nad_append_elem(nad, ns, "method", 2);
132  nad_append_cdata(nad, "zlib", 4, 3);
133 }
134 
135 static int _sx_compress_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
136  _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
137  int ret;
138  sx_error_t sxe;
139 
140  /* only bothering if they asked for wrappermode */
141  if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
142  return 1;
143 
144  _sx_debug(ZONE, "in _sx_compress_wio");
145 
146  /* move the data into the zlib write buffer */
147  if(buf->len > 0) {
148  _sx_debug(ZONE, "loading %d bytes into zlib write buffer", buf->len);
149 
150  _sx_buffer_alloc_margin(sc->wbuf, 0, buf->len);
151  memcpy(sc->wbuf->data + sc->wbuf->len, buf->data, buf->len);
152  sc->wbuf->len += buf->len;
153 
154  _sx_buffer_clear(buf);
155  }
156 
157  /* compress the data */
158  if(sc->wbuf->len > 0) {
159  sc->wstrm.avail_in = sc->wbuf->len;
160  sc->wstrm.next_in = (Bytef*)sc->wbuf->data;
161  /* deflate() on write buffer until there is data to compress */
162  do {
163  /* make place for deflated data */
164  _sx_buffer_alloc_margin(buf, 0, sc->wbuf->len + SX_COMPRESS_CHUNK);
165 
166  sc->wstrm.avail_out = sc->wbuf->len + SX_COMPRESS_CHUNK;
167  sc->wstrm.next_out = (Bytef*)(buf->data + buf->len);
168 
169  ret = deflate(&(sc->wstrm), Z_SYNC_FLUSH);
170  assert(ret != Z_STREAM_ERROR);
171 
172  buf->len += sc->wbuf->len + SX_COMPRESS_CHUNK - sc->wstrm.avail_out;
173 
174  } while (sc->wstrm.avail_out == 0);
175 
176  if(ret != Z_OK || sc->wstrm.avail_in != 0) {
177  /* throw an error */
178  _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during compression");
179  _sx_event(s, event_ERROR, (void *) &sxe);
180 
181  sx_error(s, stream_err_INTERNAL_SERVER_ERROR, "Error during compression");
182  sx_close(s);
183 
184  return -2; /* fatal */
185  }
186 
187  sc->wbuf->len = sc->wstrm.avail_in;
188  sc->wbuf->data = (char*)sc->wstrm.next_in;
189  }
190 
191  _sx_debug(ZONE, "passing %d bytes from zlib write buffer", buf->len);
192 
193  return 1;
194 }
195 
196 static int _sx_compress_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
197  _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
198  int ret;
199  sx_error_t sxe;
200 
201  /* only bothering if they asked for wrappermode */
202  if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
203  return 1;
204 
205  _sx_debug(ZONE, "in _sx_compress_rio");
206 
207  /* move the data into the zlib read buffer */
208  if(buf->len > 0) {
209  _sx_debug(ZONE, "loading %d bytes into zlib read buffer", buf->len);
210 
211  _sx_buffer_alloc_margin(sc->rbuf, 0, buf->len);
212  memcpy(sc->rbuf->data + sc->rbuf->len, buf->data, buf->len);
213  sc->rbuf->len += buf->len;
214 
215  _sx_buffer_clear(buf);
216  }
217 
218  /* decompress the data */
219  if(sc->rbuf->len > 0) {
220  sc->rstrm.avail_in = sc->rbuf->len;
221  sc->rstrm.next_in = (Bytef*)sc->rbuf->data;
222  /* run inflate() on read buffer while able to fill the output buffer */
223  do {
224  /* make place for inflated data */
225  _sx_buffer_alloc_margin(buf, 0, SX_COMPRESS_CHUNK);
226 
227  sc->rstrm.avail_out = SX_COMPRESS_CHUNK;
228  sc->rstrm.next_out = (Bytef*)(buf->data + buf->len);
229 
230  ret = inflate(&(sc->rstrm), Z_SYNC_FLUSH);
231  assert(ret != Z_STREAM_ERROR);
232  switch (ret) {
233  case Z_NEED_DICT:
234  case Z_DATA_ERROR:
235  case Z_MEM_ERROR:
236  /* throw an error */
237  _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during decompression");
238  _sx_event(s, event_ERROR, (void *) &sxe);
239 
240  sx_error(s, stream_err_INVALID_XML, "Error during decompression");
241  sx_close(s);
242 
243  return -2;
244  }
245 
246  buf->len += SX_COMPRESS_CHUNK - sc->rstrm.avail_out;
247 
248  } while (sc->rstrm.avail_out == 0);
249 
250  sc->rbuf->len = sc->rstrm.avail_in;
251  sc->rbuf->data = (char*)sc->rstrm.next_in;
252  }
253 
254  _sx_debug(ZONE, "passing %d bytes from zlib read buffer", buf->len);
255 
256  /* flag if we want to read */
257  if(sc->rbuf->len > 0)
258  s->want_read = 1;
259 
260  if(buf->len == 0)
261  return 0;
262 
263  return 1;
264 }
265 
266 static void _sx_compress_new(sx_t s, sx_plugin_t p) {
267  _sx_compress_conn_t sc;
268 
269  /* only bothering if they asked for wrappermode */
270  if(!(s->flags & SX_COMPRESS_WRAPPER) || s->compressed)
271  return;
272 
273  _sx_debug(ZONE, "preparing for compressed connect for %d", s->tag);
274 
275  sc = (_sx_compress_conn_t) calloc(1, sizeof(struct _sx_compress_conn_st));
276 
277  /* initialize streams */
278  sc->rstrm.zalloc = Z_NULL;
279  sc->rstrm.zfree = Z_NULL;
280  sc->rstrm.opaque = Z_NULL;
281  sc->rstrm.avail_in = 0;
282  sc->rstrm.next_in = Z_NULL;
283  inflateInit(&(sc->rstrm));
284 
285  sc->wstrm.zalloc = Z_NULL;
286  sc->wstrm.zfree = Z_NULL;
287  sc->wstrm.opaque = Z_NULL;
288  deflateInit(&(sc->wstrm), Z_DEFAULT_COMPRESSION);
289 
290  /* read and write buffers */
291  sc->rbuf = _sx_buffer_new(NULL, 0, NULL, NULL);
292  sc->wbuf = _sx_buffer_new(NULL, 0, NULL, NULL);
293 
294  s->plugin_data[p->index] = (void *) sc;
295 
296  /* bring the plugin online */
297  _sx_chain_io_plugin(s, p);
298 
299  /* mark stream compressed */
300  s->compressed = 1;
301 }
302 
304 static void _sx_compress_free(sx_t s, sx_plugin_t p) {
305  _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
306 
307  if(sc == NULL)
308  return;
309 
310  log_debug(ZONE, "cleaning up compression state");
311 
312  if(s->type == type_NONE) {
313  free(sc);
314  return;
315  }
316 
317  /* end streams */
318  inflateEnd(&(sc->rstrm));
319  deflateEnd(&(sc->wstrm));
320 
321  /* free buffers */
322  _sx_buffer_free(sc->rbuf);
323  _sx_buffer_free(sc->wbuf);
324 
325  free(sc);
326 
327  s->plugin_data[p->index] = NULL;
328 }
329 
331 int sx_compress_init(sx_env_t env, sx_plugin_t p, va_list args) {
332 
333  _sx_debug(ZONE, "initialising compression plugin");
334 
337  p->rio = _sx_compress_rio;
338  p->wio = _sx_compress_wio;
341  p->free = _sx_compress_free;
342 
343  return 0;
344 }
345 
346 int sx_compress_client_compress(sx_plugin_t p, sx_t s, const char *pemfile) {
347  assert((int) (p != NULL));
348  assert((int) (s != NULL));
349 
350  /* sanity */
351  if(s->type != type_CLIENT || s->state != state_STREAM) {
352  _sx_debug(ZONE, "wrong conn type or state for client compress");
353  return 1;
354  }
355 
356  /* check if we're already compressed */
357  if((s->flags & SX_COMPRESS_WRAPPER) || s->compressed) {
358  _sx_debug(ZONE, "channel already compressed");
359  return 1;
360  }
361 
362  _sx_debug(ZONE, "initiating compress sequence");
363 
364  /* go */
365  jqueue_push(s->wbufq, _sx_buffer_new("<compress xmlns='" uri_COMPRESS "'><method>zlib</method></compress>", sizeof(uri_COMPRESS)-1 + 51, NULL, NULL), 0);
366  s->want_write = 1;
367  _sx_event(s, event_WANT_WRITE, NULL);
368 
369  return 0;
370 }
Definition: nad.h:93
Definition: sx.h:113
_sx_state_t state
Definition: sx.h:313
#define _sx_event(s, e, data)
Definition: sx.h:392
unsigned int flags
Definition: sx.h:274
const char * req_to
Definition: sx.h:280
jqueue_t wbufq
Definition: sx.h:299
static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad)
Definition: compress.c:35
static void _sx_compress_new(sx_t s, sx_plugin_t p)
Definition: compress.c:266
Definition: sx.h:65
void nad_append_cdata(nad_t nad, const char *cdata, int len, int depth)
append new cdata to the last elem
Definition: nad.c:709
#define stream_err_INTERNAL_SERVER_ERROR
Definition: sx.h:131
an environment
Definition: sx.h:379
error info for event_ERROR
Definition: sx.h:99
static void _sx_compress_free(sx_t s, sx_plugin_t p)
cleanup
Definition: compress.c:304
int tag
Definition: sx.h:256
a plugin
Definition: sx.h:344
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:734
void sx_server_init(sx_t s, unsigned int flags)
Definition: server.c:228
#define SX_ERR_COMPRESS
Definition: plugins.h:44
#define NAD_ENAME(N, E)
Definition: nad.h:183
void _sx_chain_io_plugin(sx_t s, sx_plugin_t p)
Definition: chain.c:25
void(* free)(sx_t s, sx_plugin_t p)
Definition: sx.h:354
static int _sx_compress_rio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: compress.c:196
int nad_append_elem(nad_t nad, int ns, const char *name, int depth)
create a new elem on the list
Definition: nad.c:667
const char * ns
Definition: sx.h:277
void nad_free(nad_t nad)
free that nad
Definition: nad.c:178
#define SX_COMPRESS_OFFER
Definition: plugins.h:32
void(* features)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:370
void _sx_buffer_alloc_margin(sx_buf_t buf, int before, int after)
utility: ensure a certain amount of allocated space adjacent to buf-&gt;data
Definition: sx.c:264
holds the state for a single stream
Definition: sx.h:251
char * data
Definition: sx.h:114
void sx_client_init(sx_t s, unsigned int flags, const char *ns, const char *to, const char *from, const char *version)
Definition: client.c:111
#define NAD_ENAME_L(N, E)
Definition: nad.h:184
void jqueue_push(jqueue_t q, void *data, int priority)
Definition: jqueue.c:44
#define NAD_NURI_L(N, NS)
Definition: nad.h:192
Definition: sx.h:82
void _sx_buffer_free(sx_buf_t buf)
utility: kill a buffer
Definition: sx.c:246
int compressed
Definition: sx.h:340
sx_buf_t _sx_buffer_new(const char *data, int len, _sx_notify_t notify, void *notify_arg)
utility: make a new buffer if len&gt;0 but data is NULL, the buffer will contain that many bytes of garb...
Definition: sx.c:222
#define uri_COMPRESS
Definition: uri.h:44
#define uri_COMPRESS_FEATURE
Definition: uri.h:45
void sx_close(sx_t s)
Definition: io.c:480
Definition: sx.h:81
#define log_debug(...)
Definition: log.h:65
#define _sx_debug
Definition: sx.h:405
#define SX_SSL_STARTTLS_REQUIRE
Definition: plugins.h:27
void(* server)(sx_t s, sx_plugin_t p)
Definition: sx.h:357
void sx_error(sx_t s, int err, const char *text)
Definition: error.c:88
_sx_type_t type
Definition: sx.h:271
#define stream_err_INVALID_XML
Definition: sx.h:135
void _sx_reset(sx_t s)
utility; reset stream state
Definition: sx.c:154
const char * req_version
Definition: sx.h:282
const char * req_from
Definition: sx.h:281
Definition: sx.h:83
int(* rio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:361
int ssf
Definition: sx.h:337
static void _sx_compress_features(sx_t s, sx_plugin_t p, nad_t nad)
Definition: compress.c:119
unsigned int len
Definition: sx.h:115
void _sx_buffer_clear(sx_buf_t buf)
utility: clear out a buffer, but don&#39;t deallocate it
Definition: sx.c:254
int sx_compress_init(sx_env_t env, sx_plugin_t p, va_list args)
args: none
Definition: compress.c:331
static void _sx_compress_notify_compress(sx_t s, void *arg)
this plugin implements the XEP-0138: Stream Compression
Definition: compress.c:25
int(* wio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:360
#define _sx_gen_error(e, c, g, s)
helper macro to populate this struct
Definition: sx.h:106
#define ZONE
Definition: mio_impl.h:76
#define NAD_NURI(N, NS)
Definition: nad.h:191
int index
Definition: sx.h:349
#define SX_ERR_COMPRESS_FAILURE
Definition: plugins.h:45
static int _sx_compress_wio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: compress.c:135
int want_write
Definition: sx.h:304
void(* client)(sx_t s, sx_plugin_t p)
Definition: sx.h:356
int(* process)(sx_t s, sx_plugin_t p, nad_t nad)
Definition: sx.h:373
void ** plugin_data
Definition: sx.h:324
int sx_compress_client_compress(sx_plugin_t p, sx_t s, const char *pemfile)
Definition: compress.c:346
#define NAD_ENS(N, E)
Definition: nad.h:196
#define SX_COMPRESS_WRAPPER
Definition: plugins.h:31