Annotation of embedaddon/libpdel/io/base64.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (c) 2001-2002 Packet Design, LLC.
4: * All rights reserved.
5: *
6: * Subject to the following obligations and disclaimer of warranty,
7: * use and redistribution of this software, in source or object code
8: * forms, with or without modifications are expressly permitted by
9: * Packet Design; provided, however, that:
10: *
11: * (i) Any and all reproductions of the source or object code
12: * must include the copyright notice above and the following
13: * disclaimer of warranties; and
14: * (ii) No rights are granted, in any manner or form, to use
15: * Packet Design trademarks, including the mark "PACKET DESIGN"
16: * on advertising, endorsements, or otherwise except as such
17: * appears in the above copyright notice or in the software.
18: *
19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
36: * THE POSSIBILITY OF SUCH DAMAGE.
37: *
38: * Author: Archie Cobbs <archie@freebsd.org>
39: */
40:
41: #include <sys/types.h>
42: #include <sys/param.h>
43:
44: #include <assert.h>
45: #include <stdio.h>
46: #include <stdarg.h>
47: #include <string.h>
48: #include <errno.h>
49: #include <stdlib.h>
50: #include <pthread.h>
51:
52: #include "structs/structs.h"
53: #include "structs/type/array.h"
54:
55: #include "io/base64.h"
56: #include "io/filter.h"
57: #include "util/typed_mem.h"
58:
59: /************************************************************************
60: BASE 64 ENCODER
61: ************************************************************************/
62:
63: #define ENCODER_MEM_TYPE "base64_encoder"
64:
65: /* Encoder state */
66: struct b64_encoder {
67: struct filter filter;
68: pthread_mutex_t mutex;
69: char *cmap;
70: u_char dbuf[3];
71: int doff;
72: char *obuf;
73: int olen;
74: int osize;
75: u_char done;
76: };
77:
78: /* Internal functions */
79: static filter_read_t b64_encoder_read;
80: static filter_write_t b64_encoder_write;
81: static filter_end_t b64_encoder_end;
82: static filter_convert_t b64_encoder_convert;
83: static filter_destroy_t b64_encoder_destroy;
84:
85: static void b64_encoder_encode(struct b64_encoder *enc, char *buf);
86: static int b64_encoder_output(struct b64_encoder *enc, const char *buf);
87: static int b64_cmap_check(const char *cmap);
88:
89: /* Public variables */
90: const char b64_rfc2045_charset[] =
91: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
92:
93: /*
94: * Create a new base-64 encoder with optional custom character set.
95: */
96: struct filter *
97: b64_encoder_create(const char *cmap)
98: {
99: struct b64_encoder *enc;
100:
101: /* Default to HTTP charset */
102: if (cmap == NULL)
103: cmap = b64_rfc2045_charset;
104: else if (b64_cmap_check(cmap) == -1)
105: return (NULL);
106:
107: /* Create object */
108: if ((enc = MALLOC(ENCODER_MEM_TYPE, sizeof(*enc))) == NULL)
109: return (NULL);
110: memset(enc, 0, sizeof(*enc));
111:
112: /* Copy character map */
113: if ((enc->cmap = STRDUP(ENCODER_MEM_TYPE, cmap)) == NULL) {
114: FREE(ENCODER_MEM_TYPE, enc);
115: return (NULL);
116: }
117:
118: /* Create mutex */
119: if ((errno = pthread_mutex_init(&enc->mutex, NULL)) != 0) {
120: FREE(ENCODER_MEM_TYPE, enc->cmap);
121: FREE(ENCODER_MEM_TYPE, enc);
122: return (NULL);
123: }
124:
125: /* Set up methods */
126: enc->filter.read = b64_encoder_read;
127: enc->filter.write = b64_encoder_write;
128: enc->filter.end = b64_encoder_end;
129: enc->filter.convert = b64_encoder_convert;
130: enc->filter.destroy = b64_encoder_destroy;
131:
132: /* Done */
133: return (&enc->filter);
134: }
135:
136: /*
137: * Destroy a base 64 encoder.
138: */
139: void
140: b64_encoder_destroy(struct filter **filterp)
141: {
142: struct b64_encoder **const encp = (struct b64_encoder **)filterp;
143: struct b64_encoder *const enc = *encp;
144:
145: if (enc != NULL) {
146: pthread_mutex_destroy(&enc->mutex);
147: FREE(ENCODER_MEM_TYPE, enc->cmap);
148: FREE(ENCODER_MEM_TYPE, enc->obuf);
149: FREE(ENCODER_MEM_TYPE, enc);
150: *encp = NULL;
151: }
152: }
153:
154: /*
155: * Write raw bytes into the encoder.
156: */
157: int
158: b64_encoder_write(struct filter *filter, const void *data, int len)
159: {
160: struct b64_encoder *const enc = (struct b64_encoder *)filter;
161: char buf[4];
162: int total;
163: int chunk;
164: int r;
165:
166: /* Lock encoder */
167: r = pthread_mutex_lock(&enc->mutex);
168: assert(r == 0);
169:
170: /* Check if closed */
171: if (enc->done) {
172: r = pthread_mutex_unlock(&enc->mutex);
173: assert(r == 0);
174: errno = EPIPE;
175: return (-1);
176: }
177:
178: /* Process bytes */
179: for (total = 0; len > 0; total += chunk) {
180:
181: /* Read in next chunk of raw data */
182: chunk = MIN(len, 3 - enc->doff);
183: memcpy(enc->dbuf + enc->doff, data, chunk);
184: data = (char *)data + chunk;
185: len -= chunk;
186:
187: /* Process data if data buffer full */
188: if ((enc->doff += chunk) == 3) {
189: b64_encoder_encode(enc, buf);
190: if (b64_encoder_output(enc, buf) == -1) {
191: total = (total == 0) ? -1 : total;
192: break;
193: }
194: enc->doff = 0;
195: }
196:
197: /* Unlock encoder in case there is a reader */
198: r = pthread_mutex_unlock(&enc->mutex);
199: assert(r == 0);
200: r = pthread_mutex_lock(&enc->mutex);
201: assert(r == 0);
202: }
203:
204: /* Done */
205: r = pthread_mutex_unlock(&enc->mutex);
206: assert(r == 0);
207: return (total);
208: }
209:
210: /*
211: * Read out encoded data.
212: */
213: int
214: b64_encoder_read(struct filter *filter, void *data, int len)
215: {
216: struct b64_encoder *const enc = (struct b64_encoder *)filter;
217: int r;
218:
219: r = pthread_mutex_lock(&enc->mutex);
220: assert(r == 0);
221: len = MIN(len, enc->olen);
222: memcpy(data, enc->obuf, len);
223: memmove(enc->obuf, enc->obuf + len, enc->olen - len);
224: enc->olen -= len;
225: r = pthread_mutex_unlock(&enc->mutex);
226: assert(r == 0);
227: return (len);
228: }
229:
230: /*
231: * Mark end of written data.
232: */
233: int
234: b64_encoder_end(struct filter *filter)
235: {
236: struct b64_encoder *const enc = (struct b64_encoder *)filter;
237: char buf[4];
238: int r;
239:
240: /* Lock encoder */
241: r = pthread_mutex_lock(&enc->mutex);
242: assert(r == 0);
243:
244: /* Only do this once */
245: if (enc->done)
246: goto done;
247:
248: /* Pad with termination character(s) */
249: if (enc->doff > 0) {
250: memset(enc->dbuf + enc->doff, 0, 3 - enc->doff);
251: b64_encoder_encode(enc, buf);
252: switch (enc->doff) {
253: case 1:
254: buf[2] = enc->cmap[64];
255: /* fall through */
256: case 2:
257: buf[3] = enc->cmap[64];
258: break;
259: }
260: if (b64_encoder_output(enc, buf) == -1) {
261: r = pthread_mutex_unlock(&enc->mutex);
262: assert(r == 0);
263: return (-1);
264: }
265: }
266:
267: done:
268: /* Done */
269: enc->done = 1;
270: r = pthread_mutex_unlock(&enc->mutex);
271: assert(r == 0);
272: return (0);
273: }
274:
275: /*
276: * Convert byte count to read before and after filter.
277: */
278: static int
279: b64_encoder_convert(struct filter *filter, int num, int forward)
280: {
281: if (forward)
282: return (((num * 4) + 2) / 3);
283: else
284: return (((num * 3) + 3) / 4);
285: }
286:
287: /*
288: * Encode one chunk of data (3 bytes -> 4 characters).
289: *
290: * This assumes the encoder is locked.
291: */
292: static void
293: b64_encoder_encode(struct b64_encoder *enc, char *buf)
294: {
295: buf[0] = (enc->dbuf[0] >> 2) & 0x3f;
296: buf[0] = enc->cmap[(u_char)buf[0]];
297: buf[1] = ((enc->dbuf[0] << 4) & 0x30) | ((enc->dbuf[1] >> 4) & 0x0f);
298: buf[1] = enc->cmap[(u_char)buf[1]];
299: buf[2] = ((enc->dbuf[1] << 2) & 0x3c) | ((enc->dbuf[2] >> 6) & 0x03);
300: buf[2] = enc->cmap[(u_char)buf[2]];
301: buf[3] = enc->dbuf[2] & 0x3f;
302: buf[3] = enc->cmap[(u_char)buf[3]];
303: }
304:
305: /*
306: * Append encoded characters to the output buffer.
307: *
308: * This assumes the encoder is locked.
309: */
310: static int
311: b64_encoder_output(struct b64_encoder *enc, const char *buf)
312: {
313: if (enc->olen + 4 > enc->osize) {
314: const int new_osize = (enc->olen * 2) + 31;
315: char *new_obuf;
316:
317: if ((new_obuf = REALLOC(ENCODER_MEM_TYPE,
318: enc->obuf, new_osize)) == NULL)
319: return (-1);
320: enc->obuf = new_obuf;
321: enc->osize = new_osize;
322: }
323: memcpy(enc->obuf + enc->olen, buf, 4);
324: enc->olen += 4;
325: return (0);
326: }
327:
328: /*
329: * Sanity check character map.
330: */
331: static int
332: b64_cmap_check(const char *cmap)
333: {
334: int i;
335: int j;
336:
337: if (strlen(cmap) != 65)
338: goto bogus;
339: for (i = 0; i < 64; i++) {
340: for (j = i + 1; j < 65; j++) {
341: if (cmap[i] == cmap[j]) {
342: bogus: errno = EINVAL;
343: return (-1);
344: }
345: }
346: }
347: return (0);
348: }
349:
350: /************************************************************************
351: BASE 64 DECODER
352: ************************************************************************/
353:
354: #define DECODER_MEM_TYPE "base64_decoder"
355:
356: /* Encoder state */
357: struct b64_decoder {
358: struct filter filter;
359: pthread_mutex_t mutex;
360: u_char cmap[256];
361: u_char cbuf[4];
362: int coff;
363: u_char *obuf;
364: int olen;
365: int osize;
366: char pad;
367: u_char done;
368: u_char strict;
369: };
370:
371: /* Internal functions */
372: static filter_read_t b64_decoder_read;
373: static filter_write_t b64_decoder_write;
374: static filter_end_t b64_decoder_end;
375: static filter_convert_t b64_decoder_convert;
376: static filter_destroy_t b64_decoder_destroy;
377:
378: static void b64_decoder_decode(struct b64_decoder *dec, u_char *buf);
379: static int b64_decoder_output(struct b64_decoder *dec,
380: u_char *buf, int len);
381:
382: /*
383: * Create a new base-64 decoder with optional custom character set.
384: */
385: struct filter *
386: b64_decoder_create(const char *cmap, int strict)
387: {
388: struct b64_decoder *dec;
389: const char *s;
390: u_char byte;
391: int i;
392:
393: /* Default to HTTP charset */
394: if (cmap == NULL)
395: cmap = b64_rfc2045_charset;
396: else if (b64_cmap_check(cmap) == -1)
397: return (NULL);
398:
399: /* Create object */
400: if ((dec = MALLOC(DECODER_MEM_TYPE, sizeof(*dec))) == NULL)
401: return (NULL);
402: memset(dec, 0, sizeof(*dec));
403: dec->strict = !!strict;
404: dec->pad = cmap[64];
405:
406: /* Create mutex */
407: if ((errno = pthread_mutex_init(&dec->mutex, NULL)) != 0) {
408: FREE(DECODER_MEM_TYPE, dec);
409: return (NULL);
410: }
411:
412: /* Create inverse character map */
413: memset(dec->cmap, 0xff, sizeof(dec->cmap));
414: for (i = 1; i < sizeof(dec->cmap); i++) {
415: if ((s = strchr(cmap, (char)i)) != NULL) {
416: byte = (u_char)(s - cmap);
417: dec->cmap[i] = (byte == 64) ? 0xfe : byte;
418: }
419: }
420:
421: /* Set up methods */
422: dec->filter.read = b64_decoder_read;
423: dec->filter.write = b64_decoder_write;
424: dec->filter.end = b64_decoder_end;
425: dec->filter.convert = b64_decoder_convert;
426: dec->filter.destroy = b64_decoder_destroy;
427:
428: /* Done */
429: return (&dec->filter);
430: }
431:
432: /*
433: * Destroy a base 64 decoder.
434: */
435: void
436: b64_decoder_destroy(struct filter **filterp)
437: {
438: struct b64_decoder **const decp = (struct b64_decoder **)filterp;
439: struct b64_decoder *const dec = *decp;
440:
441: if (dec != NULL) {
442: pthread_mutex_destroy(&dec->mutex);
443: FREE(DECODER_MEM_TYPE, dec->obuf);
444: FREE(DECODER_MEM_TYPE, dec);
445: *decp = NULL;
446: }
447: }
448:
449: /*
450: * Write encoded characters into the decoder.
451: */
452: int
453: b64_decoder_write(struct filter *filter, const void *data, int len)
454: {
455: struct b64_decoder *const dec = (struct b64_decoder *)filter;
456: u_char buf[3];
457: int total = 0;
458: int r;
459:
460: /* Lock decoder */
461: r = pthread_mutex_lock(&dec->mutex);
462: assert(r == 0);
463:
464: /* Check if closed */
465: if (dec->done) {
466: r = pthread_mutex_unlock(&dec->mutex);
467: assert(r == 0);
468: errno = EPIPE;
469: return (-1);
470: }
471:
472: /* Process bytes */
473: while (total < len) {
474:
475: /* Fill up input buffer */
476: for ( ; dec->coff < 4 && total < len; total++) {
477: const u_char val = dec->cmap[((u_char *)data)[total]];
478:
479: if (val == 0xff) {
480: if (dec->strict) {
481: errno = EINVAL;
482: total = (total == 0) ? -1 : total;
483: goto done;
484: }
485: continue;
486: }
487: if (val == 0xfe) /* pad char */
488: continue;
489: assert(val < 64);
490: dec->cbuf[dec->coff++] = val;
491: }
492:
493: /* Process characters if character buffer is full */
494: if (dec->coff == 4) {
495: b64_decoder_decode(dec, buf);
496: if (b64_decoder_output(dec, buf, 3) == -1) {
497: total = (total == 0) ? -1 : total;
498: goto done;
499: }
500: dec->coff = 0;
501: }
502:
503: /* Take a breather */
504: r = pthread_mutex_unlock(&dec->mutex);
505: assert(r == 0);
506: r = pthread_mutex_lock(&dec->mutex);
507: assert(r == 0);
508: }
509:
510: done:
511: /* Done */
512: r = pthread_mutex_unlock(&dec->mutex);
513: assert(r == 0);
514: return (total);
515: }
516:
517: /*
518: * Read out decoded data.
519: */
520: int
521: b64_decoder_read(struct filter *filter, void *buf, int len)
522: {
523: struct b64_decoder *const dec = (struct b64_decoder *)filter;
524: int r;
525:
526: r = pthread_mutex_lock(&dec->mutex);
527: assert(r == 0);
528: len = MIN(len, dec->olen);
529: memcpy(buf, dec->obuf, len);
530: memmove(dec->obuf, dec->obuf + len, dec->olen - len);
531: dec->olen -= len;
532: r = pthread_mutex_unlock(&dec->mutex);
533: assert(r == 0);
534: return (len);
535: }
536:
537: /*
538: * Mark end of written data.
539: */
540: int
541: b64_decoder_end(struct filter *filter)
542: {
543: struct b64_decoder *const dec = (struct b64_decoder *)filter;
544: u_char buf[3];
545: int len;
546: int r;
547:
548: /* Lock decoder */
549: r = pthread_mutex_lock(&dec->mutex);
550: assert(r == 0);
551:
552: /* Only do this once */
553: if (dec->done)
554: goto done;
555:
556: /* Spit out any trailing characters */
557: if (dec->coff > 0) {
558: switch (dec->coff) {
559: case 1: /* really not valid */
560: case 2:
561: len = 1;
562: break;
563: case 3:
564: len = 2;
565: break;
566: default:
567: assert(0);
568: len = 0; /* silence gcc warning */
569: }
570: memset(dec->cbuf + dec->coff, 0, 4 - dec->coff);
571: b64_decoder_decode(dec, buf);
572: if (b64_decoder_output(dec, buf, len) == -1) {
573: r = pthread_mutex_unlock(&dec->mutex);
574: assert(r == 0);
575: return (-1);
576: }
577: dec->coff = 0;
578: }
579:
580: done:
581: /* Done */
582: dec->done = 1;
583: r = pthread_mutex_unlock(&dec->mutex);
584: assert(r == 0);
585: return (0);
586: }
587:
588: /*
589: * Convert byte count to read before and after filter.
590: */
591: static int
592: b64_decoder_convert(struct filter *filter, int num, int forward)
593: {
594: if (forward)
595: return (((num * 3) + 3) / 4);
596: else
597: return (((num * 4) + 2) / 3);
598: }
599:
600: /*
601: * Decode one chunk of characters (4 characters -> 3 bytes).
602: *
603: * This assumes the decoder is locked.
604: */
605: static void
606: b64_decoder_decode(struct b64_decoder *dec, u_char *buf)
607: {
608: buf[0] = (dec->cbuf[0] << 2) | (dec->cbuf[1] >> 4);
609: buf[1] = (dec->cbuf[1] << 4) | (dec->cbuf[2] >> 2);
610: buf[2] = (dec->cbuf[2] << 6) | dec->cbuf[3];
611: }
612:
613: /*
614: * Append decoded characters to the output buffer.
615: *
616: * This assumes the decoder is locked.
617: */
618: static int
619: b64_decoder_output(struct b64_decoder *dec, u_char *buf, int len)
620: {
621: if (dec->olen + len > dec->osize) {
622: const int new_osize = (dec->olen * 2) + 31;
623: u_char *new_obuf;
624:
625: if ((new_obuf = REALLOC(DECODER_MEM_TYPE,
626: dec->obuf, new_osize)) == NULL)
627: return (-1);
628: dec->obuf = new_obuf;
629: dec->osize = new_osize;
630: }
631: memcpy(dec->obuf + dec->olen, buf, len);
632: dec->olen += len;
633: return (0);
634: }
635:
636: #ifdef BASE64_TEST
637:
638: #include <err.h>
639: #include <unistd.h>
640:
641: int
642: main(int ac, char **av)
643: {
644: struct filter *filter;
645: int do_encode = 1; /* default encode */
646: int do_input = 1; /* default test input stream */
647: int strict = 0;
648: char buf[123];
649: FILE *input;
650: FILE *output;
651: int len;
652: int ch;
653:
654: /* Process command line */
655: while ((ch = getopt(ac, av, "deiso")) != -1) {
656: switch (ch) {
657: case 'd': /* decode */
658: do_encode = 0;
659: break;
660: case 'e': /* encode */
661: do_encode = 1;
662: break;
663: case 'i': /* test input stream */
664: do_input = 1;
665: break;
666: case 'o': /* test output stream */
667: do_input = 0;
668: break;
669: case 's': /* enforce strict decoding */
670: strict = 1;
671: break;
672: default:
673: usage:
674: errx(1, "usage: base64 <-e|-d> <-i|-o> [-s]");
675: }
676: }
677: ac -= optind;
678: av += optind;
679:
680: /* Sanity */
681: if (do_input == -1 || do_encode == -1)
682: goto usage;
683:
684: /* Get filter */
685: if (do_encode) {
686: if ((filter = b64_encoder_create(NULL)) == NULL)
687: err(1, "b64_encoder_create");
688: } else {
689: if ((filter = b64_decoder_create(NULL, strict)) == NULL)
690: err(1, "b64_decoder_create");
691: }
692:
693: /* Get filter stream */
694: if (do_input) {
695: if ((output = filter_fopen(filter, 0, stdout, "w")) == NULL)
696: err(1, "filter_fopen");
697: input = stdin;
698: } else {
699: if ((input = filter_fopen(filter, 0, stdin, "r")) == NULL)
700: err(1, "filter_fopen");
701: output = stdout;
702: }
703:
704: /* Convert */
705: while ((len = fread(buf, 1, sizeof(buf), input)) != 0)
706: fwrite(buf, 1, len, output);
707:
708: /* Done */
709: fclose(output);
710: return (0);
711: }
712:
713: #endif /* BASE64_TEST */
714:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>