INN commit: trunk (15 files)

INN Commit rra at isc.org
Sun Dec 6 21:31:43 UTC 2015


    Date: Sunday, December 6, 2015 @ 13:31:42
  Author: iulius
Revision: 9964

Initial implementation of the COMPRESS command in INN

Based on draft-murchison-nntp-compress-02 (still not published as
an RFC).

The COMPRESS command is an extension to the NNTP protocol to allow a
connection to be effectively and efficiently compressed.  News clients
that also support that extension will be able to benefit from that
bandwidth optimization and improvement in speed.

Initial implementation in INN contributed by Julien Elie.

Added:
  trunk/nnrpd/zlib.c
Modified:
  trunk/MANIFEST
  trunk/Makefile.global.in
  trunk/configure.ac
  trunk/doc/pod/install.pod
  trunk/doc/pod/news.pod
  trunk/include/inn/nntp.h
  trunk/innd/nc.c
  trunk/nnrpd/Makefile
  trunk/nnrpd/article.c
  trunk/nnrpd/commands.c
  trunk/nnrpd/line.c
  trunk/nnrpd/misc.c
  trunk/nnrpd/nnrpd.c
  trunk/nnrpd/nnrpd.h

---------------------+
 MANIFEST            |    1 
 Makefile.global.in  |   15 +++++-
 configure.ac        |   10 ++--
 doc/pod/install.pod |   22 ++++++----
 doc/pod/news.pod    |    8 +++
 include/inn/nntp.h  |    1 
 innd/nc.c           |    3 +
 nnrpd/Makefile      |   49 ++++++++++++++--------
 nnrpd/article.c     |   23 ++++++++++
 nnrpd/commands.c    |    8 +++
 nnrpd/line.c        |   78 +++++++++++++++++++++++++++++++++++
 nnrpd/misc.c        |   57 +++++++++++++++++++++++++
 nnrpd/nnrpd.c       |  110 +++++++++++++++++++++++++++++++++++++++++++++-----
 nnrpd/nnrpd.h       |   28 ++++++++++++
 nnrpd/zlib.c        |  100 +++++++++++++++++++++++++++++++++++++++++++++
 15 files changed, 467 insertions(+), 46 deletions(-)

Modified: MANIFEST
===================================================================
--- MANIFEST	2015-12-06 18:31:27 UTC (rev 9963)
+++ MANIFEST	2015-12-06 21:31:42 UTC (rev 9964)
@@ -622,6 +622,7 @@
 nnrpd/tls.c                           Transport layer security
 nnrpd/tls.h                           Transport layer security data types
 nnrpd/track.c                         Track client behavior
+nnrpd/zlib.c                          Compression support for nnrpd
 perl                                  Perl libraries (Directory)
 perl/INN                              INN Perl modules (Directory)
 perl/INN/Config.pm.in                 INN::Config module

Modified: Makefile.global.in
===================================================================
--- Makefile.global.in	2015-12-06 18:31:27 UTC (rev 9963)
+++ Makefile.global.in	2015-12-06 21:31:42 UTC (rev 9964)
@@ -126,13 +126,20 @@
 LIBCC		= $(LIBTOOLCC) $(CC)
 LIBLD		= $(LIBTOOLLD) $(CC)
 
+##  zlib support.  Additional flags and libraries used when compiling or
+##  linking code that contains compression support.
+
+ZLIB_CPPFLAGS	= @ZLIB_CPPFLAGS@
+ZLIB_LDFLAGS	= @ZLIB_LDFLAGS@
+ZLIB_LIBS	= @ZLIB_LIBS@
+
 ##  Berkeley DB support.  If this support is configured, anything linking
 ##  against libstorage also needs to link against BDB_LDFLAGS and BDB_LIBS.
+ 
+BDB_CPPFLAGS   = @BDB_CPPFLAGS@ $(ZLIB_CPPFLAGS)
+BDB_LDFLAGS    = @BDB_LDFLAGS@ $(ZLIB_LDFLAGS)
+BDB_LIBS       = @BDB_LIBS@ $(ZLIB_LIBS)
 
-BDB_CPPFLAGS	= @BDB_CPPFLAGS@ @ZLIB_CPPFLAGS@
-BDB_LDFLAGS	= @BDB_LDFLAGS@ @ZLIB_LDFLAGS@
-BDB_LIBS	= @BDB_LIBS@ @ZLIB_LIBS@
-
 ##  INN libraries.  Nearly all INN programs are linked with libinn, and any
 ##  INN program that reads from or writes to article storage or overview is
 ##  linked against libstorage.	STORAGE_LIBS is for external libraries

