summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing/base64.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/clplumbing/base64.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/lib/clplumbing/base64.c b/lib/clplumbing/base64.c
new file mode 100644
index 0000000..c8ad325
--- /dev/null
+++ b/lib/clplumbing/base64.c
@@ -0,0 +1,422 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "clplumbing/base64.h"
+
+/*
+ *
+ * Base64 conversion functions.
+ * They convert from a binary array into a single string
+ * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341
+ * The only difference is that we don't care about line lengths.
+ * We do use their encoding algorithm.
+ *
+ */
+
+
+static char b64chars[]
+= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define EQUALS '='
+#define MASK6 (077)
+#define MASK24 (077777777)
+
+
+/* Convert from binary to a base64 string (~ according to RFC1341) */
+int
+binary_to_base64(const void * data, int nbytes, char * output, int outlen)
+{
+ int requiredlen = B64_stringlen(nbytes)+1; /* EOS */
+ char * outptr;
+ const unsigned char * inmax;
+ const unsigned char * inlast;
+ const unsigned char * inptr;
+ int bytesleft;
+
+ if (outlen < requiredlen) {
+ syslog(LOG_ERR, "binary_to_base64: output area too small.");
+ return -1;
+ }
+
+ inptr = data;
+ /* Location of last whole 3-byte chunk */
+ inmax = inptr + ((nbytes / B64inunit)*B64inunit);
+ inlast = inptr + nbytes;
+ outptr = output;
+
+
+ /* Convert whole 3-byte chunks */
+ for (;inptr < inmax; inptr += B64inunit) {
+ unsigned long chunk;
+ unsigned int sixbits;
+
+ chunk = ((*inptr) << 16
+ | ((*(inptr+1)) << 8)
+ | (*(inptr+2))) & MASK24;
+
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk & MASK6);
+ *outptr = b64chars[sixbits]; ++outptr;
+ }
+
+ /* Do we have anything left over? */
+
+ bytesleft = inlast - inptr;
+ if (bytesleft > 0) {
+ /* bytesleft can only be 1 or 2 */
+
+ unsigned long chunk;
+ unsigned int sixbits;
+
+
+ /* Grab first byte */
+ chunk = (*inptr) << 16;
+
+ if (bytesleft == 2) {
+ /* Grab second byte */
+ chunk |= ((*(inptr+1)) << 8);
+ }
+ chunk &= MASK24;
+
+ /* OK, now we have our chunk... */
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ if (bytesleft == 2) {
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits];
+ }else{
+ *outptr = EQUALS;
+ }
+ ++outptr;
+
+ *outptr = EQUALS; ++outptr;
+ }
+ *outptr = EOS; /* Don't increment */
+ return (outptr - output);
+}
+
+
+/* This macro is only usable by the base64_to_binary() function.
+ *
+ * There are faster ways of doing this, but I didn't spend the time
+ * to implement one of them. If we have an array of six bit values,
+ * sized by 256 or so, then we could look it up.
+ * FIXME: This is how it really ought to be done...
+ */
+
+static unsigned char b64values [256];
+#define BADVALUE 0xff
+
+static void
+init_b64_values(void)
+{
+ int j;
+ memset(b64values, BADVALUE, sizeof(b64values));
+
+ for (j=0; b64chars[j] != EOS; ++j) {
+ b64values[(int)b64chars[j]] = (unsigned char)j;
+ }
+}
+
+
+#define Char2SixBits(in, out) { \
+ unsigned char ch; \
+ ch = b64values[(unsigned int)in]; \
+ if (ch == BADVALUE) { \
+ syslog(LOG_ERR \
+ , "base64_to_binary: invalid input [%c]!" \
+ , in); \
+ return -1; \
+ } \
+ out = ch; \
+ } \
+
+
+/* Convert from a base64 string (~ according to RFC1341) to binary */
+int
+base64_to_binary(const char * in, int inlen, void * output, int outlen)
+{
+ int maxbinlen = B64_maxbytelen(inlen); /* Worst case size */
+ const char * input = in;
+ const char * lastinput = in + inlen - B64outunit;
+ int equalcount = 0;
+ unsigned char * startout;
+ unsigned char * out;
+ unsigned sixbits1;
+ unsigned sixbits2;
+ unsigned sixbits3;
+ unsigned sixbits4;
+ unsigned long chunk;
+ static int inityet = 0;
+
+ if (!inityet) {
+ inityet=1;
+ init_b64_values();
+ }
+
+ /* Make sure we have enough room */
+ if (outlen < maxbinlen) {
+ int residue = maxbinlen - outlen;
+
+ if (residue > 2
+ || input[inlen-1] != EQUALS
+ || (residue == 2 && input[inlen-2] != EQUALS)) {
+ syslog(LOG_ERR
+ , "base64_to_binary: output area too small.");
+ return -1;
+ }
+ }
+ if ((inlen % 4) != 0) {
+ syslog(LOG_ERR
+ , "base64_to_binary: input length invalid.");
+ return -1;
+ }
+
+ if (inlen == 0) {
+ return 0;
+ }
+
+ /* We have enough space. We are happy :-) */
+
+ startout = out = (unsigned char *)output;
+
+ while (input < lastinput) {
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+ Char2SixBits(*input, sixbits3); ++input;
+ Char2SixBits(*input, sixbits4); ++input;
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ *out = ((chunk >> 16) & 0xff); ++out;
+ *out = ((chunk >> 8) & 0xff); ++out;
+ *out = (chunk & 0xff); ++out;
+ }
+
+ /* Process last 4 chars of input (1 to 3 bytes of output) */
+
+ /* The first two input chars must be normal chars */
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+
+ /* We should find one of: (char,char) (char,=) or (=,=) */
+ /* We then output: (3 bytes) (2 bytes) (1 byte) */
+
+ if (*input == EQUALS) {
+ /* The (=,=): 1 byte case */
+ equalcount=2;
+ sixbits3 = 0;
+ sixbits4 = 0;
+ /* We assume the 2nd char is an = sign :-) */
+ }else{
+ /* We have either the (char,char) or (char,=) case */
+ Char2SixBits(*input, sixbits3); ++input;
+ if (*input == EQUALS) {
+ /* The (char, =): 2 bytes case */
+ equalcount=1;
+ sixbits4 = 0;
+ }else{
+ /* The (char, char): 3 bytes case */
+ Char2SixBits(*input, sixbits4); ++input;
+ equalcount=0;
+ }
+ }
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ /* We always have one more char to output... */
+ *out = ((chunk >> 16) & 0xff); ++out;
+
+ if (equalcount < 2) {
+ /* Zero or one equal signs: total of 2 or 3 bytes output */
+ *out = ((chunk >> 8) & 0xff); ++out;
+
+ if (equalcount < 1) {
+ /* No equal signs: total of 3 bytes output */
+ *out = (chunk & 0xff); ++out;
+ }
+ }
+
+ return out - startout;
+}
+
+#if 0
+#define RAND(upb) (rand()%(upb))
+
+void dumpbin(void * Bin, int length);
+void randbin(void * Bin, int length);
+
+void
+dumpbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+
+ int j;
+
+ for (j=0; j < length; ++j) {
+ fprintf(stderr, "%02x ", bin[j]);
+ if ((j % 32) == 31) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+void
+randbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+ int j;
+
+ for (j=0; j < length; ++j) {
+ bin[j] = (unsigned char)RAND(256);
+ }
+
+}
+
+#define MAXLEN 320
+#define MAXSTRING B64_stringlen(MAXLEN)+1
+#define MAXITER 300000
+int
+main(int argc, char ** argv)
+{
+ int errcount = 0;
+ char origbin[MAXLEN+1];
+ char sourcebin[MAXLEN+1];
+ char destbin[MAXLEN+1];
+ char deststr[MAXSTRING];
+ int maxiter = MAXITER;
+ int j;
+
+ for (j=0; j < maxiter; ++j) {
+ int iterlen = RAND(MAXLEN+1);
+ int slen;
+ int blen;
+
+ if ((j%100) == 99) {
+ fprintf(stderr, "+");
+ }
+
+ memset(origbin, 0, MAXLEN+1);
+ memset(sourcebin, 0, MAXLEN+1);
+ memset(destbin, 0, MAXLEN+1);
+ randbin(origbin, iterlen);
+ origbin[iterlen] = 0x1;
+ memcpy(sourcebin, origbin, iterlen);
+ sourcebin[iterlen] = 0x2;
+ slen = binary_to_base64(sourcebin, iterlen, deststr, MAXSTRING);
+ if (slen < 0) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length %d\n"
+ , iterlen);
+ ++errcount;
+ continue;
+ }
+ if (strlen(deststr) != slen) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length was %d (strlen) vs %d (ret value)\n"
+ , strlen(deststr), slen);
+ fprintf(stderr, "binlen: %d, deststr: [%s]\n"
+ , iterlen, deststr);
+ continue;
+ ++errcount;
+ }
+ destbin[iterlen] = 0x3;
+ blen = base64_to_binary(deststr, slen, destbin, iterlen);
+
+ if (blen != iterlen) {
+ fprintf(stderr
+ , "base64_to_binary failure: length was %d vs %d\n"
+ , blen, iterlen);
+ dumpbin(origbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ continue;
+ }
+ if (memcmp(destbin, origbin, iterlen) != 0) {
+ fprintf(stderr
+ , "base64_to_binary mismatch. Orig:\n");
+ dumpbin(origbin, iterlen);
+ fprintf(stderr, "Dest:\n");
+ dumpbin(destbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ }
+ if (destbin[iterlen] != 0x3) {
+ fprintf(stderr
+ , "base64_to_binary corruption. dest byte: 0x%02x\n"
+ , destbin[iterlen]);
+ ++errcount;
+ }
+
+ if (sourcebin[iterlen] != 0x2) {
+ fprintf(stderr
+ , "base64_to_binary corruption. source byte: 0x%02x\n"
+ , sourcebin[iterlen]);
+ ++errcount;
+ }
+ sourcebin[iterlen] = 0x0;
+ origbin[iterlen] = 0x0;
+ if (memcmp(sourcebin, origbin, MAXLEN+1) != 0) {
+ fprintf(stderr
+ , "base64_to_binary corruption. origbin:\n");
+ dumpbin(origbin, MAXLEN+1);
+ fprintf(stderr, "sourcebin:\n");
+ dumpbin(sourcebin, MAXLEN+1);
+ ++errcount;
+ }
+
+ }
+
+ fprintf(stderr, "\n%d iterations, %d errors.\n"
+ , maxiter, errcount);
+
+ return errcount;
+}
+/* HA-logging function */
+void
+ha_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s\n", buf);
+
+}
+#endif