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

Archive Download this file

Branches

Tags

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