| |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| |
| #include "alloc-util.h" |
| #include "hexdecoct.h" |
| #include "macro.h" |
| #include "string-util.h" |
| #include "util.h" |
| |
| char octchar(int x) { |
| return '0' + (x & 7); |
| } |
| |
| int unoctchar(char c) { |
| |
| if (c >= '0' && c <= '7') |
| return c - '0'; |
| |
| return -EINVAL; |
| } |
| |
| char decchar(int x) { |
| return '0' + (x % 10); |
| } |
| |
| int undecchar(char c) { |
| |
| if (c >= '0' && c <= '9') |
| return c - '0'; |
| |
| return -EINVAL; |
| } |
| |
| char hexchar(int x) { |
| static const char table[16] = "0123456789abcdef"; |
| |
| return table[x & 15]; |
| } |
| |
| int unhexchar(char c) { |
| |
| if (c >= '0' && c <= '9') |
| return c - '0'; |
| |
| if (c >= 'a' && c <= 'f') |
| return c - 'a' + 10; |
| |
| if (c >= 'A' && c <= 'F') |
| return c - 'A' + 10; |
| |
| return -EINVAL; |
| } |
| |
| char *hexmem(const void *p, size_t l) { |
| const uint8_t *x; |
| char *r, *z; |
| |
| z = r = new(char, l * 2 + 1); |
| if (!r) |
| return NULL; |
| |
| for (x = p; x < (const uint8_t*) p + l; x++) { |
| *(z++) = hexchar(*x >> 4); |
| *(z++) = hexchar(*x & 15); |
| } |
| |
| *z = 0; |
| return r; |
| } |
| |
| static int unhex_next(const char **p, size_t *l) { |
| int r; |
| |
| assert(p); |
| assert(l); |
| |
| |
| |
| |
| for (;;) { |
| if (*l == 0) |
| return -EPIPE; |
| |
| if (!strchr(WHITESPACE, **p)) |
| break; |
| |
| |
| (*p)++, (*l)--; |
| } |
| |
| r = unhexchar(**p); |
| if (r < 0) |
| return r; |
| |
| for (;;) { |
| (*p)++, (*l)--; |
| |
| if (*l == 0 || !strchr(WHITESPACE, **p)) |
| break; |
| |
| |
| } |
| |
| return r; |
| } |
| |
| int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) { |
| _cleanup_free_ uint8_t *buf = NULL; |
| const char *x; |
| uint8_t *z; |
| |
| assert(ret); |
| assert(ret_len); |
| assert(p || l == 0); |
| |
| if (l == (size_t) -1) |
| l = strlen(p); |
| |
| |
| buf = malloc((l + 1) / 2 + 1); |
| if (!buf) |
| return -ENOMEM; |
| |
| for (x = p, z = buf;;) { |
| int a, b; |
| |
| a = unhex_next(&x, &l); |
| if (a == -EPIPE) |
| break; |
| if (a < 0) |
| return a; |
| |
| b = unhex_next(&x, &l); |
| if (b < 0) |
| return b; |
| |
| *(z++) = (uint8_t) a << 4 | (uint8_t) b; |
| } |
| |
| *z = 0; |
| |
| *ret_len = (size_t) (z - buf); |
| *ret = TAKE_PTR(buf); |
| |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| char base32hexchar(int x) { |
| static const char table[32] = "0123456789" |
| "ABCDEFGHIJKLMNOPQRSTUV"; |
| |
| return table[x & 31]; |
| } |
| |
| int unbase32hexchar(char c) { |
| unsigned offset; |
| |
| if (c >= '0' && c <= '9') |
| return c - '0'; |
| |
| offset = '9' - '0' + 1; |
| |
| if (c >= 'A' && c <= 'V') |
| return c - 'A' + offset; |
| |
| return -EINVAL; |
| } |
| |
| char *base32hexmem(const void *p, size_t l, bool padding) { |
| char *r, *z; |
| const uint8_t *x; |
| size_t len; |
| |
| assert(p || l == 0); |
| |
| if (padding) |
| |
| len = 8 * (l + 4) / 5; |
| else { |
| |
| len = 8 * l / 5; |
| |
| switch (l % 5) { |
| case 4: |
| len += 7; |
| break; |
| case 3: |
| len += 5; |
| break; |
| case 2: |
| len += 4; |
| break; |
| case 1: |
| len += 2; |
| break; |
| } |
| } |
| |
| z = r = malloc(len + 1); |
| if (!r) |
| return NULL; |
| |
| for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { |
| |
| |
| *(z++) = base32hexchar(x[0] >> 3); |
| *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); |
| *(z++) = base32hexchar((x[1] & 63) >> 1); |
| *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); |
| *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); |
| *(z++) = base32hexchar((x[3] & 127) >> 2); |
| *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); |
| *(z++) = base32hexchar((x[4] & 31)); |
| } |
| |
| switch (l % 5) { |
| case 4: |
| *(z++) = base32hexchar(x[0] >> 3); |
| *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); |
| *(z++) = base32hexchar((x[1] & 63) >> 1); |
| *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); |
| *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); |
| *(z++) = base32hexchar((x[3] & 127) >> 2); |
| *(z++) = base32hexchar((x[3] & 3) << 3); |
| if (padding) |
| *(z++) = '='; |
| |
| break; |
| |
| case 3: |
| *(z++) = base32hexchar(x[0] >> 3); |
| *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); |
| *(z++) = base32hexchar((x[1] & 63) >> 1); |
| *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); |
| *(z++) = base32hexchar((x[2] & 15) << 1); |
| if (padding) { |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| } |
| |
| break; |
| |
| case 2: |
| *(z++) = base32hexchar(x[0] >> 3); |
| *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); |
| *(z++) = base32hexchar((x[1] & 63) >> 1); |
| *(z++) = base32hexchar((x[1] & 1) << 4); |
| if (padding) { |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| } |
| |
| break; |
| |
| case 1: |
| *(z++) = base32hexchar(x[0] >> 3); |
| *(z++) = base32hexchar((x[0] & 7) << 2); |
| if (padding) { |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| *(z++) = '='; |
| } |
| |
| break; |
| } |
| |
| *z = 0; |
| return r; |
| } |
| |
| int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { |
| _cleanup_free_ uint8_t *r = NULL; |
| int a, b, c, d, e, f, g, h; |
| uint8_t *z; |
| const char *x; |
| size_t len; |
| unsigned pad = 0; |
| |
| assert(p || l == 0); |
| assert(mem); |
| assert(_len); |
| |
| if (l == (size_t) -1) |
| l = strlen(p); |
| |
| |
| if (padding && l % 8 != 0) |
| return -EINVAL; |
| |
| if (padding) { |
| |
| while (l > 0 && p[l - 1] == '=' && pad < 7) { |
| pad++; |
| l--; |
| } |
| } |
| |
| |
| |
| len = (l / 8) * 5; |
| |
| switch (l % 8) { |
| case 7: |
| len += 4; |
| break; |
| case 5: |
| len += 3; |
| break; |
| case 4: |
| len += 2; |
| break; |
| case 2: |
| len += 1; |
| break; |
| case 0: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| z = r = malloc(len + 1); |
| if (!r) |
| return -ENOMEM; |
| |
| for (x = p; x < p + (l / 8) * 8; x += 8) { |
| |
| |
| a = unbase32hexchar(x[0]); |
| if (a < 0) |
| return -EINVAL; |
| |
| b = unbase32hexchar(x[1]); |
| if (b < 0) |
| return -EINVAL; |
| |
| c = unbase32hexchar(x[2]); |
| if (c < 0) |
| return -EINVAL; |
| |
| d = unbase32hexchar(x[3]); |
| if (d < 0) |
| return -EINVAL; |
| |
| e = unbase32hexchar(x[4]); |
| if (e < 0) |
| return -EINVAL; |
| |
| f = unbase32hexchar(x[5]); |
| if (f < 0) |
| return -EINVAL; |
| |
| g = unbase32hexchar(x[6]); |
| if (g < 0) |
| return -EINVAL; |
| |
| h = unbase32hexchar(x[7]); |
| if (h < 0) |
| return -EINVAL; |
| |
| *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; |
| *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; |
| *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; |
| *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; |
| *(z++) = (uint8_t) g << 5 | (uint8_t) h; |
| } |
| |
| switch (l % 8) { |
| case 7: |
| a = unbase32hexchar(x[0]); |
| if (a < 0) |
| return -EINVAL; |
| |
| b = unbase32hexchar(x[1]); |
| if (b < 0) |
| return -EINVAL; |
| |
| c = unbase32hexchar(x[2]); |
| if (c < 0) |
| return -EINVAL; |
| |
| d = unbase32hexchar(x[3]); |
| if (d < 0) |
| return -EINVAL; |
| |
| e = unbase32hexchar(x[4]); |
| if (e < 0) |
| return -EINVAL; |
| |
| f = unbase32hexchar(x[5]); |
| if (f < 0) |
| return -EINVAL; |
| |
| g = unbase32hexchar(x[6]); |
| if (g < 0) |
| return -EINVAL; |
| |
| |
| if (g & 7) |
| return -EINVAL; |
| |
| *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; |
| *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; |
| *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; |
| *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; |
| |
| break; |
| case 5: |
| a = unbase32hexchar(x[0]); |
| if (a < 0) |
| return -EINVAL; |
| |
| b = unbase32hexchar(x[1]); |
| if (b < 0) |
| return -EINVAL; |
| |
| c = unbase32hexchar(x[2]); |
| if (c < 0) |
| return -EINVAL; |
| |
| d = unbase32hexchar(x[3]); |
| if (d < 0) |
| return -EINVAL; |
| |
| e = unbase32hexchar(x[4]); |
| if (e < 0) |
| return -EINVAL; |
| |
| |
| if (e & 1) |
| return -EINVAL; |
| |
| *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; |
| *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; |
| *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; |
| |
| break; |
| case 4: |
| a = unbase32hexchar(x[0]); |
| if (a < 0) |
| return -EINVAL; |
| |
| b = unbase32hexchar(x[1]); |
| if (b < 0) |
| return -EINVAL; |
| |
| c = unbase32hexchar(x[2]); |
| if (c < 0) |
| return -EINVAL; |
| |
| d = unbase32hexchar(x[3]); |
| if (d < 0) |
| return -EINVAL; |
| |
| |
| if (d & 15) |
| return -EINVAL; |
| |
| *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; |
| *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; |
| |
| break; |
| case 2: |
| a = unbase32hexchar(x[0]); |
| if (a < 0) |
| return -EINVAL; |
| |
| b = unbase32hexchar(x[1]); |
| if (b < 0) |
| return -EINVAL; |
| |
| |
| if (b & 3) |
| return -EINVAL; |
| |
| *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; |
| |
| break; |
| case 0: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| *z = 0; |
| |
| *mem = TAKE_PTR(r); |
| *_len = len; |
| |
| return 0; |
| } |
| |
| |
| char base64char(int x) { |
| static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz" |
| "0123456789+/"; |
| return table[x & 63]; |
| } |
| |
| int unbase64char(char c) { |
| unsigned offset; |
| |
| if (c >= 'A' && c <= 'Z') |
| return c - 'A'; |
| |
| offset = 'Z' - 'A' + 1; |
| |
| if (c >= 'a' && c <= 'z') |
| return c - 'a' + offset; |
| |
| offset += 'z' - 'a' + 1; |
| |
| if (c >= '0' && c <= '9') |
| return c - '0' + offset; |
| |
| offset += '9' - '0' + 1; |
| |
| if (c == '+') |
| return offset; |
| |
| offset++; |
| |
| if (c == '/') |
| return offset; |
| |
| return -EINVAL; |
| } |
| |
| ssize_t base64mem(const void *p, size_t l, char **out) { |
| char *r, *z; |
| const uint8_t *x; |
| |
| assert(p || l == 0); |
| assert(out); |
| |
| |
| z = r = malloc(4 * (l + 2) / 3 + 1); |
| if (!r) |
| return -ENOMEM; |
| |
| for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { |
| |
| *(z++) = base64char(x[0] >> 2); |
| *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); |
| *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); |
| *(z++) = base64char(x[2] & 63); |
| } |
| |
| switch (l % 3) { |
| case 2: |
| *(z++) = base64char(x[0] >> 2); |
| *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); |
| *(z++) = base64char((x[1] & 15) << 2); |
| *(z++) = '='; |
| |
| break; |
| case 1: |
| *(z++) = base64char(x[0] >> 2); |
| *(z++) = base64char((x[0] & 3) << 4); |
| *(z++) = '='; |
| *(z++) = '='; |
| |
| break; |
| } |
| |
| *z = 0; |
| *out = r; |
| return z - r; |
| } |
| |
| static int base64_append_width( |
| char **prefix, int plen, |
| const char *sep, int indent, |
| const void *p, size_t l, |
| int width) { |
| |
| _cleanup_free_ char *x = NULL; |
| char *t, *s; |
| ssize_t slen, len, avail; |
| int line, lines; |
| |
| len = base64mem(p, l, &x); |
| if (len <= 0) |
| return len; |
| |
| lines = DIV_ROUND_UP(len, width); |
| |
| slen = strlen_ptr(sep); |
| t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); |
| if (!t) |
| return -ENOMEM; |
| |
| memcpy_safe(t + plen, sep, slen); |
| |
| for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { |
| int act = MIN(width, avail); |
| |
| if (line > 0 || sep) { |
| memset(s, ' ', indent); |
| s += indent; |
| } |
| |
| memcpy(s, x + width * line, act); |
| s += act; |
| *(s++) = line < lines - 1 ? '\n' : '\0'; |
| avail -= act; |
| } |
| assert(avail == 0); |
| |
| *prefix = t; |
| return 0; |
| } |
| |
| int base64_append( |
| char **prefix, int plen, |
| const void *p, size_t l, |
| int indent, int width) { |
| |
| if (plen > width / 2 || plen + indent > width) |
| |
| return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); |
| else |
| |
| return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); |
| } |
| |
| static int unbase64_next(const char **p, size_t *l) { |
| int ret; |
| |
| assert(p); |
| assert(l); |
| |
| |
| |
| |
| for (;;) { |
| if (*l == 0) |
| return -EPIPE; |
| |
| if (!strchr(WHITESPACE, **p)) |
| break; |
| |
| |
| (*p)++, (*l)--; |
| } |
| |
| if (**p == '=') |
| ret = INT_MAX; |
| else { |
| ret = unbase64char(**p); |
| if (ret < 0) |
| return ret; |
| } |
| |
| for (;;) { |
| (*p)++, (*l)--; |
| |
| if (*l == 0) |
| break; |
| if (!strchr(WHITESPACE, **p)) |
| break; |
| |
| |
| } |
| |
| return ret; |
| } |
| |
| int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { |
| _cleanup_free_ uint8_t *buf = NULL; |
| const char *x; |
| uint8_t *z; |
| size_t len; |
| |
| assert(p || l == 0); |
| assert(ret); |
| assert(ret_size); |
| |
| if (l == (size_t) -1) |
| l = strlen(p); |
| |
| |
| |
| len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); |
| |
| buf = malloc(len + 1); |
| if (!buf) |
| return -ENOMEM; |
| |
| for (x = p, z = buf;;) { |
| int a, b, c, d; |
| |
| a = unbase64_next(&x, &l); |
| if (a == -EPIPE) |
| break; |
| if (a < 0) |
| return a; |
| if (a == INT_MAX) |
| return -EINVAL; |
| |
| b = unbase64_next(&x, &l); |
| if (b < 0) |
| return b; |
| if (b == INT_MAX) |
| return -EINVAL; |
| |
| c = unbase64_next(&x, &l); |
| if (c < 0) |
| return c; |
| |
| d = unbase64_next(&x, &l); |
| if (d < 0) |
| return d; |
| |
| if (c == INT_MAX) { |
| |
| if (d != INT_MAX) |
| return -EINVAL; |
| |
| |
| if (b & 15) |
| return -EINVAL; |
| |
| if (l > 0) |
| return -ENAMETOOLONG; |
| |
| *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); |
| break; |
| } |
| |
| if (d == INT_MAX) { |
| |
| if (c & 3) |
| return -EINVAL; |
| |
| if (l > 0) |
| return -ENAMETOOLONG; |
| |
| *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; |
| *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; |
| break; |
| } |
| |
| *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; |
| *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; |
| *(z++) = (uint8_t) c << 6 | (uint8_t) d; |
| } |
| |
| *z = 0; |
| |
| *ret_size = (size_t) (z - buf); |
| *ret = TAKE_PTR(buf); |
| |
| return 0; |
| } |
| |
| void hexdump(FILE *f, const void *p, size_t s) { |
| const uint8_t *b = p; |
| unsigned n = 0; |
| |
| assert(b || s == 0); |
| |
| if (!f) |
| f = stdout; |
| |
| while (s > 0) { |
| size_t i; |
| |
| fprintf(f, "%04x ", n); |
| |
| for (i = 0; i < 16; i++) { |
| |
| if (i >= s) |
| fputs(" ", f); |
| else |
| fprintf(f, "%02x ", b[i]); |
| |
| if (i == 7) |
| fputc(' ', f); |
| } |
| |
| fputc(' ', f); |
| |
| for (i = 0; i < 16; i++) { |
| |
| if (i >= s) |
| fputc(' ', f); |
| else |
| fputc(isprint(b[i]) ? (char) b[i] : '.', f); |
| } |
| |
| fputc('\n', f); |
| |
| if (s < 16) |
| break; |
| |
| n += 16; |
| b += 16; |
| s -= 16; |
| } |
| } |