NeoMutt  2019-12-07-168-gc45f47
Teaching an old dog new tricks
DOXYGEN
mutt_zstrm.c File Reference

Zlib compression of network traffic. More...

#include "config.h"
#include <errno.h>
#include <string.h>
#include <zlib.h>
#include "conn/lib.h"
#include "mutt.h"
#include "mutt_zstrm.h"
#include "mutt_socket.h"
+ Include dependency graph for mutt_zstrm.c:

Go to the source code of this file.

Data Structures

struct  ZstrmDirection
 A stream of data being (de-)compressed. More...
 
struct  ZstrmContext
 Data compression layer. More...
 

Functions

static void * zstrm_malloc (void *opaque, unsigned int items, unsigned int size)
 Redirector function for zlib's malloc() More...
 
static void zstrm_free (void *opaque, void *address)
 Redirector function for zlib's free() More...
 
static int zstrm_open (struct Connection *conn)
 Open a socket - Implements Connection::conn_open() More...
 
static int zstrm_close (struct Connection *conn)
 Close a socket - Implements Connection::conn_close() More...
 
static int zstrm_read (struct Connection *conn, char *buf, size_t len)
 Read compressed data from a socket - Implements Connection::conn_read() More...
 
static int zstrm_poll (struct Connection *conn, time_t wait_secs)
 Checks whether reads would block - Implements Connection::conn_poll() More...
 
static int zstrm_write (struct Connection *conn, const char *buf, size_t count)
 Write compressed data to a socket - Implements Connection::conn_write() More...
 
void mutt_zstrm_wrap_conn (struct Connection *conn)
 Wrap a compression layer around a Connection. More...
 

Detailed Description

Zlib compression of network traffic.

Copyright(C) 2019 Fabian Groffen grobi.nosp@m.an@g.nosp@m.entoo.nosp@m..org

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file mutt_zstrm.c.

Function Documentation

◆ zstrm_malloc()

static void* zstrm_malloc ( void *  opaque,
unsigned int  items,
unsigned int  size 
)
static

Redirector function for zlib's malloc()

Parameters
opaqueOpaque zlib handle
itemsNumber of blocks
sizeSize of blocks
Return values
ptrMemory on the heap

Definition at line 67 of file mutt_zstrm.c.

