monotone

monotone Mtn Source Tree

Root/botan/gzip.cpp

1/*************************************************
2* Gzip Compressor Source File *
3* (C) 1999-2004 The Botan Project *
4* *
5* Based on the comp_zlib module, modified *
6* by Matt Johnston. This is not a complete *
7* gzip implementation (it only handles basic *
8* headers). *
9*************************************************/
10
11/* This could be implemented a lot more cleanly if we rely on zlib >= 1.2
12 * being available. the Gzip Compressor would just be a subclass of
13 * Zlib Compressor, with window_bits+=16 for deflateInit2(), etc */
14
15#include <botan/gzip.h>
16#include <botan/filters.h>
17#include <botan/bit_ops.h>
18#include <botan/init.h>
19#include <cstring>
20#include <map>
21#include <zlib.h>
22
23namespace Botan {
24
25namespace {
26
27/*************************************************
28* Allocation Information for Zlib *
29*************************************************/
30class Zlib_Alloc_Info
31 {
32 public:
33 std::map<void*, u32bit> current_allocs;
34 Allocator* alloc;
35
36 Zlib_Alloc_Info() { alloc = get_allocator(); }
37 };
38
39/*************************************************
40* Allocation Function for Zlib *
41*************************************************/
42void* zlib_malloc(void* info_ptr, unsigned int n, unsigned int size)
43 {
44 printf("XXXXXXXX\n");
45 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(info_ptr);
46 void* ptr = info->alloc->allocate(n * size);
47 info->current_allocs[ptr] = n * size;
48 return ptr;
49 }
50
51/*************************************************
52* Allocation Function for Zlib *
53*************************************************/
54void zlib_free(void* info_ptr, void* ptr)
55 {
56 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(info_ptr);
57 std::map<void*, u32bit>::const_iterator i = info->current_allocs.find(ptr);
58 if(i == info->current_allocs.end())
59 throw Invalid_Argument("zlib_free: Got pointer not allocated by us");
60 info->alloc->deallocate(ptr, i->second);
61 }
62}
63
64/*************************************************
65* Wrapper Type for Zlib z_stream *
66*************************************************/
67class Zlib_Stream
68 {
69 public:
70 z_stream stream;
71
72 Zlib_Stream()
73 {
74 std::memset(&stream, 0, sizeof(z_stream));
75 if (paranoid_memory_clearing)
76 {
77 stream.zalloc = zlib_malloc;
78 stream.zfree = zlib_free;
79 stream.opaque = new Zlib_Alloc_Info;
80 }
81 }
82 ~Zlib_Stream()
83 {
84 if (paranoid_memory_clearing)
85 {
86 Zlib_Alloc_Info* info = static_cast<Zlib_Alloc_Info*>(stream.opaque);
87 delete info;
88 std::memset(&stream, 0, sizeof(z_stream));
89 }
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 Exception("Gzip_Compression: Memory allocation error");
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[], u32bit 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 Exception("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 Exception("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());
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 Exception("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 Exception("Gzip_Decompression: Memory allocation error");
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 Exception("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[], u32bit 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 if (length == 0)
281 return;
282 }
283
284 // Check the gzip header
285 if (pos < sizeof(GZIP::GZIP_HEADER))
286 {
287 u32bit len = std::min((u32bit)sizeof(GZIP::GZIP_HEADER)-pos, length);
288 u32bit cmplen = len;
289 // The last byte is the OS flag - we don't care about that
290 if (pos + len - 1 >= GZIP::HEADER_POS_OS)
291 cmplen--;
292
293 if (std::memcmp(input, &GZIP::GZIP_HEADER[pos], cmplen) != 0)
294 {
295 throw Decoding_Error("Gzip_Decompression: Data integrity error in header");
296 }
297 input += len;
298 length -= len;
299 pos += len;
300 }
301
302 pos += length;
303
304 zlib->stream.next_in = (Bytef*)input;
305 zlib->stream.avail_in = length;
306
307 while(zlib->stream.avail_in != 0)
308 {
309 zlib->stream.next_out = (Bytef*)buffer.begin();
310 zlib->stream.avail_out = buffer.size();
311
312 int rc = inflate(&(zlib->stream), Z_SYNC_FLUSH);
313 if(rc != Z_OK && rc != Z_STREAM_END)
314 {
315 if(rc == Z_DATA_ERROR)
316 throw Decoding_Error("Gzip_Decompression: Data integrity error");
317 if(rc == Z_NEED_DICT)
318 throw Decoding_Error("Gzip_Decompression: Need preset dictionary");
319 if(rc == Z_MEM_ERROR)
320 throw Exception("Gzip_Decompression: Memory allocation error");
321 throw Exception("Gzip_Decompression: Unknown decompress error");
322 }
323 send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
324 pipe.write(buffer.begin(), buffer.size() - zlib->stream.avail_out);
325 datacount += buffer.size() - zlib->stream.avail_out;
326
327 // Reached the end - we now need to check the footer
328 if(rc == Z_STREAM_END)
329 {
330 u32bit read_from_block = length - zlib->stream.avail_in;
331 u32bit eat_len = eat_footer((Bytef*)input + read_from_block, zlib->stream.avail_in);
332 read_from_block += eat_len;
333 input += read_from_block;
334 length -= read_from_block;
335 zlib->stream.next_in = (Bytef*)input;
336 zlib->stream.avail_in = length;
337 }
338 }
339 }
340
341/*************************************************
342* Store the footer bytes *
343*************************************************/
344u32bit Gzip_Decompression::eat_footer(const byte input[], u32bit length)
345 {
346 if (footer.size() >= GZIP::FOOTER_LENGTH)
347 throw Decoding_Error("Gzip_Decompression: Data integrity error in footer");
348
349 u32bit eat_len = std::min(GZIP::FOOTER_LENGTH-footer.size(), length);
350 footer.append(input, eat_len);
351
352 if (footer.size() == GZIP::FOOTER_LENGTH)
353 {
354 check_footer();
355 clear();
356 }
357
358 return eat_len;
359 }
360
361/*************************************************
362* Check the gzip footer *
363*************************************************/
364void Gzip_Decompression::check_footer()
365 {
366 if (footer.size() != GZIP::FOOTER_LENGTH)
367 throw Exception("Gzip_Decompression: Error finalizing decompression");
368
369 pipe.end_msg();
370
371 // 4 byte CRC32, and 4 byte length field
372 SecureVector<byte> buf(4);
373 SecureVector<byte> tmpbuf(4);
374 pipe.read(tmpbuf.begin(), tmpbuf.size());
375
376 // CRC32 is the reverse order to what gzip expects.
377 for (int i = 0; i < 4; i++)
378 buf[3-i] = tmpbuf[i];
379
380 tmpbuf.set(footer.begin(), 4);
381 if (buf != tmpbuf)
382 throw Exception("Gzip_Decompression: Data integrity error - CRC32 error");
383
384 // Check the length matches - it is encoded LSB-first
385 for (int i = 0; i < 4; i++)
386 {
387 if (footer.begin()[GZIP::FOOTER_LENGTH-1-i] != get_byte(i, datacount))
388 throw Exception("Gzip_Decompression: Data integrity error - incorrect length");
389 }
390
391 }
392
393/*************************************************
394* Finish Decompressing with Gzip *
395*************************************************/
396void Gzip_Decompression::end_msg()
397 {
398
399 // All messages should end with a footer, and when a footer is successfully
400 // read, clear() will reset no_writes
401 if(no_writes) return;
402
403 throw Exception("Gzip_Decompression: didn't find footer");
404
405 }
406
407/*************************************************
408* Clean up Decompression Context *
409*************************************************/
410void Gzip_Decompression::clear()
411 {
412 no_writes = true;
413 inflateReset(&(zlib->stream));
414
415 footer.clear();
416 pos = 0;
417 datacount = 0;
418 }
419
420}

Archive Download this file

Branches

Tags

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