jabberd2  2.2.16
sx/compress.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2007 Tomasz Sterna
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; version 2 of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00017  */
00018 
00023 #include "sx.h"
00024 
00025 static void _sx_compress_notify_compress(sx_t s, void *arg) {
00026 
00027     _sx_debug(ZONE, "preparing for compress");
00028 
00029     _sx_reset(s);
00030 
00031     /* start listening */
00032     sx_server_init(s, s->flags | SX_COMPRESS_WRAPPER);
00033 }
00034 
00035 static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad) {
00036     int flags;
00037     char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
00038     sx_error_t sxe;
00039 
00040     /* not interested if we're a server and we never offered it */
00041     if(s->type == type_SERVER && !(s->flags & SX_COMPRESS_OFFER))
00042         return 1;
00043 
00044     /* only want compress packets */
00045     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)
00046         return 1;
00047 
00048     /* compress from client */
00049     if(s->type == type_SERVER) {
00050         if(NAD_ENAME_L(nad, 0) == 8 && strncmp(NAD_ENAME(nad, 0), "compress", 8) == 0) {
00051             nad_free(nad);
00052 
00053             /* can't go on if we've been here before */
00054             if(s->compressed) {
00055                 _sx_debug(ZONE, "compress requested on already compressed channel, dropping packet");
00056                 return 0;
00057             }
00058 
00059             _sx_debug(ZONE, "compress requested, setting up");
00060 
00061             /* go ahead */
00062             jqueue_push(s->wbufq, _sx_buffer_new("<compressed xmlns='" uri_COMPRESS "'/>", sizeof(uri_COMPRESS)-1 + 22, _sx_compress_notify_compress, NULL), 0);
00063             s->want_write = 1;
00064 
00065             /* handled the packet */
00066             return 0;
00067         }
00068     }
00069 
00070     else if(s->type == type_CLIENT) {
00071         /* kick off the handshake */
00072         if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "compressed", 7) == 0) {
00073             nad_free(nad);
00074 
00075             /* save interesting bits */
00076             flags = s->flags;
00077 
00078             if(s->ns != NULL) ns = strdup(s->ns);
00079 
00080             if(s->req_to != NULL) to = strdup(s->req_to);
00081             if(s->req_from != NULL) from = strdup(s->req_from);
00082             if(s->req_version != NULL) version = strdup(s->req_version);
00083 
00084             /* reset state */
00085             _sx_reset(s);
00086 
00087             _sx_debug(ZONE, "server ready for compression, starting");
00088 
00089             /* second time round */
00090             sx_client_init(s, flags | SX_COMPRESS_WRAPPER, ns, to, from, version);
00091 
00092             /* free bits */
00093             if(ns != NULL) free(ns);
00094             if(to != NULL) free(to);
00095             if(from != NULL) free(from);
00096             if(version != NULL) free(version);
00097 
00098             return 0;
00099         }
00100 
00101         /* busted server */
00102         if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) {
00103             nad_free(nad);
00104 
00105             _sx_debug(ZONE, "server can't handle compression, business as usual");
00106 
00107             _sx_gen_error(sxe, SX_ERR_COMPRESS_FAILURE, "compress failure", "Server was unable to establish compression");
00108             _sx_event(s, event_ERROR, (void *) &sxe);
00109 
00110             return 0;
00111         }
00112     }
00113 
00114     _sx_debug(ZONE, "unknown compress namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
00115     nad_free(nad);
00116     return 0;
00117 }
00118 
00119 static void _sx_compress_features(sx_t s, sx_plugin_t p, nad_t nad) {
00120     int ns;
00121 
00122     /* if the session is already compressed, or the app told us not to,
00123      * or STARTTLS is required and stream is not encrypted yet, then we don't offer anything */
00124     if(s->compressed || !(s->flags & SX_COMPRESS_OFFER) || ((s->flags & SX_SSL_STARTTLS_REQUIRE) && s->ssf == 0))
00125         return;
00126 
00127     _sx_debug(ZONE, "offering compression");
00128 
00129     ns = nad_add_namespace(nad, uri_COMPRESS_FEATURE, NULL);
00130     nad_append_elem(nad, ns, "compression", 1);
00131     nad_append_elem(nad, ns, "method", 2);
00132     nad_append_cdata(nad, "zlib", 4, 3);
00133 }
00134 
00135 static int _sx_compress_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00136     _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
00137     int ret;
00138     sx_error_t sxe;
00139 
00140     /* only bothering if they asked for wrappermode */
00141     if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
00142         return 1;
00143 
00144     _sx_debug(ZONE, "in _sx_compress_wio");
00145 
00146     /* move the data into the zlib write buffer */
00147     if(buf->len > 0) {
00148         _sx_debug(ZONE, "loading %d bytes into zlib write buffer", buf->len);
00149 
00150         _sx_buffer_alloc_margin(sc->wbuf, 0, buf->len);
00151         memcpy(sc->wbuf->data + sc->wbuf->len, buf->data, buf->len);
00152         sc->wbuf->len += buf->len;
00153 
00154         _sx_buffer_clear(buf);
00155     }
00156 
00157     /* compress the data */
00158     if(sc->wbuf->len > 0) {
00159         sc->wstrm.avail_in = sc->wbuf->len;
00160         sc->wstrm.next_in = sc->wbuf->data;
00161         /* deflate() on write buffer until there is data to compress */
00162         do {
00163             /* make place for deflated data */
00164             _sx_buffer_alloc_margin(buf, 0, sc->wbuf->len + SX_COMPRESS_CHUNK);
00165 
00166                 sc->wstrm.avail_out = sc->wbuf->len + SX_COMPRESS_CHUNK;
00167             sc->wstrm.next_out = buf->data + buf->len;
00168 
00169             ret = deflate(&(sc->wstrm), Z_SYNC_FLUSH);
00170             assert(ret != Z_STREAM_ERROR);
00171 
00172             buf->len += sc->wbuf->len + SX_COMPRESS_CHUNK - sc->wstrm.avail_out;
00173 
00174         } while (sc->wstrm.avail_out == 0);
00175 
00176         if(ret != Z_OK || sc->wstrm.avail_in != 0) {
00177             /* throw an error */
00178             _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during compression");
00179             _sx_event(s, event_ERROR, (void *) &sxe);
00180 
00181             sx_error(s, stream_err_INTERNAL_SERVER_ERROR, "Error during compression");
00182             sx_close(s);
00183 
00184             return -2;  /* fatal */
00185         }
00186 
00187         sc->wbuf->len = sc->wstrm.avail_in;
00188         sc->wbuf->data = sc->wstrm.next_in;
00189     }
00190 
00191     _sx_debug(ZONE, "passing %d bytes from zlib write buffer", buf->len);
00192 
00193     return 1;
00194 }
00195 
00196 static int _sx_compress_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
00197     _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
00198     int ret;
00199     sx_error_t sxe;
00200 
00201     /* only bothering if they asked for wrappermode */
00202     if(!(s->flags & SX_COMPRESS_WRAPPER) || !s->compressed)
00203         return 1;
00204 
00205     _sx_debug(ZONE, "in _sx_compress_rio");
00206 
00207     /* move the data into the zlib read buffer */
00208     if(buf->len > 0) {
00209         _sx_debug(ZONE, "loading %d bytes into zlib read buffer", buf->len);
00210 
00211         _sx_buffer_alloc_margin(sc->rbuf, 0, buf->len);
00212         memcpy(sc->rbuf->data + sc->rbuf->len, buf->data, buf->len);
00213         sc->rbuf->len += buf->len;
00214 
00215         _sx_buffer_clear(buf);
00216     }
00217 
00218     /* decompress the data */
00219     if(sc->rbuf->len > 0) {
00220         sc->rstrm.avail_in = sc->rbuf->len;
00221         sc->rstrm.next_in = sc->rbuf->data;
00222         /* run inflate() on read buffer while able to fill the output buffer */
00223         do {
00224             /* make place for inflated data */
00225             _sx_buffer_alloc_margin(buf, 0, SX_COMPRESS_CHUNK);
00226 
00227             sc->rstrm.avail_out = SX_COMPRESS_CHUNK;
00228             sc->rstrm.next_out = buf->data + buf->len;
00229 
00230             ret = inflate(&(sc->rstrm), Z_SYNC_FLUSH);
00231             assert(ret != Z_STREAM_ERROR);
00232             switch (ret) {
00233             case Z_NEED_DICT:
00234             case Z_DATA_ERROR:
00235             case Z_MEM_ERROR:
00236                 /* throw an error */
00237                 _sx_gen_error(sxe, SX_ERR_COMPRESS, "compression error", "Error during decompression");
00238                 _sx_event(s, event_ERROR, (void *) &sxe);
00239 
00240                 sx_error(s, stream_err_INVALID_XML, "Error during decompression");
00241                 sx_close(s);
00242 
00243                 return -2;
00244             }
00245 
00246             buf->len += SX_COMPRESS_CHUNK - sc->rstrm.avail_out;
00247 
00248         } while (sc->rstrm.avail_out == 0);
00249 
00250         sc->rbuf->len = sc->rstrm.avail_in;
00251         sc->rbuf->data = sc->rstrm.next_in;
00252     }
00253 
00254     _sx_debug(ZONE, "passing %d bytes from zlib read buffer", buf->len);
00255 
00256     /* flag if we want to read */
00257     if(sc->rbuf->len > 0)
00258     s->want_read = 1;
00259 
00260     if(buf->len == 0)
00261         return 0;
00262 
00263     return 1;
00264 }
00265 
00266 static void _sx_compress_new(sx_t s, sx_plugin_t p) {
00267     _sx_compress_conn_t sc;
00268 
00269     /* only bothering if they asked for wrappermode */
00270     if(!(s->flags & SX_COMPRESS_WRAPPER) || s->compressed)
00271         return;
00272 
00273     _sx_debug(ZONE, "preparing for compressed connect for %d", s->tag);
00274 
00275     sc = (_sx_compress_conn_t) calloc(1, sizeof(struct _sx_compress_conn_st));
00276 
00277     /* initialize streams */
00278     sc->rstrm.zalloc = Z_NULL;
00279     sc->rstrm.zfree = Z_NULL;
00280     sc->rstrm.opaque = Z_NULL;
00281     sc->rstrm.avail_in = 0;
00282     sc->rstrm.next_in = Z_NULL;
00283     inflateInit(&(sc->rstrm));
00284 
00285     sc->wstrm.zalloc = Z_NULL;
00286     sc->wstrm.zfree = Z_NULL;
00287     sc->wstrm.opaque = Z_NULL;
00288     deflateInit(&(sc->wstrm), Z_DEFAULT_COMPRESSION);
00289 
00290     /* read and write buffers */
00291     sc->rbuf = _sx_buffer_new(NULL, 0, NULL, NULL);
00292     sc->wbuf = _sx_buffer_new(NULL, 0, NULL, NULL);
00293 
00294     s->plugin_data[p->index] = (void *) sc;
00295 
00296     /* bring the plugin online */
00297     _sx_chain_io_plugin(s, p);
00298 
00299     /* mark stream compressed */
00300     s->compressed = 1;
00301 }
00302 
00304 static void _sx_compress_free(sx_t s, sx_plugin_t p) {
00305     _sx_compress_conn_t sc = (_sx_compress_conn_t) s->plugin_data[p->index];
00306 
00307     if(sc == NULL)
00308         return;
00309 
00310     log_debug(ZONE, "cleaning up compression state");
00311 
00312     if(s->type == type_NONE) {
00313         free(sc);
00314         return;
00315     }
00316 
00317     /* end streams */
00318     inflateEnd(&(sc->rstrm));
00319     deflateEnd(&(sc->wstrm));
00320 
00321     /* free buffers */
00322     _sx_buffer_free(sc->rbuf);
00323     _sx_buffer_free(sc->wbuf);
00324 
00325     free(sc);
00326 
00327     s->plugin_data[p->index] = NULL;
00328 }
00329 
00331 int sx_compress_init(sx_env_t env, sx_plugin_t p, va_list args) {
00332 
00333     _sx_debug(ZONE, "initialising compression plugin");
00334 
00335     p->client = _sx_compress_new;
00336     p->server = _sx_compress_new;
00337     p->rio = _sx_compress_rio;
00338     p->wio = _sx_compress_wio;
00339     p->features = _sx_compress_features;
00340     p->process = _sx_compress_process;
00341     p->free = _sx_compress_free;
00342 
00343     return 0;
00344 }
00345 
00346 int sx_compress_client_compress(sx_plugin_t p, sx_t s, char *pemfile) {
00347     assert((int) (p != NULL));
00348     assert((int) (s != NULL));
00349 
00350     /* sanity */
00351     if(s->type != type_CLIENT || s->state != state_STREAM) {
00352         _sx_debug(ZONE, "wrong conn type or state for client compress");
00353         return 1;
00354     }
00355 
00356     /* check if we're already compressed */
00357     if((s->flags & SX_COMPRESS_WRAPPER) || s->compressed) {
00358         _sx_debug(ZONE, "channel already compressed");
00359         return 1;
00360     }
00361 
00362     _sx_debug(ZONE, "initiating compress sequence");
00363 
00364     /* go */
00365     jqueue_push(s->wbufq, _sx_buffer_new("<compress xmlns='" uri_COMPRESS "'><method>zlib</method></compress>", sizeof(uri_COMPRESS)-1 + 51, NULL, NULL), 0);
00366     s->want_write = 1;
00367     _sx_event(s, event_WANT_WRITE, NULL);
00368 
00369     return 0;
00370 }