68 {
69  return mutt_mem_calloc(items, size);
70 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ zstrm_free()

static void zstrm_free ( void *  opaque,
void *  address 
)
static

Redirector function for zlib's free()

Parameters
opaqueOpaque zlib handle
addressMemory to free

Definition at line 77 of file mutt_zstrm.c.

78 {
79  FREE(&address);
80 }
#define FREE(x)
Definition: memory.h:40
+ Here is the caller graph for this function:

◆ zstrm_open()

static int zstrm_open ( struct Connection conn)
static

Open a socket - Implements Connection::conn_open()

Return values
-1Always

Cannot open a zlib connection, must wrap an existing one

Definition at line 88 of file mutt_zstrm.c.

89 {
90  return -1;
91 }
+ Here is the caller graph for this function:

◆ zstrm_close()

static int zstrm_close ( struct Connection conn)
static

Close a socket - Implements Connection::conn_close()

Definition at line 96 of file mutt_zstrm.c.

97 {
98  struct ZstrmContext *zctx = conn->sockdata;
99 
100  int rc = zctx->next_conn.conn_close(&zctx->next_conn);
101 
102  mutt_debug(LL_DEBUG5, "read %llu->%llu (%.1fx) wrote %llu<-%llu (%.1fx)\n",
103  zctx->read.z.total_in, zctx->read.z.total_out,
104  (float) zctx->read.z.total_out / (float) zctx->read.z.total_in,
105  zctx->write.z.total_in, zctx->write.z.total_out,
106  (float) zctx->write.z.total_in / (float) zctx->write.z.total_out);
107 
108  // Restore the Connection's original functions
109  conn->sockdata = zctx->next_conn.sockdata;
110  conn->conn_open = zctx->next_conn.conn_open;
111  conn->conn_close = zctx->next_conn.conn_close;
112  conn->conn_read = zctx->next_conn.conn_read;
113  conn->conn_write = zctx->next_conn.conn_write;
114  conn->conn_poll = zctx->next_conn.conn_poll;
115 
116  inflateEnd(&zctx->read.z);
117  deflateEnd(&zctx->write.z);
118  FREE(&zctx->read.buf);
119  FREE(&zctx->write.buf);
120  FREE(&zctx);
121 
122  return rc;
123 }
Data compression layer.
Definition: mutt_zstrm.c:53
struct ZstrmDirection write
Data being compressed and written.
Definition: mutt_zstrm.c:56
int(* conn_open)(struct Connection *conn)
Open a socket Connection.
Definition: connection.h:53
int(* conn_read)(struct Connection *conn, char *buf, size_t count)
Read from a socket Connection.
Definition: connection.h:62
void * sockdata
Definition: connection.h:45
struct Connection next_conn
Underlying stream.
Definition: mutt_zstrm.c:57
struct ZstrmDirection read
Data being read and de-compressed.
Definition: mutt_zstrm.c:55
int(* conn_poll)(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block.
Definition: connection.h:80
int(* conn_close)(struct Connection *conn)
Close a socket Connection.
Definition: connection.h:87
int(* conn_write)(struct Connection *conn, const char *buf, size_t count)
Write to a socket Connection.
Definition: connection.h:71
char * buf
Buffer for data being (de-)compressed.
Definition: mutt_zstrm.c:43
#define FREE(x)
Definition: memory.h:40
z_stream z
zlib compression handle
Definition: mutt_zstrm.c:42
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
+ Here is the caller graph for this function:

◆ zstrm_read()

static int zstrm_read ( struct Connection conn,
char *  buf,
size_t  len 
)
static

Read compressed data from a socket - Implements Connection::conn_read()

Definition at line 128 of file mutt_zstrm.c.

129 {
130  struct ZstrmContext *zctx = conn->sockdata;
131  int rc = 0;
132  int zrc = 0;
133 
134 retry:
135  if (zctx->read.stream_eof)
136  return 0;
137 
138  /* when avail_out was 0 on last call, we need to call inflate again, because
139  * more data might be available using the current input, so avoid callling
140  * read on the underlying stream in that case (for it might block) */
141  if ((zctx->read.pos == 0) && !zctx->read.conn_eof)
142  {
143  rc = zctx->next_conn.conn_read(&zctx->next_conn, zctx->read.buf, zctx->read.len);
144  mutt_debug(LL_DEBUG5, "consuming data from next stream: %d bytes\n", rc);
145  if (rc < 0)
146  return rc;
147  else if (rc == 0)
148  zctx->read.conn_eof = true;
149  else
150  zctx->read.pos += rc;
151  }
152 
153  zctx->read.z.avail_in = (uInt) zctx->read.pos;
154  zctx->read.z.next_in = (Bytef *) zctx->read.buf;
155  zctx->read.z.avail_out = (uInt) len;
156  zctx->read.z.next_out = (Bytef *) buf;
157 
158  zrc = inflate(&zctx->read.z, Z_SYNC_FLUSH);
159  mutt_debug(LL_DEBUG5, "rc=%d, consumed %u/%u bytes, produced %u/%u bytes\n",
160  zrc, zctx->read.pos - zctx->read.z.avail_in, zctx->read.pos,
161  len - zctx->read.z.avail_out, len);
162 
163  /* shift any remaining input data to the front of the buffer */
164  if ((Bytef *) zctx->read.buf != zctx->read.z.next_in)
165  {
166  memmove(zctx->read.buf, zctx->read.z.next_in, zctx->read.z.avail_in);
167  zctx->read.pos = zctx->read.z.avail_in;
168  }
169 
170  switch (zrc)
171  {
172  case Z_OK: /* progress has been made */
173  zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
174  if (zrc == 0)
175  {
176  /* there was progress, so must have been reading input */
177  mutt_debug(LL_DEBUG5, "inflate just consumed\n");
178  goto retry;
179  }
180  break;
181 
182  case Z_STREAM_END: /* everything flushed, nothing remaining */
183  mutt_debug(LL_DEBUG5, "inflate returned Z_STREAM_END.\n");
184  zrc = len - zctx->read.z.avail_out; /* "returned" bytes */
185  zctx->read.stream_eof = true;
186  break;
187 
188  case Z_BUF_ERROR: /* no progress was possible */
189  if (!zctx->read.conn_eof)
190  {
191  mutt_debug(LL_DEBUG5, "inflate returned Z_BUF_ERROR. retrying.\n");
192  goto retry;
193  }
194  zrc = 0;
195  break;
196 
197  default:
198  /* bail on other rcs, such as Z_DATA_ERROR, or Z_MEM_ERROR */
199  mutt_debug(LL_DEBUG5, "inflate returned %d. aborting.\n", zrc);
200  zrc = -1;
201  break;
202  }
203 
204  return zrc;
205 }
Data compression layer.
Definition: mutt_zstrm.c:53
int(* conn_read)(struct Connection *conn, char *buf, size_t count)
Read from a socket Connection.
Definition: connection.h:62
void * sockdata
Definition: connection.h:45
bool conn_eof
Connection end-of-file reached.
Definition: mutt_zstrm.c:46
struct Connection next_conn
Underlying stream.
Definition: mutt_zstrm.c:57
struct ZstrmDirection read
Data being read and de-compressed.
Definition: mutt_zstrm.c:55
bool stream_eof
Stream end-of-file reached.
Definition: mutt_zstrm.c:47
unsigned int len
Length of data.
Definition: mutt_zstrm.c:44
unsigned int pos
Current position.
Definition: mutt_zstrm.c:45
char * buf
Buffer for data being (de-)compressed.
Definition: mutt_zstrm.c:43
z_stream z
zlib compression handle
Definition: mutt_zstrm.c:42
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
+ Here is the caller graph for this function:

◆ zstrm_poll()

static int zstrm_poll ( struct Connection conn,
time_t  wait_secs 
)
static

Checks whether reads would block - Implements Connection::conn_poll()

Definition at line 210 of file mutt_zstrm.c.

211 {
212  struct ZstrmContext *zctx = conn->sockdata;
213 
214  mutt_debug(LL_DEBUG5, "%s\n",
215  (zctx->read.z.avail_out == 0) || (zctx->read.pos > 0) ?
216  "last read wrote full buffer" :
217  "falling back on next stream");
218  if ((zctx->read.z.avail_out == 0) || (zctx->read.pos > 0))
219  return 1;
220 
221  return zctx->next_conn.conn_poll(&zctx->next_conn, wait_secs);
222 }
Data compression layer.
Definition: mutt_zstrm.c:53
void * sockdata
Definition: connection.h:45
struct Connection next_conn
Underlying stream.
Definition: mutt_zstrm.c:57
struct ZstrmDirection read
Data being read and de-compressed.
Definition: mutt_zstrm.c:55
int(* conn_poll)(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block.
Definition: connection.h:80
unsigned int pos
Current position.
Definition: mutt_zstrm.c:45
z_stream z
zlib compression handle
Definition: mutt_zstrm.c:42
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
+ Here is the caller graph for this function:

◆ zstrm_write()

static int zstrm_write ( struct Connection conn,
const char *  buf,
size_t  count 
)
static

Write compressed data to a socket - Implements Connection::conn_write()

Definition at line 227 of file mutt_zstrm.c.

228 {
229  struct ZstrmContext *zctx = conn->sockdata;
230  int rc;
231 
232  zctx->write.z.avail_in = (uInt) count;
233  zctx->write.z.next_in = (Bytef *) buf;
234  zctx->write.z.avail_out = (uInt) zctx->write.len;
235  zctx->write.z.next_out = (Bytef *) zctx->write.buf;
236 
237  do
238  {
239  int zrc = deflate(&zctx->write.z, Z_PARTIAL_FLUSH);
240  if (zrc == Z_OK)
241  {
242  /* push out produced data to the underlying stream */
243  zctx->write.pos = zctx->write.len - zctx->write.z.avail_out;
244  char *wbufp = zctx->write.buf;
245  mutt_debug(LL_DEBUG5, "deflate consumed %d/%d bytes\n",
246  count - zctx->write.z.avail_in, count);
247  while (zctx->write.pos > 0)
248  {
249  rc = zctx->next_conn.conn_write(&zctx->next_conn, wbufp, zctx->write.pos);
250  mutt_debug(LL_DEBUG5, "next stream wrote: %d bytes\n", rc);
251  if (rc < 0)
252  return -1; /* we can't recover from write failure */
253 
254  wbufp += rc;
255  zctx->write.pos -= rc;
256  }
257 
258  /* see if there's more for us to do, retry if the output buffer
259  * was full (there may be something in zlib buffers), and retry
260  * when there is still available input data */
261  if ((zctx->write.z.avail_out != 0) && (zctx->write.z.avail_in == 0))
262  break;
263 
264  zctx->write.z.avail_out = (uInt) zctx->write.len;
265  zctx->write.z.next_out = (Bytef *) zctx->write.buf;
266  }
267  else
268  {
269  /* compression went wrong, but this is basically impossible
270  * according to the docs */
271  return -1;
272  }
273  } while (true);
274 
275  rc = (int) count;
276  return (rc <= 0) ? 1 : rc; /* avoid wrong behaviour due to overflow */
277 }
Data compression layer.
Definition: mutt_zstrm.c:53
struct ZstrmDirection write
Data being compressed and written.
Definition: mutt_zstrm.c:56
void * sockdata
Definition: connection.h:45
struct Connection next_conn
Underlying stream.
Definition: mutt_zstrm.c:57
unsigned int len
Length of data.
Definition: mutt_zstrm.c:44
int(* conn_write)(struct Connection *conn, const char *buf, size_t count)
Write to a socket Connection.
Definition: connection.h:71
unsigned int pos
Current position.
Definition: mutt_zstrm.c:45
char * buf
Buffer for data being (de-)compressed.
Definition: mutt_zstrm.c:43
z_stream z
zlib compression handle
Definition: mutt_zstrm.c:42
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 5.
Definition: logging.h:44
+ Here is the caller graph for this function:

◆ mutt_zstrm_wrap_conn()

void mutt_zstrm_wrap_conn ( struct Connection conn)

Wrap a compression layer around a Connection.

Parameters
connConnection to wrap

Replace the read/write functions with our compression functions. After reading from the socket, we decompress and pass on the data. Before writing to a socket, we compress the data.

Definition at line 287 of file mutt_zstrm.c.

288 {
289  struct ZstrmContext *zctx = mutt_mem_calloc(1, sizeof(struct ZstrmContext));
290 
291  /* store wrapped stream as next stream */
292  zctx->next_conn.fd = conn->fd;
293  zctx->next_conn.sockdata = conn->sockdata;
294  zctx->next_conn.conn_open = conn->conn_open;
295  zctx->next_conn.conn_close = conn->conn_close;
296  zctx->next_conn.conn_read = conn->conn_read;
297  zctx->next_conn.conn_write = conn->conn_write;
298  zctx->next_conn.conn_poll = conn->conn_poll;
299 
300  /* replace connection with our wrappers, where appropriate */
301  conn->sockdata = zctx;
302  conn->conn_open = zstrm_open;
303  conn->conn_read = zstrm_read;
304  conn->conn_write = zstrm_write;
305  conn->conn_close = zstrm_close;
306  conn->conn_poll = zstrm_poll;
307 
308  /* allocate/setup (de)compression buffers */
309  zctx->read.len = 8192;
310  zctx->read.buf = mutt_mem_malloc(zctx->read.len);
311  zctx->read.pos = 0;
312  zctx->write.len = 8192;
313  zctx->write.buf = mutt_mem_malloc(zctx->write.len);
314  zctx->write.pos = 0;
315 
316  /* initialise zlib for inflate and deflate for RFC4978 */
317  zctx->read.z.zalloc = zstrm_malloc;
318  zctx->read.z.zfree = zstrm_free;
319  zctx->read.z.opaque = NULL;
320  zctx->read.z.avail_out = zctx->read.len;
321  inflateInit2(&zctx->read.z, -15);
322  zctx->write.z.zalloc = zstrm_malloc;
323  zctx->write.z.zfree = zstrm_free;
324  zctx->write.z.opaque = NULL;
325  zctx->write.z.avail_out = zctx->write.len;
326  deflateInit2(&zctx->write.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
327 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
Data compression layer.
Definition: mutt_zstrm.c:53
struct ZstrmDirection write
Data being compressed and written.
Definition: mutt_zstrm.c:56
int(* conn_open)(struct Connection *conn)
Open a socket Connection.
Definition: connection.h:53
int(* conn_read)(struct Connection *conn, char *buf, size_t count)
Read from a socket Connection.
Definition: connection.h:62
static int zstrm_read(struct Connection *conn, char *buf, size_t len)
Read compressed data from a socket - Implements Connection::conn_read()
Definition: mutt_zstrm.c:128
static int zstrm_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::conn_poll()
Definition: mutt_zstrm.c:210
void * sockdata
Definition: connection.h:45
struct Connection next_conn
Underlying stream.
Definition: mutt_zstrm.c:57
static int zstrm_open(struct Connection *conn)
Open a socket - Implements Connection::conn_open()
Definition: mutt_zstrm.c:88
struct ZstrmDirection read
Data being read and de-compressed.
Definition: mutt_zstrm.c:55
int(* conn_poll)(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block.
Definition: connection.h:80
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
unsigned int len
Length of data.
Definition: mutt_zstrm.c:44
static void zstrm_free(void *opaque, void *address)
Redirector function for zlib&#39;s free()
Definition: mutt_zstrm.c:77
int(* conn_close)(struct Connection *conn)
Close a socket Connection.
Definition: connection.h:87
int(* conn_write)(struct Connection *conn, const char *buf, size_t count)
Write to a socket Connection.
Definition: connection.h:71
unsigned int pos
Current position.
Definition: mutt_zstrm.c:45
static int zstrm_close(struct Connection *conn)
Close a socket - Implements Connection::conn_close()
Definition: mutt_zstrm.c:96
char * buf
Buffer for data being (de-)compressed.
Definition: mutt_zstrm.c:43
static void * zstrm_malloc(void *opaque, unsigned int items, unsigned int size)
Redirector function for zlib&#39;s malloc()
Definition: mutt_zstrm.c:67
z_stream z
zlib compression handle
Definition: mutt_zstrm.c:42
static int zstrm_write(struct Connection *conn, const char *buf, size_t count)
Write compressed data to a socket - Implements Connection::conn_write()
Definition: mutt_zstrm.c:227
+ Here is the call graph for this function:
+ Here is the caller graph for this function: