monotone

monotone Mtn Source Tree

Root/src/gzip.cc

1/*************************************************
2* Gzip Compressor Source File *
3* (C) 2001 Peter J Jones (pjones@pmade.org) *
4* 2001-2004 Jack Lloyd *
5* *
6* Based on the comp_zlib module, modified *
7* by Matt Johnston. This is not a complete *
8* gzip implementation (it only handles basic *
9* headers). *
10*************************************************/
11
12/* This could be implemented a lot more cleanly if we rely on zlib >= 1.2
13 * being available. the Gzip Compressor would just be a subclass of
14 * Zlib Compressor, with window_bits+=16 for deflateInit2(), etc */
15
16#include "base.hh"
17#include "gzip.hh"
18
19#include <botan/botan.h>
20
21#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
22#include <botan/loadstor.h>
23#include <botan/filters.h>
24#endif
25
26#include <cstring>
27#include <map>
28#include <zlib.h>
29
30namespace Botan {
31
32namespace {
33
34/*************************************************
35* Allocation Information for Zlib *
36*************************************************/
37class Zlib_Alloc_Info
38 {
39 public:
40 std::map<void*, u32bit> current_allocs;
41 Allocator* alloc;
42
43 Zlib_Alloc_Info() { alloc = Allocator::get(false); }
44 };
45
46/*************************************************
47* Allocation Function for Zlib *
48*************************************************/
49void* zlib_malloc(void* info_ptr, unsigned int n, unsigned int size)
50 {
51 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(info_ptr);
52 void* ptr = info->alloc->allocate(n * size);
53 info->current_allocs[ptr] = n * size;
54 return ptr;
55 }
56
57/*************************************************
58* Allocation Function for Zlib *
59*************************************************/
60void zlib_free(void* info_ptr, void* ptr)
61 {
62 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(info_ptr);
63 std::map<void*, u32bit>::const_iterator i = info->current_allocs.find(ptr);
64 if(i == info->current_allocs.end())
65 throw Invalid_Argument("zlib_free: Got pointer not allocated by us");
66 info->alloc->deallocate(ptr, i->second);
67 }
68}
69
70/*************************************************
71* Wrapper Type for Zlib z_stream *
72*************************************************/
73class Zlib_Stream
74 {
75 public:
76 z_stream stream;
77
78 Zlib_Stream()
79 {
80 std::memset(&stream, 0, sizeof(z_stream));
81 stream.zalloc = zlib_malloc;
82 stream.zfree = zlib_free;
83 stream.opaque = new Zlib_Alloc_Info;
84 }
85 ~Zlib_Stream()
86 {
87 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(stream.opaque);
88 delete info;
89 std::memset(&stream, 0, sizeof(z_stream));
90 }
91 };
92
93/*************************************************
94* Gzip_Compression Constructor *
95*************************************************/
96Gzip_Compression::Gzip_Compression(u32bit l) :
97 level((l >= 9) ? 9 : l), buffer(DEFAULT_BUFFERSIZE),
98 pipe(new Hash_Filter("CRC32")), count( 0 )
99 {
100
101 zlib = new Zlib_Stream;
102 // window_bits == -15 relies on an undocumented feature of zlib, which
103 // supresses the zlib header on the message. We need that since gzip doesn't
104 // use this header. The feature been confirmed to exist in 1.1.4, which
105 // everyone should be using due to security fixes. In later versions this
106 // feature is documented, along with the ability to do proper gzip output
107 // (that would be a nicer way to do things, but will have to wait until 1.2
108 // becomes more widespread).
109 // The other settings are the defaults that deflateInit() gives
110 if(deflateInit2(&(zlib->stream), level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
111 {
112 delete zlib; zlib = 0;
113 throw Memory_Exhaustion();
114 }
115 }
116
117/*************************************************
118* Gzip_Compression Destructor *
119*************************************************/
120Gzip_Compression::~Gzip_Compression()
121 {
122 deflateEnd(&(zlib->stream));
123 delete zlib; zlib = 0;
124 }
125
126/*************************************************
127* Start Compressing with Gzip *
128*************************************************/
129void Gzip_Compression::start_msg()
130 {
131 clear();
132 put_header();
133 pipe.start_msg();
134 count = 0;
135 }
136
137/*************************************************
138* Compress Input with Gzip *
139*************************************************/
140void Gzip_Compression::write(const byte input[], filter_length_t length)
141 {
142
143 count += length;
144 pipe.write(input, length);
145
146 zlib->stream.next_in = (Bytef*)input;
147 zlib->stream.avail_in = length;
148
149 while(zlib->stream.avail_in != 0)
150 {
151 zlib->stream.next_out = (Bytef*)buffer.begin();
152 zlib->stream.avail_out = buffer.size();
153 int rc = deflate(&(zlib->stream), Z_NO_FLUSH);
154 if (rc != Z_OK && rc != Z_STREAM_END)
155 throw Invalid_State("Internal error in Gzip_Compression deflate.");
156 send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
157 }
158 }
159
160/*************************************************
161* Finish Compressing with Gzip *
162*************************************************/
163void Gzip_Compression::end_msg()
164 {
165 zlib->stream.next_in = 0;
166 zlib->stream.avail_in = 0;
167
168 int rc = Z_OK;
169 while(rc != Z_STREAM_END)
170 {
171 zlib->stream.next_out = (Bytef*)buffer.begin();
172 zlib->stream.avail_out = buffer.size();
173 rc = deflate(&(zlib->stream), Z_FINISH);
174 if (rc != Z_OK && rc != Z_STREAM_END)
175 throw Invalid_State("Internal error in Gzip_Compression finishing deflate.");
176 send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
177 }
178
179 pipe.end_msg();
180 put_footer();
181 clear();
182 }
183
184/*************************************************
185* Clean up Compression Context *
186*************************************************/
187void Gzip_Compression::clear()
188 {
189 deflateReset(&(zlib->stream));
190 }
191
192/*************************************************
193* Put a basic gzip header at the beginning *
194*************************************************/
195void Gzip_Compression::put_header()
196 {
197 send(GZIP::GZIP_HEADER, sizeof(GZIP::GZIP_HEADER));
198 }
199
200/*************************************************
201* Put a gzip footer at the end *
202*************************************************/
203void Gzip_Compression::put_footer()
204 {
205 // 4 byte CRC32, and 4 byte length field
206 SecureVector<byte> buf(4);
207 SecureVector<byte> tmpbuf(4);
208
209 pipe.read(tmpbuf.begin(), tmpbuf.size(), Pipe::LAST_MESSAGE);
210
211 // CRC32 is the reverse order to what gzip expects.
212 for (int i = 0; i < 4; i++)
213 buf[3-i] = tmpbuf[i];
214
215 send(buf.begin(), buf.size());
216
217 // Length - LSB first
218 for (int i = 0; i < 4; i++)
219 buf[3-i] = get_byte(i, count);
220
221 send(buf.begin(), buf.size());
222 }
223
224/*************************************************
225* Gzip_Decompression Constructor *
226*************************************************/
227Gzip_Decompression::Gzip_Decompression() : buffer(DEFAULT_BUFFERSIZE),
228 no_writes(true), pipe(new Hash_Filter("CRC32")), footer(0)
229 {
230 if (DEFAULT_BUFFERSIZE < sizeof(GZIP::GZIP_HEADER))
231 throw Decoding_Error("DEFAULT_BUFFERSIZE is too small");
232
233 zlib = new Zlib_Stream;
234
235 // window_bits == -15 is raw zlib (no header) - see comment
236 // above about deflateInit2
237 if(inflateInit2(&(zlib->stream), -15) != Z_OK)
238 {
239 delete zlib; zlib = 0;
240 throw Memory_Exhaustion();
241 }
242 }
243
244/*************************************************
245* Gzip_Decompression Destructor *
246*************************************************/
247Gzip_Decompression::~Gzip_Decompression()
248 {
249 inflateEnd(&(zlib->stream));
250 delete zlib; zlib = 0;
251 }
252
253/*************************************************
254* Start Decompressing with Gzip *
255*************************************************/
256void Gzip_Decompression::start_msg()
257 {
258 if (!no_writes)
259 throw Decoding_Error("Gzip_Decompression: start_msg after already writing");
260
261 pipe.start_msg();
262 datacount = 0;
263 pos = 0;
264 in_footer = false;
265 }
266
267/*************************************************
268* Decompress Input with Gzip *
269*************************************************/
270void Gzip_Decompression::write(const byte input[], filter_length_t length)
271 {
272 if(length) no_writes = false;
273
274 // If we're in the footer, take what we need, then go to the next block
275 if (in_footer)
276 {
277 u32bit eat_len = eat_footer(input, length);
278 input += eat_len;
279 length -= eat_len;
280 }
281
282 if (length == 0)
283 return;
284
285 // Check the gzip header
286 if (pos < sizeof(GZIP::GZIP_HEADER))
287 {
288 filter_length_t len = std::min((filter_length_t)sizeof(GZIP::GZIP_HEADER)-pos, length);
289 filter_length_t cmplen = len;
290 // The last byte is the OS flag - we don't care about that
291 if (pos + len - 1 >= GZIP::HEADER_POS_OS)
292 cmplen--;
293
294 if (std::memcmp(input, &GZIP::GZIP_HEADER[pos], cmplen) != 0)
295 {
296 throw Decoding_Error("Gzip_Decompression: Data integrity error in header");
297 }
298 input += len;
299 length -= len;
300 pos += len;
301 }
302
303 pos += length;
304
305 zlib->stream.next_in = (Bytef*)input;
306 zlib->stream.avail_in = length;
307
308 while(zlib->stream.avail_in != 0)
309 {
310 zlib->stream.next_out = (Bytef*)buffer.begin();
311 zlib->stream.avail_out = buffer.size();
312
313 int rc = inflate(&(zlib->stream), Z_SYNC_FLUSH);
314 if(rc != Z_OK && rc != Z_STREAM_END)
315 {
316 if(rc == Z_DATA_ERROR)
317 throw Decoding_Error("Gzip_Decompression: Data integrity error");
318 if(rc == Z_NEED_DICT)
319 throw Decoding_Error("Gzip_Decompression: Need preset dictionary");
320 if(rc == Z_MEM_ERROR)
321 throw Memory_Exhaustion();
322 throw Decoding_Error("Gzip_Decompression: Unknown decompress error");
323 }
324 send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
325 pipe.write(buffer.begin(), buffer.size() - zlib->stream.avail_out);
326 datacount += buffer.size() - zlib->stream.avail_out;
327
328 // Reached the end - we now need to check the footer
329 if(rc == Z_STREAM_END)
330 {
331 u32bit read_from_block = length - zlib->stream.avail_in;
332 u32bit eat_len = eat_footer((Bytef*)input + read_from_block, zlib->stream.avail_in);
333 read_from_block += eat_len;
334 input += read_from_block;
335 length -= read_from_block;
336 zlib->stream.next_in = (Bytef*)input;
337 zlib->stream.avail_in = length;
338 }
339 }
340 }
341
342/*************************************************
343* Store the footer bytes *
344*************************************************/
345u32bit Gzip_Decompression::eat_footer(const byte input[], u32bit length)
346 {
347 if (footer.size() >= GZIP::FOOTER_LENGTH)
348 throw Decoding_Error("Gzip_Decompression: Data integrity error in footer");
349
350#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
351 size_t eat_len = std::min(GZIP::FOOTER_LENGTH-footer.size(),
352 static_cast<size_t>(length));
353 footer += std::make_pair(input, eat_len);
354#else
355 u32bit eat_len = std::min(GZIP::FOOTER_LENGTH-footer.size(), length);
356 footer.append(input, eat_len);
357#endif
358
359 if (footer.size() == GZIP::FOOTER_LENGTH)
360 {
361 check_footer();
362 clear();
363 }
364
365 return eat_len;
366 }
367
368/*************************************************
369* Check the gzip footer *
370*************************************************/
371void Gzip_Decompression::check_footer()
372 {
373 if (footer.size() != GZIP::FOOTER_LENGTH)
374 throw Decoding_Error("Gzip_Decompression: Error finalizing decompression");
375
376 pipe.end_msg();
377
378 // 4 byte CRC32, and 4 byte length field
379 SecureVector<byte> buf(4);
380 SecureVector<byte> tmpbuf(4);
381 pipe.read(tmpbuf.begin(), tmpbuf.size(), Pipe::LAST_MESSAGE);
382
383 // CRC32 is the reverse order to what gzip expects.
384 for (int i = 0; i < 4; i++)
385 buf[3-i] = tmpbuf[i];
386
387#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
388 tmpbuf.resize(4);
389 tmpbuf.copy(footer.begin(), 4);
390#else
391 tmpbuf.set(footer.begin(), 4);
392#endif
393 if (buf != tmpbuf)
394 throw Decoding_Error("Gzip_Decompression: Data integrity error - CRC32 error");
395
396 // Check the length matches - it is encoded LSB-first
397 for (int i = 0; i < 4; i++)
398 {
399 if (footer.begin()[GZIP::FOOTER_LENGTH-1-i] != get_byte(i, datacount))
400 throw Decoding_Error("Gzip_Decompression: Data integrity error - incorrect length");
401 }
402
403 }
404
405/*************************************************
406* Finish Decompressing with Gzip *
407*************************************************/
408void Gzip_Decompression::end_msg()
409 {
410
411 // All messages should end with a footer, and when a footer is successfully
412 // read, clear() will reset no_writes
413 if(no_writes) return;
414
415 throw Decoding_Error("Gzip_Decompression: didn't find footer");
416
417 }
418
419/*************************************************
420* Clean up Decompression Context *
421*************************************************/
422void Gzip_Decompression::clear()
423 {
424 no_writes = true;
425 inflateReset(&(zlib->stream));
426
427#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
428 footer.clear();
429#else
430 footer.destroy();
431#endif
432 pos = 0;
433 datacount = 0;
434 }
435
436}
437
438// Local Variables:
439// mode: C++
440// fill-column: 76
441// c-file-style: "gnu"
442// indent-tabs-mode: nil
443// End:
444// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status