1 /*
2 * image.c:
3 * Attempt to find GIF/JPEG/PNG data embedded in buffers.
4 *
5 * Copyright (c) 2001 Chris Lightfoot.
6 * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
7 *
8 */
9
10 #include "compat/compat.h"
11
12 #include <stdio.h>
13 #include <stdlib.h> /* On many systems (Darwin...), stdio.h is a prerequisite. */
14 #include <string.h>
15 #include <sys/types.h>
16
17 #include <netinet/in.h> /* ntohl */
18
19 #include "common/util.h"
20
21 #include "pngformat.h"
22
23 /* If we run out of space, put us back to the last candidate GIF header. */
24 #define spaceleft if (block >= data + len) return gifhdr /* > ?? */
25
find_gif_image(const unsigned char * data,const size_t len,unsigned char ** gifdata,size_t * giflen)26 unsigned char *find_gif_image(const unsigned char *data, const size_t len, unsigned char **gifdata, size_t *giflen)
27 {
28 unsigned char *gifhdr, *block;
29 int ncolours;
30
31 *gifdata = NULL;
32
33 if (len < 6) return (unsigned char*)data;
34
35 gifhdr = memstr(data, len, (unsigned char*)"GIF89a", 6);
36 if (!gifhdr) gifhdr = memstr(data, len, (unsigned char*)"GIF87a", 6);
37 if (!gifhdr) return (unsigned char*)(data + len - 6);
38 if (data + len - gifhdr < 14) return gifhdr; /* no space for header */
39
40 ncolours = (1 << ((gifhdr[10] & 0x7) + 1));
41 /* printf("gif header %d colours\n", ncolours); */
42 block = gifhdr + 13;
43 spaceleft;
44 if (gifhdr[10] & 0x80) block += 3 * ncolours; /* global colour table */
45 spaceleft;
46
47 do {
48 /* printf("gifhdr = %p block = %p off = %u %02x\n", gifhdr, block, block - gifhdr, (unsigned int)*block); */
49 switch (*block) {
50 case 0x2c:
51 /* image block */
52 /* printf("image data\n"); */
53 if (block + 9 > data + len) return gifhdr;
54 if (block[9] & 0x80) {
55 /* local colour table */
56 block += 3 * ((1 << ((gifhdr[9] & 0x7) + 1)));
57 spaceleft;
58 }
59 block += 10;
60 ++block; /* lzw code size */
61 do {
62 spaceleft;
63 block += *block + 1;
64 spaceleft;
65 } while (*block);
66 ++block;
67 spaceleft;
68 /*gotimgblock = 1;*/
69
70 break;
71
72 case 0x21:
73 /* extension */
74 ++block;
75 spaceleft;
76 if (*block == 0xf9) {
77 /* graphic control */
78 /* printf("graphic control\n"); */
79 ++block;
80 spaceleft;
81 block += *block + 2;
82 spaceleft;
83 break;
84 } else if (*block == 0xfe) {
85 /* comment */
86 /* printf("comment\n"); */
87 ++block;
88 do {
89 spaceleft;
90 block += *block + 1;
91 spaceleft;
92 } while (*block);
93 ++block;
94 spaceleft;
95 } else if (*block == 0x01) {
96 /* text label */
97 /* printf("text label\n"); */
98 ++block;
99 spaceleft;
100 if (*block != 12) return gifhdr + 6;
101 block += 13;
102 do {
103 spaceleft;
104 block += *block + 1;
105 spaceleft;
106 } while (*block);
107 ++block;
108 spaceleft;
109 } else if (*block == 0xff) {
110 /* printf("application extension\n"); */
111 ++block;
112 spaceleft;
113 if (*block != 11) return gifhdr + 6;
114 block += 12;
115 do {
116 spaceleft;
117 /* printf("app extension data %d bytes\n", (int)*block); */
118 block += *block + 1;
119 spaceleft;
120 } while (*block);
121 ++block;
122 spaceleft;
123 } else {
124 /* printf("unknown extension block\n"); */
125 return gifhdr + 6;
126 }
127 break;
128
129 case 0x3b:
130 /* end of file block: we win. */
131 /* printf("gif data from %p to %p\n", gifhdr, block); */
132 *gifdata = gifhdr;
133 *giflen = block - gifhdr + 1;
134 return block + 1;
135 break;
136
137 default:
138 /* printf("unknown block %02x\n", *block); */
139 return gifhdr + 6;
140 }
141 } while (1);
142 }
143
144 enum jpeg_marker {
145 SOI = 0xD8,
146 DHT = 0xC4,
147 DAC = 0xCC,
148 DQT = 0xDB,
149 DRI = 0xDD,
150 DHP = 0xDE,
151 EXP = 0xDF,
152 SOS = 0xDA,
153 COM = 0xFE,
154 EOI = 0xD9,
155
156 RST0 = 0xD0,
157 RST1 = 0xD1,
158 RST2 = 0xD2,
159 RST3 = 0xD3,
160 RST4 = 0xD4,
161 RST5 = 0xD5,
162 RST6 = 0xD6,
163 RST7 = 0xD7,
164
165 SOF0 = 0xC0, SOF1 = 0xC1, SOF2 = 0xC2, SOF3 = 0xC3, SOF5 = 0xC5, SOF6 = 0xC6, SOF7 = 0xC7,
166 SOF8 = 0xC8, SOF9 = 0xC9, SOFA = 0xCA, SOFB = 0xCB, SOFD = 0xCD, SOFE = 0xCE, SOFF = 0xCF,
167
168 APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3, APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
169 APP8 = 0xE8, APP9 = 0xE9, APPA = 0xEA, APPB = 0xEB, APPC = 0xEC, APPD = 0xED, APPE = 0xEE, APPF = 0xEF,
170
171 JPG0 = 0xF0, JPG1 = 0xF1, JPG2 = 0xF2, JPG3 = 0xF3, JPG4 = 0xF4, JPG5 = 0xF5, JPG6 = 0xF6,
172 JPG7 = 0xF7, JPG8 = 0xF8, JPG9 = 0xF9, JPGA = 0xFA, JPGB = 0xFB, JPGC = 0xFC, JPGD = 0xFD
173 };
174
175 typedef struct {
176 unsigned char start_of_marker; /* always 0xFF */
177 unsigned char marker_type;
178 unsigned char length[2];
179 } jpg_segment_t;
180
181 /* If we run out of space, put us back to the last candidate JPEG header. */
182 #define jpegcount(c) ((*(c) << 8) | *((c) + 1))
183
is_jpeg_segment(jpg_segment_t * segment)184 unsigned int is_jpeg_segment(jpg_segment_t* segment)
185 {
186 if (segment->start_of_marker != 0xFF) {
187 return 0;
188 }
189
190 /*
191 * Check for markers
192 */
193 switch (segment->marker_type) {
194
195 /* without payload */
196 case SOI:
197 case EOI:
198 case RST0:
199 case RST1:
200 case RST2:
201 case RST3:
202 case RST4:
203 case RST5:
204 case RST6:
205 case RST7:
206 case DHP:
207 case EXP:
208 case DAC:
209 return 4;
210
211 /* constant payload */
212 case DRI:
213 return 4 + 4;
214
215 /* variable payload */
216 case DHT:
217 case DQT:
218 case SOS:
219 case COM:
220
221 case SOF0:case SOF1:case SOF2:case SOF3:case SOF5:case SOF6:case SOF7:
222 case SOF8:case SOF9:case SOFA:case SOFB:case SOFD:case SOFE:case SOFF:
223
224 case APP0:case APP1:case APP2:case APP3:case APP4:case APP5:case APP6:case APP7:
225 case APP8:case APP9:case APPA:case APPB:case APPC:case APPD:case APPE:case APPF:
226
227 case JPG0:case JPG1:case JPG2:case JPG3:case JPG4:case JPG5:case JPG6:
228 case JPG7:case JPG8:case JPG9:case JPGA:case JPGB:case JPGC:case JPGD:
229
230 return (((segment->length[0] << 8) & 0xFF00) | segment->length[1]) + 2;
231 }
232
233 return 0;
234 }
235
find_jpeg_image(const unsigned char * data,const size_t len,unsigned char ** jpegdata,size_t * jpeglen)236 unsigned char *find_jpeg_image(const unsigned char *data, const size_t len, unsigned char **jpegdata, size_t *jpeglen)
237 {
238 unsigned char *jpeghdr;
239
240 *jpegdata = NULL;
241 *jpeglen = 0;
242
243 if (data == NULL) {
244 return NULL;
245 }
246
247 /* find SOI marker */
248 jpeghdr = memstr(data, len, (unsigned char*)"\xff\xd8", 2);
249 if (!jpeghdr) return (unsigned char*)(data + len - 1);
250
251 jpg_segment_t* segment;
252 unsigned int segment_lenght = 2;
253 unsigned char *block = jpeghdr;
254
255 do {
256 /* more data to advance ? */
257 if (data + len < block + segment_lenght) {
258 break;
259 }
260
261 /* advance to next block */
262 block = block + segment_lenght;
263 segment = (jpg_segment_t*) block;
264 segment_lenght = is_jpeg_segment(segment);
265
266 /*
267 * start of scan
268 *
269 * XXX: dunno how to parse...
270 */
271 if (segment->marker_type == SOS) {
272 block = memstr(block, len - (block - data), (unsigned char*)"\xff\xd9", 2);
273
274 if (block) {
275 *jpegdata = jpeghdr;
276 *jpeglen = block + 2 - jpeghdr;
277 return block + 2;
278 } else break;
279 }
280
281 } while (segment_lenght != 0);
282
283 return jpeghdr;
284 }
285
286 /* find_png_eoi BUFFER LEN
287 * Returns the first position in BUFFER of LEN bytes after the end of the image
288 * or NULL if end of image not found. */
find_png_eoi(unsigned char * buffer,const size_t len)289 unsigned char *find_png_eoi(unsigned char *buffer, const size_t len) {
290 unsigned char *end_data, *data, chunk_code[PNG_CODE_LEN + 1];
291 struct png_chunk chunk;
292 u_int32_t datalen;
293
294 /* Move past the PNG header */
295 data = (buffer + PNG_SIG_LEN);
296 end_data = (buffer + len - (sizeof(struct png_chunk) + PNG_CRC_LEN));
297
298 while (data <= end_data) {
299 memcpy(&chunk, data, sizeof chunk);
300 /* chunk = (struct png_chunk *)data; */ /* can't do that. */
301 memset(chunk_code, '\0', PNG_CODE_LEN + 1);
302 memcpy(chunk_code, chunk.code, PNG_CODE_LEN);
303
304 datalen = ntohl(chunk.datalen);
305
306 if (!strncasecmp((char*)chunk_code, "iend", PNG_CODE_LEN))
307 return (unsigned char *)(data + sizeof(struct png_chunk) + PNG_CRC_LEN);
308
309 /* Would this push us off the end of the buffer? */
310 if (datalen > (len - (data - buffer)))
311 return NULL;
312
313 data += (sizeof(struct png_chunk) + datalen + PNG_CRC_LEN);
314 }
315
316 return NULL;
317 }
318
319 /* find_png_image DATA LEN PNGDATA PNGLEN
320 * Look for PNG images in LEN bytes buffer DATA. */
find_png_image(const unsigned char * data,const size_t len,unsigned char ** pngdata,size_t * pnglen)321 unsigned char *find_png_image(const unsigned char *data, const size_t len, unsigned char **pngdata, size_t *pnglen) {
322 unsigned char *pnghdr, *data_end, *png_eoi;
323
324 *pngdata = NULL;
325
326 if (len < PNG_SIG_LEN)
327 return (unsigned char*)data;
328
329 pnghdr = memstr(data, len, (unsigned char*)"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", PNG_SIG_LEN);
330 if (!pnghdr)
331 return (unsigned char*)(data + len - PNG_SIG_LEN);
332
333 data_end = (unsigned char *)(data + len);
334
335 if ((png_eoi = find_png_eoi(pnghdr, (data_end - pnghdr))) == NULL)
336 return pnghdr;
337
338 *pngdata = pnghdr;
339 *pnglen = (png_eoi - pnghdr);
340 return png_eoi;
341 }
342
343
344 #if 0
345 #include <unistd.h>
346 #include <fcntl.h>
347
348 int main(int argc, char **argv) {
349 unsigned char buf[262144];
350 char **a;
351 for (a = argv + 1; *a; ++a) {
352 unsigned char *p, *img;
353 size_t len;
354 int fd = open(*a, O_RDONLY);
355 read(fd, buf + rand() % 256, 261000);
356 /* printf("jpeg file %s\n", *a); */
357 p = buf;
358 do {
359 /* printf("--> now p = %p\n", p); */
360 p = find_jpeg_image(p, 262144 - (p - buf), &img, &len);
361 if (img) /* printf(" found image %p len %u\n", img, len); */
362 } while (p);
363 }
364 }
365 #endif
366