conn-zip.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2007 Alexander Barton (alex@barton.de)
  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; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * Please read the file COPYING, README and AUTHORS for more information.
  10. */
  11. #include "portab.h"
  12. /**
  13. * @file
  14. * Connection compression using ZLIB
  15. */
  16. #define CONN_MODULE
  17. #ifdef ZLIB
  18. /* enable more zlib related debug messages: */
  19. /* #define DEBUG_ZLIB */
  20. #include "imp.h"
  21. #include <assert.h>
  22. #include <string.h>
  23. #include <zlib.h>
  24. #include "conn.h"
  25. #include "conn-func.h"
  26. #include "log.h"
  27. #include "array.h"
  28. #include "exp.h"
  29. #include "conn-zip.h"
  30. GLOBAL bool
  31. Zip_InitConn( CONN_ID Idx )
  32. {
  33. /* initialize zlib compression on this link */
  34. assert( Idx > NONE );
  35. My_Connections[Idx].zip.in.avail_in = 0;
  36. My_Connections[Idx].zip.in.total_in = 0;
  37. My_Connections[Idx].zip.in.total_out = 0;
  38. My_Connections[Idx].zip.in.zalloc = NULL;
  39. My_Connections[Idx].zip.in.zfree = NULL;
  40. My_Connections[Idx].zip.in.data_type = Z_ASCII;
  41. if (inflateInit( &My_Connections[Idx].zip.in ) != Z_OK) {
  42. Log(LOG_ALERT, "Can't initialize compression on connection %d (zlib inflate)!", Idx);
  43. return false;
  44. }
  45. My_Connections[Idx].zip.out.total_in = 0;
  46. My_Connections[Idx].zip.out.total_in = 0;
  47. My_Connections[Idx].zip.out.zalloc = NULL;
  48. My_Connections[Idx].zip.out.zfree = NULL;
  49. My_Connections[Idx].zip.out.data_type = Z_ASCII;
  50. if (deflateInit( &My_Connections[Idx].zip.out, Z_DEFAULT_COMPRESSION ) != Z_OK) {
  51. Log(LOG_ALERT, "Can't initialize compression on connection %d (zlib deflate)!", Idx);
  52. return false;
  53. }
  54. My_Connections[Idx].zip.bytes_in = My_Connections[Idx].bytes_in;
  55. My_Connections[Idx].zip.bytes_out = My_Connections[Idx].bytes_out;
  56. Log(LOG_INFO, "Enabled link compression (zlib) on connection %d.", Idx);
  57. Conn_OPTION_ADD( &My_Connections[Idx], CONN_ZIP );
  58. return true;
  59. } /* Zip_InitConn */
  60. /**
  61. * Copy data to the compression buffer of a connection. We do collect
  62. * some data there until it's full so that we can achieve better
  63. * compression ratios.
  64. * If the (pre-)compression buffer is full, we try to flush it ("actually
  65. * compress some data") and to add the new (uncompressed) data afterwards.
  66. * This function closes the connection on error.
  67. * @param Idx Connection handle.
  68. * @param Data Pointer to the data.
  69. * @param Len Length of the data to add.
  70. * @return true on success, false otherwise.
  71. */
  72. GLOBAL bool
  73. Zip_Buffer( CONN_ID Idx, const char *Data, size_t Len )
  74. {
  75. size_t buflen;
  76. assert( Idx > NONE );
  77. assert( Data != NULL );
  78. assert( Len > 0 );
  79. buflen = array_bytes(&My_Connections[Idx].zip.wbuf);
  80. if (buflen + Len >= WRITEBUFFER_SLINK_LEN) {
  81. /* compression buffer is full, flush */
  82. if( ! Zip_Flush( Idx )) return false;
  83. }
  84. /* check again; if zip buf is still too large do not append data:
  85. * otherwise the zip wbuf would grow too large */
  86. buflen = array_bytes(&My_Connections[Idx].zip.wbuf);
  87. if (buflen + Len >= WRITEBUFFER_SLINK_LEN) {
  88. Log(LOG_ALERT, "Zip Write buffer space exhausted: %lu bytes", buflen + Len);
  89. Conn_Close(Idx, "Zip Write buffer space exhausted", NULL, false);
  90. return false;
  91. }
  92. return array_catb(&My_Connections[Idx].zip.wbuf, Data, Len);
  93. } /* Zip_Buffer */
  94. /**
  95. * Compress data in ZIP buffer and move result to the write buffer of
  96. * the connection.
  97. * This function closes the connection on error.
  98. * @param Idx Connection handle.
  99. * @return true on success, false otherwise.
  100. */
  101. GLOBAL bool
  102. Zip_Flush( CONN_ID Idx )
  103. {
  104. int result;
  105. unsigned char zipbuf[WRITEBUFFER_SLINK_LEN];
  106. int zipbuf_used = 0;
  107. z_stream *out;
  108. out = &My_Connections[Idx].zip.out;
  109. out->avail_in = (uInt)array_bytes(&My_Connections[Idx].zip.wbuf);
  110. if (!out->avail_in)
  111. return true; /* nothing to do. */
  112. out->next_in = array_start(&My_Connections[Idx].zip.wbuf);
  113. assert(out->next_in != NULL);
  114. out->next_out = zipbuf;
  115. out->avail_out = (uInt)sizeof zipbuf;
  116. #ifdef DEBUG_ZIP
  117. Log(LOG_DEBUG, "out->avail_in %d, out->avail_out %d",
  118. out->avail_in, out->avail_out);
  119. #endif
  120. result = deflate( out, Z_SYNC_FLUSH );
  121. if(( result != Z_OK ) || ( out->avail_in > 0 ))
  122. {
  123. Log( LOG_ALERT, "Compression error: code %d!?", result );
  124. Conn_Close( Idx, "Compression error!", NULL, false );
  125. return false;
  126. }
  127. if (out->avail_out <= 0) {
  128. /* Not all data was compressed, because data became
  129. * bigger while compressing it. */
  130. Log(LOG_ALERT, "Compression error: buffer overflow!?");
  131. Conn_Close(Idx, "Compression error!", NULL, false);
  132. return false;
  133. }
  134. assert(out->avail_out <= WRITEBUFFER_SLINK_LEN);
  135. zipbuf_used = WRITEBUFFER_SLINK_LEN - out->avail_out;
  136. #ifdef DEBUG_ZIP
  137. Log(LOG_DEBUG, "zipbuf_used: %d", zipbuf_used);
  138. #endif
  139. if (!array_catb(&My_Connections[Idx].wbuf,
  140. (char *)zipbuf, (size_t) zipbuf_used)) {
  141. Log (LOG_ALERT, "Compression error: can't copy data!?");
  142. Conn_Close(Idx, "Compression error!", NULL, false);
  143. return false;
  144. }
  145. My_Connections[Idx].bytes_out += zipbuf_used;
  146. My_Connections[Idx].zip.bytes_out += array_bytes(&My_Connections[Idx].zip.wbuf);
  147. array_trunc(&My_Connections[Idx].zip.wbuf);
  148. return true;
  149. } /* Zip_Flush */
  150. /**
  151. * uncompress data and copy it to read buffer.
  152. * Returns true if data has been unpacked or no
  153. * compressed data is currently pending in the zread buffer.
  154. * This function closes the connection on error.
  155. * @param Idx Connection handle.
  156. * @return true on success, false otherwise.
  157. */
  158. GLOBAL bool
  159. Unzip_Buffer( CONN_ID Idx )
  160. {
  161. int result;
  162. unsigned char unzipbuf[READBUFFER_LEN];
  163. int unzipbuf_used = 0;
  164. unsigned int z_rdatalen;
  165. unsigned int in_len;
  166. z_stream *in;
  167. assert( Idx > NONE );
  168. z_rdatalen = (unsigned int)array_bytes(&My_Connections[Idx].zip.rbuf);
  169. if (z_rdatalen == 0)
  170. return true;
  171. in = &My_Connections[Idx].zip.in;
  172. in->next_in = array_start(&My_Connections[Idx].zip.rbuf);
  173. assert(in->next_in != NULL);
  174. in->avail_in = z_rdatalen;
  175. in->next_out = unzipbuf;
  176. in->avail_out = (uInt)sizeof unzipbuf;
  177. #ifdef DEBUG_ZIP
  178. Log(LOG_DEBUG, "in->avail_in %d, in->avail_out %d",
  179. in->avail_in, in->avail_out);
  180. #endif
  181. result = inflate( in, Z_SYNC_FLUSH );
  182. if( result != Z_OK )
  183. {
  184. Log(LOG_ALERT, "Decompression error: %s (code=%d, ni=%d, ai=%d, no=%d, ao=%d)!?", in->msg, result, in->next_in, in->avail_in, in->next_out, in->avail_out);
  185. Conn_Close(Idx, "Decompression error!", NULL, false);
  186. return false;
  187. }
  188. assert(z_rdatalen >= in->avail_in);
  189. in_len = z_rdatalen - in->avail_in;
  190. unzipbuf_used = READBUFFER_LEN - in->avail_out;
  191. #ifdef DEBUG_ZIP
  192. Log(LOG_DEBUG, "unzipbuf_used: %d - %d = %d", READBUFFER_LEN,
  193. in->avail_out, unzipbuf_used);
  194. #endif
  195. assert(unzipbuf_used <= READBUFFER_LEN);
  196. if (!array_catb(&My_Connections[Idx].rbuf, (char*) unzipbuf,
  197. (size_t)unzipbuf_used)) {
  198. Log (LOG_ALERT, "Decompression error: can't copy data!?");
  199. Conn_Close(Idx, "Decompression error!", NULL, false);
  200. return false;
  201. }
  202. if( in->avail_in > 0 ) {
  203. array_moveleft(&My_Connections[Idx].zip.rbuf, 1, in_len );
  204. } else {
  205. array_trunc( &My_Connections[Idx].zip.rbuf );
  206. My_Connections[Idx].zip.bytes_in += unzipbuf_used;
  207. }
  208. return true;
  209. } /* Unzip_Buffer */
  210. /**
  211. * @param Idx Connection handle.
  212. * @return amount of sent (compressed) bytes
  213. */
  214. GLOBAL long
  215. Zip_SendBytes( CONN_ID Idx )
  216. {
  217. assert( Idx > NONE );
  218. return My_Connections[Idx].zip.bytes_out;
  219. } /* Zip_SendBytes */
  220. /**
  221. * @param Idx Connection handle.
  222. * @return amount of received (compressed) bytes
  223. */
  224. GLOBAL long
  225. Zip_RecvBytes( CONN_ID Idx )
  226. {
  227. assert( Idx > NONE );
  228. return My_Connections[Idx].zip.bytes_in;
  229. } /* Zip_RecvBytes */
  230. #endif
  231. /* -eof- */