diff --git a/encoding.c b/encoding.c index 45b2bf9..ef007fb 100644 --- a/encoding.c +++ b/encoding.c @@ -15,31 +15,117 @@ */ #include +#include +#include // For FreeBSD #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif -static const char to_hex[] = "0123456789ABCDEF"; +#define SPACING 63 +#define ENC_CHUNK 8 +#define RAW_CHUNK 5 + +static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ98765-"; +static const char padder[] = " 1234"; +static char reverse32[128]; +static int reverse_init = 0; + +/* Eat 5 bytes from src, write 8 bytes to dest */ +static void +encode_chunk(char *dest, char *src) +{ + unsigned char c; + + *dest++ = base32[(*src & 0xF8) >> 3]; // 1111 1000 first byte + + c = (*src++ & 0x07) << 2; // 0000 0111 first byte + c |= ((*src & 0xC0) >> 6); // 1100 0000 second byte + *dest++ = base32[(int) c]; + + *dest++ = base32[(*src & 0x3E) >> 1]; // 0011 1110 second byte + + c = (*src++ & 0x01) << 4; // 0000 0001 second byte + c |= ((*src & 0xF0) >> 4); // 1111 0000 third byte + *dest++ = base32[(int) c]; + + c = (*src++ & 0x0F) << 1; // 0000 1111 third byte + c |= ((*src & 0x80) >> 7); // 1000 0000 fourth byte + *dest++ = base32[(int) c]; + + *dest++ = base32[(*src & 0x7C) >> 2]; // 0111 1100 fourth byte + + c = (*src++ & 0x03) << 3; // 0000 0011 fourth byte + c |= ((*src & 0xE0) >> 5); // 1110 0000 fifth byte + *dest++ = base32[(int) c]; + + *dest++ = base32[*src++ & 0x1F]; // 0001 1111 fifth byte +} + +/* Eat 8 bytes from src, write 5 bytes to dest */ +static void +decode_chunk(char *dest, char *src) +{ + unsigned char c; + int i; + + if (!reverse_init) { + for (i = 0; i < 32; i++) { + c = base32[i]; + reverse32[(int) c] = i; + } + reverse_init = 1; + } + + c = reverse32[(int) *src++] << 3; // Take bits 11111 from byte 1 + c |= (reverse32[(int) *src] & 0x1C) >> 2; // Take bits 11100 from byte 2 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0x3) << 6; // Take bits 00011 from byte 2 + c |= reverse32[(int) *src++] << 1; // Take bits 11111 from byte 3 + c |= (reverse32[(int) *src] & 0x10) >> 4; // Take bits 10000 from byte 4 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0xF) << 4; // Take bits 01111 from byte 4 + c |= (reverse32[(int) *src] & 0x1E) >> 1; // Take bits 11110 from byte 5 + *dest++ = c; + + c = reverse32[(int) *src++] << 7; // Take bits 00001 from byte 5 + c |= reverse32[(int) *src++] << 2; // Take bits 11111 from byte 6 + c |= (reverse32[(int) *src] & 0x18) >> 3; // Take bits 11000 from byte 7 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0x7) << 5; // Take bits 00111 from byte 7 + c |= reverse32[(int) *src++]; // Take bits 11111 from byte 8 + *dest++ = c; +} int encode_data(char *buf, int len, int space, char *dest, char flag) { int final; int write; + int realwrite; + int chunks; + int leftovers; int i; - int t; - -#define CHUNK 31 -// 31 bytes expands to 62 chars in domain -// We just use hex as encoding right now - - write = space / 2; // use two chars per byte in encoding - write -= (write/CHUNK); // make space for parts + char encoded[255]; + char padding[5]; + char *pp; + char *dp; + char *ep; + space -= space / SPACING; + chunks = (space - 1) / ENC_CHUNK; + while ((chunks + 1) * ENC_CHUNK + 1 > space) { + chunks--; + } + write = RAW_CHUNK * chunks; write = MIN(write, len); // do not use more bytes than is available; final = (write == len); // is this the last block? + chunks = write / RAW_CHUNK; + leftovers = write % RAW_CHUNK; if (flag != 0) { *dest = flag; @@ -49,43 +135,91 @@ encode_data(char *buf, int len, int space, char *dest, char flag) } dest++; + bzero(encoded, sizeof(encoded)); + ep = encoded; + dp = buf; + for (i = 0; i < chunks; i++) { + encode_chunk(ep, dp); + ep += ENC_CHUNK; + dp += RAW_CHUNK; + } + realwrite = ENC_CHUNK * chunks; + bzero(padding, sizeof(padding)); + pp = padding; + if (leftovers) { + pp += RAW_CHUNK - leftovers; + memcpy(pp, dp, leftovers); + + pp = padding; + *ep++ = padder[leftovers]; + encode_chunk(ep, pp); + + realwrite += ENC_CHUNK + 1; // plus padding character + } + ep = encoded; if (len > 0) { - for (i = 0; i < write; i++) { - if (i > 0 && i % CHUNK == 0) { - *dest = '.'; - dest++; + for (i = 1; i <= realwrite; i++) { + if (i % SPACING == 0) { + *dest++ = '.'; } - t = (buf[i] & 0xF0) >> 4; - *dest++ = to_hex[t]; - t = buf[i] & 0x0F; - *dest++ = to_hex[t]; + *dest++ = *ep++; } } + return write; } int decode_data(char *dest, int size, const char *src, char *srcend) { - int r; int len; + int i; + int chunks; + int padded; + char encoded[255]; + char padding[5]; + char *pp; + char *ep; + // Copy flag len = 1; *dest = *src; dest++; src++; + bzero(encoded, sizeof(encoded)); + ep = encoded; while(len < size && src < srcend) { if(*src == '.') { src++; continue; } - sscanf(src, "%02X", &r); - *dest++ = (char)r; - src+=2; - len++; - } + *ep++ = *src++; + } + chunks = strlen(encoded) / 8; + padded = strlen(encoded) % 8; + + ep = encoded; + for (i = 0; i < chunks-1; i++) { + decode_chunk(dest, ep); + dest += RAW_CHUNK; + ep += ENC_CHUNK; + len += RAW_CHUNK; + } + // Read last chunk: + if (padded) { + pp = padding; + padded = *ep++ - '0'; + decode_chunk(pp, ep); + pp += RAW_CHUNK - padded; + memcpy(dest, pp, padded); + len += padded; + } else { + decode_chunk(dest, ep); + len += RAW_CHUNK; + } + return len; }