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

Archive Download this file

Branches

Tags

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