1 /*
   2  * Author: Tatu Ylonen <ylo@cs.hut.fi>
   3  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
   4  *                    All rights reserved
   5  * Interface to packet compression for ssh.
   6  *
   7  * As far as I am concerned, the code I have written for this software
   8  * can be used freely for any purpose.  Any derived versions of this
   9  * software must be clearly marked as such, and if the derived work is
  10  * incompatible with the protocol description in the RFC file, it must be
  11  * called by a name other than "ssh" or "Secure Shell".
  12  */
  13 
  14 #include "includes.h"
  15 RCSID("$OpenBSD: compress.c,v 1.19 2002/03/18 17:31:54 provos Exp $");
  16 
  17 #pragma ident   "%Z%%M% %I%     %E% SMI"
  18 
  19 #include "log.h"
  20 #include "buffer.h"
  21 #include "zlib.h"
  22 #include "compress.h"
  23 
  24 z_stream incoming_stream;
  25 z_stream outgoing_stream;
  26 static int compress_init_send_called = 0;
  27 static int compress_init_recv_called = 0;
  28 static int inflate_failed = 0;
  29 static int deflate_failed = 0;
  30 
  31 /*
  32  * Initializes compression; level is compression level from 1 to 9
  33  * (as in gzip).
  34  */
  35 
  36 void
  37 buffer_compress_init_send(int level)
  38 {
  39         if (compress_init_send_called == 1)
  40                 deflateEnd(&outgoing_stream);
  41         compress_init_send_called = 1;
  42         debug("Enabling compression at level %d.", level);
  43         if (level < 1 || level > 9)
  44                 fatal("Bad compression level %d.", level);
  45         deflateInit(&outgoing_stream, level);
  46 }
  47 void
  48 buffer_compress_init_recv(void)
  49 {
  50         if (compress_init_recv_called == 1)
  51                 inflateEnd(&incoming_stream);
  52         compress_init_recv_called = 1;
  53         inflateInit(&incoming_stream);
  54 }
  55 
  56 /* Frees any data structures allocated for compression. */
  57 
  58 void
  59 buffer_compress_uninit(void)
  60 {
  61         debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f",
  62             outgoing_stream.total_in, outgoing_stream.total_out,
  63             outgoing_stream.total_in == 0 ? 0.0 :
  64             (double) outgoing_stream.total_out / outgoing_stream.total_in);
  65         debug("compress incoming: raw data %lu, compressed %lu, factor %.2f",
  66             incoming_stream.total_out, incoming_stream.total_in,
  67             incoming_stream.total_out == 0 ? 0.0 :
  68             (double) incoming_stream.total_in / incoming_stream.total_out);
  69         if (compress_init_recv_called == 1 && inflate_failed == 0)
  70                 inflateEnd(&incoming_stream);
  71         if (compress_init_send_called == 1 && deflate_failed == 0)
  72                 deflateEnd(&outgoing_stream);
  73 }
  74 
  75 /*
  76  * Compresses the contents of input_buffer into output_buffer.  All packets
  77  * compressed using this function will form a single compressed data stream;
  78  * however, data will be flushed at the end of every call so that each
  79  * output_buffer can be decompressed independently (but in the appropriate
  80  * order since they together form a single compression stream) by the
  81  * receiver.  This appends the compressed data to the output buffer.
  82  */
  83 
  84 void
  85 buffer_compress(Buffer * input_buffer, Buffer * output_buffer)
  86 {
  87         u_char buf[4096];
  88         int status;
  89 
  90         /* This case is not handled below. */
  91         if (buffer_len(input_buffer) == 0)
  92                 return;
  93 
  94         /* Input is the contents of the input buffer. */
  95         outgoing_stream.next_in = buffer_ptr(input_buffer);
  96         outgoing_stream.avail_in = buffer_len(input_buffer);
  97 
  98         /* Loop compressing until deflate() returns with avail_out != 0. */
  99         do {
 100                 /* Set up fixed-size output buffer. */
 101                 outgoing_stream.next_out = buf;
 102                 outgoing_stream.avail_out = sizeof(buf);
 103 
 104                 /* Compress as much data into the buffer as possible. */
 105                 status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH);
 106                 switch (status) {
 107                 case Z_OK:
 108                         /* Append compressed data to output_buffer. */
 109                         buffer_append(output_buffer, buf,
 110                             sizeof(buf) - outgoing_stream.avail_out);
 111                         break;
 112                 default:
 113                         deflate_failed = 1;
 114                         fatal("buffer_compress: deflate returned %d", status);
 115                         /* NOTREACHED */
 116                 }
 117         } while (outgoing_stream.avail_out == 0);
 118 }
 119 
 120 /*
 121  * Uncompresses the contents of input_buffer into output_buffer.  All packets
 122  * uncompressed using this function will form a single compressed data
 123  * stream; however, data will be flushed at the end of every call so that
 124  * each output_buffer.  This must be called for the same size units that the
 125  * buffer_compress was called, and in the same order that buffers compressed
 126  * with that.  This appends the uncompressed data to the output buffer.
 127  */
 128 
 129 void
 130 buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer)
 131 {
 132         u_char buf[4096];
 133         int status;
 134 
 135         incoming_stream.next_in = buffer_ptr(input_buffer);
 136         incoming_stream.avail_in = buffer_len(input_buffer);
 137 
 138         for (;;) {
 139                 /* Set up fixed-size output buffer. */
 140                 incoming_stream.next_out = buf;
 141                 incoming_stream.avail_out = sizeof(buf);
 142 
 143                 status = inflate(&incoming_stream, Z_PARTIAL_FLUSH);
 144                 switch (status) {
 145                 case Z_OK:
 146                         buffer_append(output_buffer, buf,
 147                             sizeof(buf) - incoming_stream.avail_out);
 148                         break;
 149                 case Z_BUF_ERROR:
 150                         /*
 151                          * Comments in zlib.h say that we should keep calling
 152                          * inflate() until we get an error.  This appears to
 153                          * be the error that we get.
 154                          */
 155                         return;
 156                 default:
 157                         inflate_failed = 1;
 158                         fatal("buffer_uncompress: inflate returned %d", status);
 159                         /* NOTREACHED */
 160                 }
 161         }
 162 }