Modified: configure.ac
===================================================================
--- configure.ac	2015-12-06 18:31:27 UTC (rev 9963)
+++ configure.ac	2015-12-06 21:31:42 UTC (rev 9964)
@@ -324,11 +324,12 @@
 fi
 
 dnl Handle optional libraries and probing for their locations and component
-dnl libraries if needed.  Support for zlib is handled later.
+dnl libraries if needed.
 INN_LIB_BDB_OPTIONAL
 INN_LIB_KRB5_OPTIONAL
 INN_LIB_OPENSSL_OPTIONAL
 INN_LIB_SASL_OPTIONAL
+INN_LIB_ZLIB_OPTIONAL
 
 dnl If Kerberos is found, define KRB5_AUTH to auth_krb5 so as to build
 dnl that program.  In case neither et/com_err.h nor kerberosv5/com_err.h
@@ -345,8 +346,8 @@
                         [AC_MSG_ERROR([cannot find usable com_err header])])])])])])
 AC_SUBST([KRB5_AUTH])
 
-dnl If Berkeley DB is found, check the presence of its header.
-dnl Also, do not build with zlib support unless Berkeley DB is enabled.
+dnl If Berkeley DB is found, check the presence of its header and whether the
+dnl Berkeley DB library has ndbm support.
 AS_IF([test x"$inn_use_BDB" = xtrue],
     [inn_BDB_incroot=
      inn_BDB_header_found=
@@ -364,8 +365,7 @@
                 [Define if you have the <db.h> header file.])],
             [inn_BDB_header_found=no])])
      AS_IF([test x"${inn_BDB_header_found}" = xyes],
-        [INN_LIB_BDB_NDBM
-         INN_LIB_ZLIB_OPTIONAL],
+        [INN_LIB_BDB_NDBM],
         [AS_IF([test x"$with_bdb" = x],
             [BDB_CPPFLAGS=
              BDB_LDFLAGS=

Modified: doc/pod/install.pod
===================================================================
--- doc/pod/install.pod	2015-12-06 18:31:27 UTC (rev 9963)
+++ doc/pod/install.pod	2015-12-06 21:31:42 UTC (rev 9964)
@@ -413,16 +413,22 @@
 
 =item B<--with-zlib>=PATH
 
-The ovdb storage method can optionally use compression.  If B<--with-bdb>
-is set, and configure finds a suitable S<Berkeley DB> version, configure
-will by default also try to find the zlib library.  INN will then be
-built with zlib support unless the B<--without-zlib> flag is explicitly
+Enables support for compression for news reading, which means a
+compression layer can be negotiated between your server and newsreaders
+supporting that NNTP extension.
+
+Also enables support for compression with the ovdb storage method.
+
+This option requires that zlib be installed on your system (including the
+header files, not just the runtime libraries).  If a path is given, it
+sets the installed directory of zlib.  In case non-standard paths to the
+zlib library are used, one or both of the options B<--with-zlib-include>
+and B<--with-zlib-lib> can be given to configure with a path.
+
+If the zlib library is found at configure time, INN will be built with
+compression support unless the B<--without-zlib> flag is explicitly
 passed to configure.
 
-In case non-standard paths to the zlib library are used, one or both
-of the options B<--with-zlib-include> and B<--with-zlib-lib> can be
-given to configure with a path.
-
 =item B<--with-openssl>=PATH
 
 Enables support for TLS/SSL for news reading, which means it will be possible

Modified: doc/pod/news.pod
===================================================================
--- doc/pod/news.pod	2015-12-06 18:31:27 UTC (rev 9963)
+++ doc/pod/news.pod	2015-12-06 21:31:42 UTC (rev 9964)
@@ -13,6 +13,14 @@
 
 =item *
 
+Julien Elie has implemented in B<nnrpd> the new COMPRESS command
+described in draft-murchison-nntp-compress that extends the NNTP protocol
+to allow a connection to be effectively and efficiently compressed.
+News clients that also support that extension will be able to benefit
+from that bandwidth optimization and improvement in speed.
+
+=item *
+
 When an encryption layer is negotiated during a successful use of the
 STARTTLS command, or after a successful authentication using a SASL
 mechanism which negotiates an encryption layer, B<nnrpd> now updates

Modified: include/inn/nntp.h
===================================================================
--- include/inn/nntp.h	2015-12-06 18:31:27 UTC (rev 9963)
+++ include/inn/nntp.h	2015-12-06 21:31:42 UTC (rev 9964)
@@ -55,6 +55,7 @@
     NNTP_OK_BANNER_POST         = 200,
     NNTP_OK_BANNER_NOPOST       = 201,
     NNTP_OK_QUIT                = 205,
+    NNTP_OK_COMPRESS            = 206,
     NNTP_OK_GROUP               = 211,
     NNTP_OK_LIST                = 215,
     NNTP_OK_ARTICLE             = 220,

Modified: innd/nc.c
===================================================================
--- innd/nc.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ innd/nc.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -88,6 +88,9 @@
        READER command. */
     COMMAND_READER("ARTICLE"),
     COMMAND_READER("BODY"),
+#if defined(HAVE_ZLIB)
+    COMMAND_READER("COMPRESS"),
+#endif /* HAVE_ZLIB */
     COMMAND_READER("DATE"),
     COMMAND_READER("GROUP"),
     COMMAND_READER("HDR"),

Modified: nnrpd/Makefile
===================================================================
--- nnrpd/Makefile	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/Makefile	2015-12-06 21:31:42 UTC (rev 9964)
@@ -3,13 +3,13 @@
 include ../Makefile.global
 
 top		= ..
-CFLAGS		= $(GCFLAGS) $(SSL_CPPFLAGS) $(SASL_CPPFLAGS)
+CFLAGS		= $(GCFLAGS) $(SSL_CPPFLAGS) $(SASL_CPPFLAGS) $(ZLIB_CPPFLAGS)
 
 ALL		= nnrpd
 
-SOURCES		= article.c auth-ext.c cache.c group.c commands.c line.c \
+SOURCES		= article.c auth-ext.c cache.c commands.c group.c line.c \
 		  list.c misc.c newnews.c nnrpd.c perl.c perm.c post.c \
-		  python.c sasl.c tls.c track.c
+		  python.c sasl.c tls.c track.c zlib.c
 
 INCLUDES	= cache.h nnrpd.h post.h tls.h
 
@@ -34,10 +34,11 @@
 
 NNRPDLIBS	= $(LIBSTORAGE) $(LIBHIST) $(LIBINN) $(STORAGE_LIBS) \
 		  $(PERL_LIBS) $(PYTHON_LIBS) $(SSL_LDFLAGS) $(SSL_LIBS) \
-		  $(CRYPTO_LIBS) $(SASL_LDFLAGS) $(SASL_LIBS) $(LIBS)
+		  $(CRYPTO_LIBS) $(SASL_LDFLAGS) $(SASL_LIBS) \
+		  $(ZLIB_LDFLAGS) $(ZLIB_LIBS) $(LIBS)
 
 .c.o:
-	$(CC) $(CFLAGS) $(SSL_CPPFLAGS) $(SASL_CPPFLAGS) -c $<
+	$(CC) $(CFLAGS) -c $<
 
 perl.o:		perl.c   ; $(CC) $(CFLAGS) $(PERL_CPPFLAGS) -c perl.c
 python.o:	python.c ; $(CC) $(CFLAGS) $(PYTHON_CPPFLAGS) -c python.c
@@ -104,19 +105,6 @@
   ../include/inn/tst.h ../include/inn/list.h ../include/inn/libinn.h \
   ../include/inn/xmalloc.h ../include/inn/xwrite.h \
   ../include/inn/storage.h ../include/inn/options.h cache.h
-group.o: group.c ../include/config.h ../include/inn/defines.h \
-  ../include/inn/system.h ../include/inn/macros.h \
-  ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \
-  ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \
-  ../include/config.h ../include/inn/macros.h \
-  ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \
-  ../include/portable/macros.h ../include/portable/socket.h \
-  ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
-  ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \
-  ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \
-  ../include/inn/storage.h ../include/inn/options.h \
-  ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \
-  ../include/inn/history.h ../include/inn/storage.h
 commands.o: commands.c ../include/config.h ../include/inn/defines.h \
   ../include/inn/system.h ../include/inn/macros.h \
   ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \
@@ -132,6 +120,19 @@
   ../include/inn/ov.h ../include/inn/history.h ../include/inn/storage.h \
   ../include/inn/innconf.h ../include/inn/messages.h \
   ../include/inn/version.h tls.h
+group.o: group.c ../include/config.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \
+  ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \
+  ../include/config.h ../include/inn/macros.h \
+  ../include/portable/stdbool.h ../include/inn/innconf.h nnrpd.h \
+  ../include/portable/macros.h ../include/portable/socket.h \
+  ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
+  ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \
+  ../include/inn/storage.h ../include/inn/options.h \
+  ../include/inn/vector.h ../include/inn/timer.h ../include/inn/ov.h \
+  ../include/inn/history.h ../include/inn/storage.h
 line.o: line.c ../include/config.h ../include/inn/defines.h \
   ../include/inn/system.h ../include/inn/macros.h \
   ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \
@@ -290,3 +291,15 @@
   ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \
   ../include/inn/storage.h ../include/inn/options.h \
   ../include/inn/vector.h ../include/inn/timer.h
+zlib.o: zlib.c ../include/config.h ../include/inn/defines.h \
+  ../include/inn/system.h ../include/inn/macros.h \
+  ../include/inn/portable-macros.h ../include/inn/portable-stdbool.h \
+  ../include/inn/defines.h ../include/inn/options.h ../include/clibrary.h \
+  ../include/config.h ../include/inn/macros.h \
+  ../include/portable/stdbool.h ../include/inn/messages.h nnrpd.h \
+  ../include/portable/macros.h ../include/portable/socket.h \
+  ../include/portable/getaddrinfo.h ../include/portable/getnameinfo.h \
+  ../include/inn/qio.h ../include/inn/libinn.h ../include/inn/xmalloc.h \
+  ../include/inn/xwrite.h ../include/inn/nntp.h ../include/inn/paths.h \
+  ../include/inn/storage.h ../include/inn/options.h \
+  ../include/inn/vector.h ../include/inn/timer.h

Modified: nnrpd/article.c
===================================================================
--- nnrpd/article.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/article.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -64,6 +64,23 @@
 
     TMRstart(TMR_NNTPWRITE);
 
+#if defined(HAVE_ZLIB)
+    if (compression_layer_on) {
+        int i;
+
+        for (i = 0; i < *countp; i++) {
+            if (i+1 == *countp) {
+                /* Time to flush the compressed output stream. */
+                zstream_flush_needed = true;
+            }
+            write_buffer(vec[i].iov_base, vec[i].iov_len);
+        }
+
+        *countp = 0;
+        return;
+    }
+#endif /* HAVE_ZLIB */
+
 #ifdef HAVE_SASL
     if (sasl_conn && sasl_ssf) {
 	int i;
@@ -196,6 +213,12 @@
 static void
 PushIOb(void)
 {
+#if defined(HAVE_ZLIB)
+    /* Last line of a multi-line data block response.
+     * Time to flush the compressed output stream. */
+    zstream_flush_needed = true;
+#endif /* HAVE_ZLIB */
+
     write_buffer(_IO_buffer_, highwater);
     highwater = 0;
 }

Modified: nnrpd/commands.c
===================================================================
--- nnrpd/commands.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/commands.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -264,6 +264,14 @@
     char        errorstr[BIG_BUFFER];
     int         code;
 
+#if defined(HAVE_ZLIB)
+    /* If a compression layer is active, AUTHINFO is not possible. */
+    if (compression_layer_on && !tls_compression_on) {
+        Reply("%d Already using a compression layer\r\n", NNTP_ERR_ACCESS);
+        return;
+    }
+#endif
+
     if (strcasecmp(av[1], "GENERIC") == 0) {
 	char *logrec = Glom(av);
 

Modified: nnrpd/line.c
===================================================================
--- nnrpd/line.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/line.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -27,6 +27,7 @@
 extern SSL *tls_conn;
 #endif
 
+
 /*
 **  Free a previously allocated line structure.
 */
@@ -86,6 +87,39 @@
     ssize_t n;
 
     do {
+#if defined(HAVE_ZLIB)
+        /* Process data that may already be available in the zlib buffer. */
+        if (compression_layer_on &&
+            (zstream_in->avail_in > 0 || zstream_inflate_needed)) {
+            int r;
+
+            zstream_in->next_out = p;
+            zstream_in->avail_out = len;
+
+            r = inflate(zstream_in, Z_SYNC_FLUSH);
+
+            if (!(r == Z_OK || r == Z_BUF_ERROR || r == Z_STREAM_END)) {
+                sysnotice("inflate() failed: %d; %s", r,
+                          zstream_in->msg != NULL ? zstream_in->msg :
+                          "no detail");
+                n = -1;
+                break;
+            }
+
+            /* Check whether inflate() has finished to process its input.
+             * If not, we need to call it again, even though avail_in is 0. */
+            zstream_inflate_needed = (r != Z_STREAM_END);
+
+            if (zstream_in->avail_out < len) {
+                /* Some data has been uncompressed.  Treat it now. */
+                n = len - zstream_in->avail_out;
+                break;
+            }
+            /* If we reach here, then it means that inflate() needs more
+             * input, so we go on reading data on the wire. */
+        }
+#endif /* HAVE_ZLIB */
+
 #ifdef HAVE_OPENSSL
 	if (tls_conn) {
 	    int err;
@@ -129,7 +163,7 @@
             const char *out;
             unsigned outlen;
             int r;
- 
+
             if ((r = sasl_decode(sasl_conn, p, n, &out, &outlen)) == SASL_OK) {
                 if (outlen > len) {
                     sysnotice("sasl_decode() returned too much output");
@@ -150,6 +184,48 @@
             }
         }
 #endif /* HAVE_SASL */
+
+#if defined(HAVE_ZLIB)
+        if (compression_layer_on && n > 0) {
+            size_t zconsumed;
+
+            if (zstream_in->avail_in > 0 && zstream_in->next_in != Z_NULL) {
+                zconsumed = zstream_in->next_in - zbuf_in;
+            } else {
+                zconsumed = 0;
+                zbuf_in_allocated = 0;
+            }
+
+            /* Transfer the data we have just read to zstream_in,
+             * and loop to actually process it. */
+            if ((ssize_t) (zbuf_in_size - zbuf_in_allocated) < n) {
+                size_t newsize = zbuf_in_size * 2 + n;
+
+                /* Don't grow the buffer bigger than the maximum
+                 * article size we'll accept. */
+                if (PERMaccessconf->localmaxartsize > NNTP_MAXLEN_COMMAND) {
+                    if (newsize > PERMaccessconf->localmaxartsize) {
+                        newsize = PERMaccessconf->localmaxartsize;
+                    }
+                }
+                if (newsize == zbuf_in_size) {
+                    warn("%s overflowed our zstream_in buffer (%lu)",
+                         Client.host, newsize);
+                    n = -1;
+                    break;
+                }
+                zbuf_in = xrealloc(zbuf_in, newsize);
+                zbuf_in_size = newsize;
+            }
+            memcpy(zbuf_in + zbuf_in_allocated, p, n);
+            zstream_in->next_in = zbuf_in + zconsumed;
+            zstream_in->avail_in += n;
+            zbuf_in_allocated += n;
+            zstream_inflate_needed = true;
+            /* Loop to actually inflate the compressed data we received. */
+            n = 0;
+        }
+#endif /* HAVE_ZLIB */
     } while (n == 0); /* Split SASL blob, need to read more data. */
 
     return n;

Modified: nnrpd/misc.c
===================================================================
--- nnrpd/misc.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/misc.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -449,8 +449,47 @@
      return 1;
 }
 
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_ZLIB)
 /*
+**  The COMPRESS command.
+*/
+void
+CMDcompress(int ac, char *av[])
+{
+    bool result;
+
+    /* Check the argument. */
+    if (ac > 1) {
+        if (strcasecmp(av[1], "DEFLATE") != 0) {
+            Reply("%d Only the DEFLATE compression algorithm is supported\r\n",
+                  NNTP_ERR_UNAVAILABLE);
+            return;
+        }
+    }
+
+    if (compression_layer_on) {
+        Reply("%d Already using a compression layer\r\n", NNTP_ERR_ACCESS);
+        return;
+    }
+
+    result = zlib_init();
+
+    if (!result) {
+        Reply("%d Impossible to activate compression\r\n", NNTP_FAIL_ACTION);
+        return;
+    }
+
+    Reply("%d Compression now active; enjoy the speed!\r\n", NNTP_OK_COMPRESS);
+
+    /* Flush any pending output, before enabling compression. */
+    fflush(stdout);
+
+    compression_layer_on = true;
+}
+#endif /* HAVE_ZLIB */
+
+#if defined(HAVE_OPENSSL)
+/*
 **  The STARTTLS command.  RFC 4642.
 */
 void
@@ -464,6 +503,14 @@
         return;
     }
 
+# if defined(HAVE_ZLIB)
+    /* If a compression layer is active, STARTTLS is not possible. */
+    if (compression_layer_on) {
+        Reply("%d Already using a compression layer\r\n", NNTP_ERR_ACCESS);
+        return;
+    }
+# endif /* HAVE_ZLIB */
+
     /* If the client is already authenticated, STARTTLS is not possible. */
     if (PERMauthorized && !PERMneedauth && !PERMcanauthenticate) {
         Reply("%d Already authenticated without the use of a security layer\r\n",
@@ -534,6 +581,14 @@
     }
 # endif /* HAVE_SASL */
 
+# if defined(HAVE_ZLIB) && OPENSSL_VERSION_NUMBER >= 0x00090800fL
+    /* Check whether a compression layer has just been added.
+     * SSL_get_current_compression() is defined in OpenSSL versions >= 0.9.8
+     * final release. */
+    tls_compression_on = (SSL_get_current_compression(tls_conn) != NULL);
+    compression_layer_on = tls_compression_on;
+# endif /* HAVE_ZLIB && OPENSSL >= v0.9.8 */
+
     /* Reset our read buffer so as to prevent plaintext command injection. */
     line_reset(&NNTPline);
 }

Modified: nnrpd/nnrpd.c
===================================================================
--- nnrpd/nnrpd.c	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/nnrpd.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -129,6 +129,10 @@
 	CMDfetchhelp },
     {   "CAPABILITIES", CMDcapabilities,false,  1,      2,      true,
         "[keyword]" },
+#if defined(HAVE_ZLIB)
+    {   "COMPRESS",     CMDcompress,    false,  2,      2,      true,
+        "DEFLATE" },
+#endif
     {	"DATE",		CMDdate,	false,	1,	1,      true,
 	NULL },
     {	"GROUP",	CMDgroup,	true,	2,	2,      true,
@@ -257,6 +261,17 @@
     sasl_done();
 #endif /* HAVE_SASL */
 
+#if defined(HAVE_ZLIB)
+    if (compression_layer_on) {
+        inflateEnd(zstream_in);
+        free(zstream_in);
+        free(zbuf_in);
+        deflateEnd(zstream_out);
+        free(zstream_out);
+        free(zbuf_out);
+    }
+#endif /* HAVE_ZLIB */
+
      if (DaemonMode) {
      	shutdown(STDIN_FILENO, 2);
      	shutdown(STDOUT_FILENO, 2);
@@ -372,9 +387,15 @@
     if ((!PERMauthorized || PERMneedauth || PERMcanauthenticate)) {
         Printf("AUTHINFO");
 
-        /* No arguments if the server does not permit any authentication commands
-         * in its current state. */
-        if (PERMcanauthenticate) {
+        /* No arguments if the server does not permit any authentication
+         * commands in its current state (either a compression layer other
+         * than the one negotiated along with TLS is active, or the user
+         * has no way to authenticate successfully). */
+        if (
+#if defined(HAVE_ZLIB)
+            (!compression_layer_on || tls_compression_on) &&
+#endif /* HAVE_ZLIB */
+            PERMcanauthenticate) {
 #ifdef HAVE_OPENSSL
             if (PERMcanauthenticatewithoutSSL || encryption_layer_on) {
 #endif
@@ -415,6 +436,13 @@
         Printf("\r\n");
     }
 
+#if defined(HAVE_ZLIB)
+    /* A compression layer is not active. */
+    if (!compression_layer_on) {
+        Printf("COMPRESS DEFLATE\r\n");
+    }
+#endif /* HAVE_ZLIB */
+
     if (PERMcanread) {
         Printf("HDR\r\n");
     }
@@ -450,13 +478,17 @@
     }
 #endif /* HAVE_SASL */
 
-#ifdef HAVE_OPENSSL
-    /* A TLS layer is not active and the client is not already authenticated. */
+#if defined(HAVE_OPENSSL)
+    /* A TLS layer is not active, a compression layer is not active,
+     * and the client is not already authenticated. */
     if (!encryption_layer_on
+# if defined(HAVE_ZLIB)
+        && !compression_layer_on
+# endif /* HAVE_ZLIB */
         && (!PERMauthorized || PERMneedauth || PERMcanauthenticate)) {
         Printf("STARTTLS\r\n");
     }
-#endif
+#endif /* HAVE_OPENSSL */
 
     Printf(".\r\n");
 }
@@ -629,7 +661,8 @@
 
 
 /*
-**  Write a buffer, via SASL security layer and/or TLS if necessary.
+**  Write a buffer, via compression layer and/or SASL security layer
+**  and/or TLS if necessary.
 */
 void
 write_buffer(const char *buff, ssize_t len)
@@ -638,8 +671,44 @@
     ssize_t n;
 
     TMRstart(TMR_NNTPWRITE);
+    p = buff;
 
-    p = buff;
+#if defined(HAVE_ZLIB)
+    if (compression_layer_on) {
+        int r;
+
+        zstream_out->next_in = (unsigned char *) p;
+        zstream_out->avail_in = len;
+        zstream_out->next_out = zbuf_out;
+        zstream_out->avail_out = zbuf_out_size;
+
+        do {
+            /* Grow the output buffer if needed. */
+            if (zstream_out->avail_out == 0) {
+                size_t newsize = zbuf_out_size * 2;
+                zbuf_out = xrealloc(zbuf_out, newsize);
+                zstream_out->next_out = zbuf_out + zbuf_out_size;
+                zstream_out->avail_out = zbuf_out_size;
+                zbuf_out_size = newsize;
+            }
+
+            r = deflate(zstream_out,
+                        zstream_flush_needed ? Z_PARTIAL_FLUSH : Z_NO_FLUSH);
+
+            if (!(r == Z_OK || r == Z_BUF_ERROR || r == Z_STREAM_END)) {
+                sysnotice("deflate() failed: %d; %s", r,
+                          zstream_out->msg != NULL ? zstream_out->msg :
+                          "no detail");
+                return;
+            }
+        } while (r == Z_OK && zstream_out->avail_out == 0);
+
+        p = (char *) zbuf_out;
+        len = zbuf_out_size - zstream_out->avail_out;
+        zstream_flush_needed = false;
+    }
+#endif /* HAVE_ZLIB */
+
     while (len > 0) {
 	const char *out;
 	unsigned outlen;
@@ -663,7 +732,7 @@
 	{
 	    /* Output the entire unencoded string. */
 	    n = len;
-	    out = buff;
+	    out = p;
 	    outlen = len;
 	}
 
@@ -739,6 +808,11 @@
 {
     va_list args;
 
+#if defined(HAVE_ZLIB)
+    /* For single-line responses, immediately flush the output stream. */
+    zstream_flush_needed = true;
+#endif /* HAVE_ZLIB */
+
     va_start(args, fmt);
     VPrintf(fmt, args, 1);
     va_end(args);
@@ -749,6 +823,16 @@
 {
     va_list args;
 
+#if defined(HAVE_ZLIB)
+    /* Last line of a multi-line data block response.
+     * Time to flush the compressed output stream.
+     * Check that only when the compression layer is active. */
+    if (compression_layer_on &&
+        strlen(fmt) == 3 && strcasecmp(fmt, ".\r\n") == 0) {
+        zstream_flush_needed = true;
+    }
+#endif /* HAVE_ZLIB */
+
     va_start(args, fmt);
     VPrintf(fmt, args, 0);
     va_end(args);
@@ -1212,6 +1296,14 @@
             ExitWithStats(1, false);
         }
         encryption_layer_on = true;
+
+# if defined(HAVE_ZLIB) && OPENSSL_VERSION_NUMBER >= 0x00090800fL
+        /* Check whether a compression layer has just been added.
+         * SSL_get_current_compression() is defined in OpenSSL versions >= 0.9.8
+         * final release. */
+        tls_compression_on = (SSL_get_current_compression(tls_conn) != NULL);
+        compression_layer_on = tls_compression_on;
+# endif /* HAVE_ZLIB && OPENSSL >= v0.9.8 */
     }
 #endif /* HAVE_OPENSSL */
 

Modified: nnrpd/nnrpd.h
===================================================================
--- nnrpd/nnrpd.h	2015-12-06 18:31:27 UTC (rev 9963)
+++ nnrpd/nnrpd.h	2015-12-06 21:31:42 UTC (rev 9964)
@@ -34,6 +34,13 @@
 #include <sasl/saslutil.h>
 #endif
 
+#if !defined(HAVE_ZLIB_H)
+# undef HAVE_ZLIB
+#endif
+#if defined(HAVE_ZLIB)
+# include <zlib.h>
+#endif
+
 /*
 **  A range of article numbers.
 */
@@ -246,6 +253,9 @@
 
 extern void             CMDauthinfo     (int ac, char** av);
 extern void             CMDcapabilities (int ac, char** av);
+#if defined(HAVE_ZLIB)
+extern void             CMDcompress     (int ac, char** av);
+#endif
 extern void             CMDdate         (int ac, char** av);
 extern void             CMDfetch        (int ac, char** av);
 extern void             CMDgroup        (int ac, char** av);
@@ -314,3 +324,21 @@
 void SASLauth(int ac, char *av[]);
 void SASLnewserver(void);
 #endif /* HAVE_SASL */
+
+#if defined(HAVE_ZLIB)
+extern bool compression_layer_on;
+extern bool tls_compression_on;
+/* (De)compress streams and related variables. */
+extern z_stream *zstream_in;
+extern z_stream *zstream_out;
+extern bool zstream_inflate_needed;
+extern bool zstream_flush_needed;
+/* (De)compress buffers and related variables. */
+extern unsigned char *zbuf_in;
+extern size_t zbuf_in_allocated;
+extern size_t zbuf_in_size;
+extern unsigned char *zbuf_out;
+extern size_t zbuf_out_size;
+
+bool zlib_init(void);
+#endif /* HAVE_ZLIB */

Added: nnrpd/zlib.c
===================================================================
--- nnrpd/zlib.c	                        (rev 0)
+++ nnrpd/zlib.c	2015-12-06 21:31:42 UTC (rev 9964)
@@ -0,0 +1,100 @@
+/*  $Id$
+**
+**  COMPRESS functionality.
+*/
+
+#include "config.h"
+#include "clibrary.h"
+
+#include "inn/messages.h"
+#include "nnrpd.h"
+
+#if defined(HAVE_ZLIB)
+# define ZBUFSIZE 65536
+# define MEM_LEVEL 9
+# define WINDOW_BITS (-15)      /* Raw deflate. */
+bool compression_layer_on = false;
+bool tls_compression_on = false;
+z_stream *zstream_in = NULL;
+z_stream *zstream_out = NULL;
+bool zstream_inflate_needed = false;
+bool zstream_flush_needed = false;
+unsigned char *zbuf_in = NULL;
+unsigned char *zbuf_out = NULL;
+size_t zbuf_in_size = NNTP_MAXLEN_COMMAND;  /* Initial size of the input
+                                             * buffer.  Can be reallocated. */
+size_t zbuf_in_allocated = 0;
+size_t zbuf_out_size = ZBUFSIZE;  /* Initial size of the output buffer.
+                                   * Can be reallocated, when needed. */
+
+/*
+**  Wrappers for our memory management functions.
+*/
+static voidpf zalloc(voidpf opaque UNUSED, uInt items, uInt size)
+{
+    return (voidpf) xcalloc(items, size);
+}
+
+static void zfree(voidpf opaque UNUSED, voidpf address)
+{
+    free(address);
+}
+
+
+/*
+**  The function called by nnrpd to initialize compression support.  Calls
+**  both deflateInit2 and inflateInit2, and then checks the result.
+**
+**  Returns false on error.
+*/
+bool
+zlib_init(void)
+{
+    int result;
+    zstream_in = (z_stream *) xmalloc(sizeof(z_stream));
+    zstream_out = (z_stream *) xmalloc(sizeof(z_stream));
+
+    /* Allocate the buffer for compressed input data given to inflate(). */
+    zbuf_in = (unsigned char *) xmalloc(zbuf_in_size);
+
+    /* Allocate the buffer for compressed output data produced by deflate(). */
+    zbuf_out = (unsigned char *) xmalloc(zbuf_out_size);
+
+    zstream_in->zalloc = zalloc;
+    zstream_in->zfree = zfree;
+    zstream_in->opaque = Z_NULL;
+    zstream_in->next_in = Z_NULL;
+    zstream_in->avail_in = 0;
+
+    zstream_out->zalloc = zalloc;
+    zstream_out->zfree = zfree;
+    zstream_out->opaque = Z_NULL;
+
+    result = inflateInit2(zstream_in, WINDOW_BITS);
+
+    if (result != Z_OK) {
+        syslog(L_NOTICE, "inflateInit2() failed with error %d", result);
+        free(zstream_in);
+        free(zstream_out);
+        free(zbuf_in);
+        free(zbuf_out);
+        return false;
+    }
+
+    result = deflateInit2(zstream_out, Z_BEST_COMPRESSION, Z_DEFLATED,
+                          WINDOW_BITS, MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+    if (result != Z_OK) {
+        syslog(L_NOTICE, "deflateInit2() failed with error %d", result);
+        inflateEnd(zstream_in);
+        free(zstream_in);
+        free(zstream_out);
+        free(zbuf_in);
+        free(zbuf_out);
+        return false;
+    }
+
+    return true;
+}
+
+#endif /* HAVE_ZLIB */


Property changes on: trunk/nnrpd/zlib.c
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Author Date Id Revision
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property


More information about the inn-committers mailing list