base64.c revision 5f0e2c8913fed44e1629f1367ce54e74ce2a2eb3
/*
* Copyright (C) 1998, 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* $Id: base64.c,v 1.5 1999/09/15 23:58:39 explorer Exp $ */
#include <config.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <isc/base64.h>
#include <isc/buffer.h>
#include <isc/lex.h>
#include <isc/assertions.h>
#include <isc/error.h>
#include <isc/region.h>
#define RETERR(x) do { \
isc_result_t __r = (x); \
if (__r != ISC_R_SUCCESS) \
return (__r); \
} while (0)
/* These static functions are also present in lib/dns/rdata.c. I'm not
* sure where they should go. -- bwelling
*/
static isc_result_t str_totext(char *source, isc_buffer_t *target);
static isc_result_t gettoken(isc_lex_t *lexer, isc_token_t *token,
isc_tokentype_t expect, isc_boolean_t eol);
static isc_result_t mem_tobuffer(isc_buffer_t *target, void *base,
unsigned int length);
static const char base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
isc_result_t
isc_base64_totext(isc_region_t *source, int wordlength,
char *wordbreak, isc_buffer_t *target)
{
char buf[5];
unsigned int loops = 0;
if (wordlength < 4)
wordlength = 4;
memset(buf, 0, sizeof buf);
while (source->length > 2) {
buf[0] = base64[(source->base[0]>>2)&0x3f];
buf[1] = base64[((source->base[0]<<4)&0x30)|
((source->base[1]>>4)&0x0f)];
buf[2] = base64[((source->base[1]<<2)&0x3c)|
((source->base[2]>>6)&0x03)];
buf[3] = base64[source->base[2]&0x3f];
RETERR(str_totext(buf, target));
isc_region_consume(source, 3);
loops++;
if (source->length != 0 &&
(int)((loops + 1) * 4) >= wordlength)
{
loops = 0;
RETERR(str_totext(wordbreak, target));
}
}
if (source->length == 2) {
buf[0] = base64[(source->base[0]>>2)&0x3f];
buf[1] = base64[((source->base[0]<<4)&0x30)|
((source->base[1]>>4)&0x0f)];
buf[2] = base64[((source->base[1]<<2)&0x3c)];
buf[3] = '=';
RETERR(str_totext(buf, target));
} else if (source->length == 1) {
buf[0] = base64[(source->base[0]>>2)&0x3f];
buf[1] = base64[((source->base[0]<<4)&0x30)];
buf[2] = buf[3] = '=';
RETERR(str_totext(buf, target));
}
return (ISC_R_SUCCESS);
}
isc_result_t
isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
int digits = 0;
isc_textregion_t *tr;
int val[4];
unsigned char buf[3];
int seen_end = 0;
unsigned int i;
isc_token_t token;
char *s;
int n;
while (!seen_end && (length != 0)) {
if (length > 0)
RETERR(gettoken(lexer, &token, isc_tokentype_string,
ISC_FALSE));
else
RETERR(gettoken(lexer, &token, isc_tokentype_string,
ISC_TRUE));
if (token.type != isc_tokentype_string)
break;
tr = &token.value.as_textregion;
for (i = 0 ;i < tr->length; i++) {
if (seen_end)
return (ISC_R_BADBASE64);
if ((s = strchr(base64, tr->base[i])) == NULL)
return (ISC_R_BADBASE64);
val[digits++] = s - base64;
if (digits == 4) {
if (val[0] == 64 || val[1] == 64)
return (ISC_R_BADBASE64);
if (val[2] == 64 && val[3] != 64)
return (ISC_R_BADBASE64);
n = (val[2] == 64) ? 1 :
(val[3] == 64) ? 2 : 3;
if (n != 3) {
seen_end = 1;
if (val[2] == 64)
val[2] = 0;
if (val[3] == 64)
val[3] = 0;
}
buf[0] = (val[0]<<2)|(val[1]>>4);
buf[1] = (val[1]<<4)|(val[2]>>2);
buf[2] = (val[2]<<6)|(val[3]);
RETERR(mem_tobuffer(target, buf, n));
if (length >= 0) {
if (n > length)
return (ISC_R_BADBASE64);
else
length -= n;
}
digits = 0;
}
}
}
if (length < 0 && !seen_end)
isc_lex_ungettoken(lexer, &token);
if (length > 0)
return (ISC_R_UNEXPECTEDEND);
if (digits != 0)
return (ISC_R_BADBASE64);
return (ISC_R_SUCCESS);
}
static isc_result_t
str_totext(char *source, isc_buffer_t *target) {
unsigned int l;
isc_region_t region;
isc_buffer_available(target, &region);
l = strlen(source);
if (l > region.length)
return (ISC_R_NOSPACE);
memcpy(region.base, source, l);
isc_buffer_add(target, l);
return (ISC_R_SUCCESS);
}
static isc_result_t
mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
isc_region_t tr;
isc_buffer_available(target, &tr);
if (length > tr.length)
return (ISC_R_NOSPACE);
memcpy(tr.base, base, length);
isc_buffer_add(target, length);
return (ISC_R_SUCCESS);
}
static isc_result_t
gettoken(isc_lex_t *lexer, isc_token_t *token, isc_tokentype_t expect,
isc_boolean_t eol)
{
unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
isc_result_t result;
if (expect == isc_tokentype_qstring)
options |= ISC_LEXOPT_QSTRING;
else if (expect == isc_tokentype_number)
options |= ISC_LEXOPT_NUMBER;
result = isc_lex_gettoken(lexer, options, token);
switch (result) {
case ISC_R_SUCCESS:
break;
case ISC_R_NOMEMORY:
return (ISC_R_NOMEMORY);
case ISC_R_NOSPACE:
return (ISC_R_NOSPACE);
default:
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_lex_gettoken() failed: %s\n",
isc_result_totext(result));
return (ISC_R_UNEXPECTED);
}
if (eol && ((token->type == isc_tokentype_eol) ||
(token->type == isc_tokentype_eof)))
return (ISC_R_SUCCESS);
if (token->type == isc_tokentype_string &&
expect == isc_tokentype_qstring)
return (ISC_R_SUCCESS);
if (token->type != expect) {
isc_lex_ungettoken(lexer, token);
if (token->type == isc_tokentype_eol ||
token->type == isc_tokentype_eof)
return (ISC_R_UNEXPECTEDEND);
return (ISC_R_UNEXPECTEDTOKEN);
}
return (ISC_R_SUCCESS);
}