base64.c revision 40f53fa8d9c6a4fc38c0014495e7a42b08f52481
/*
* Copyright (C) 1998-2000 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.18 2000/08/01 01:29:14 tale Exp $ */
#include <config.h>
#include <isc/base64.h>
#include <isc/buffer.h>
#include <isc/lex.h>
#include <isc/string.h>
#include <isc/util.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(const 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,
const 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);
}
isc_result_t
isc_base64_decodestring(isc_mem_t *mctx, char *cstr, isc_buffer_t *target) {
isc_result_t result;
isc_buffer_t source;
isc_lex_t *lex = NULL;
isc_boolean_t isopen = ISC_FALSE;
REQUIRE(mctx != NULL);
REQUIRE(cstr != NULL);
REQUIRE(ISC_BUFFER_VALID(target));
isc_buffer_init(&source, cstr, strlen(cstr));
isc_buffer_add(&source, strlen(cstr));
result = isc_lex_create(mctx, 256, &lex);
if (result == ISC_R_SUCCESS)
result = isc_lex_openbuffer(lex, &source);
if (result == ISC_R_SUCCESS) {
isopen = ISC_TRUE;
result = isc_base64_tobuffer(lex, target, -1);
}
if (isopen)
(void)isc_lex_close(lex);
if (lex != NULL)
isc_lex_destroy(&lex);
return (result);
}
static isc_result_t
str_totext(const char *source, isc_buffer_t *target) {
unsigned int l;
isc_region_t region;
isc_buffer_availableregion(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_availableregion(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",
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);
}