monotone

monotone Mtn Source Tree

Root/gzip.cc

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

Archive Download this file

Branches

Tags

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