|
|
#include "../../unity/unity.h" |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <stdio.h> |
|
|
#include <unistd.h> |
|
|
#include <sys/types.h> |
|
|
#include <sys/wait.h> |
|
|
#include <fcntl.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void select_base64_mode(void) |
|
|
{ |
|
|
#if BASE_TYPE == 42 |
|
|
|
|
|
base_length = base64_length_wrapper; |
|
|
required_padding = base64_required_padding; |
|
|
isubase = isubase64; |
|
|
base_encode = base64_encode; |
|
|
|
|
|
base_decode_ctx_init = base64_decode_ctx_init_wrapper; |
|
|
base_decode_ctx = base64_decode_ctx_wrapper; |
|
|
base_decode_ctx_finalize = decode_ctx_finalize; |
|
|
|
|
|
has_padding = base64_ctx_has_padding; |
|
|
get_pending_length = base64_ctx_get_pending_length; |
|
|
#else |
|
|
|
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
static int run_do_decode_case(const char *input, size_t input_len, |
|
|
bool ignore_garbage, |
|
|
char **out_buf, size_t *out_len) |
|
|
{ |
|
|
select_base64_mode(); |
|
|
|
|
|
|
|
|
char tmpl[] = "/tmp/basenc_test_XXXXXX"; |
|
|
int fd = mkstemp(tmpl); |
|
|
if (fd < 0) { |
|
|
perror("mkstemp"); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
ssize_t written = 0; |
|
|
while ((size_t)written < input_len) { |
|
|
ssize_t w = write(fd, input + written, input_len - written); |
|
|
if (w < 0) { |
|
|
if (errno == EINTR) continue; |
|
|
perror("write"); |
|
|
close(fd); |
|
|
unlink(tmpl); |
|
|
return -1; |
|
|
} |
|
|
written += w; |
|
|
} |
|
|
if (lseek(fd, 0, SEEK_SET) < 0) { |
|
|
perror("lseek"); |
|
|
close(fd); |
|
|
unlink(tmpl); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
FILE *in_fp = fdopen(fd, "rb"); |
|
|
if (!in_fp) { |
|
|
perror("fdopen"); |
|
|
close(fd); |
|
|
unlink(tmpl); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
|
|
|
int pipefd[2]; |
|
|
if (pipe(pipefd) != 0) { |
|
|
perror("pipe"); |
|
|
fclose(in_fp); |
|
|
unlink(tmpl); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
fflush(stdout); |
|
|
fflush(stderr); |
|
|
pid_t pid = fork(); |
|
|
if (pid < 0) { |
|
|
perror("fork"); |
|
|
fclose(in_fp); |
|
|
close(pipefd[0]); |
|
|
close(pipefd[1]); |
|
|
unlink(tmpl); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
if (pid == 0) { |
|
|
|
|
|
close(pipefd[0]); |
|
|
FILE *out_fp = fdopen(pipefd[1], "wb"); |
|
|
if (!out_fp) { |
|
|
_exit(127); |
|
|
} |
|
|
|
|
|
|
|
|
do_decode(in_fp, tmpl, out_fp, ignore_garbage); |
|
|
|
|
|
|
|
|
_exit(126); |
|
|
} |
|
|
|
|
|
|
|
|
close(pipefd[1]); |
|
|
|
|
|
unlink(tmpl); |
|
|
fclose(in_fp); |
|
|
|
|
|
|
|
|
size_t cap = 1024; |
|
|
size_t used = 0; |
|
|
char *buf = (char *)malloc(cap); |
|
|
if (!buf) { |
|
|
close(pipefd[0]); |
|
|
|
|
|
int st; waitpid(pid, &st, 0); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
for (;;) { |
|
|
if (used == cap) { |
|
|
size_t new_cap = cap * 2; |
|
|
char *new_buf = (char *)realloc(buf, new_cap); |
|
|
if (!new_buf) { |
|
|
free(buf); |
|
|
close(pipefd[0]); |
|
|
int st; waitpid(pid, &st, 0); |
|
|
return -1; |
|
|
} |
|
|
buf = new_buf; |
|
|
cap = new_cap; |
|
|
} |
|
|
ssize_t r = read(pipefd[0], buf + used, cap - used); |
|
|
if (r > 0) { |
|
|
used += (size_t)r; |
|
|
continue; |
|
|
} else if (r == 0) { |
|
|
break; |
|
|
} else { |
|
|
if (errno == EINTR) continue; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
close(pipefd[0]); |
|
|
|
|
|
int status = 0; |
|
|
if (waitpid(pid, &status, 0) < 0) { |
|
|
free(buf); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
*out_buf = buf; |
|
|
*out_len = used; |
|
|
|
|
|
if (WIFEXITED(status)) |
|
|
return WEXITSTATUS(status); |
|
|
else |
|
|
return 128; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
static void test_do_decode_base64_simple_no_padding(void) |
|
|
{ |
|
|
const char *enc = "TWFu"; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(3, out_len); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Man", out, 3); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_base64_with_padding(void) |
|
|
{ |
|
|
const char *enc = "TWE="; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(2, out_len); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_base64_missing_padding_autopad(void) |
|
|
{ |
|
|
const char *enc = "TWE"; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(2, out_len); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_base64_ignore_garbage(void) |
|
|
{ |
|
|
|
|
|
const char *enc = "T?W!E= \n"; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), true, &out, &out_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(2, out_len); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_invalid_char_fails(void) |
|
|
{ |
|
|
const char *enc = "TW?="; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len); |
|
|
TEST_ASSERT_NOT_EQUAL(0, ec); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(0, out_len); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_incomplete_length_error(void) |
|
|
{ |
|
|
|
|
|
const char *enc = "T"; |
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len); |
|
|
TEST_ASSERT_NOT_EQUAL(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(0, out_len); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
static void test_do_decode_large_multiblock(void) |
|
|
{ |
|
|
|
|
|
|
|
|
const int repeats = 2000; |
|
|
size_t enc_len = 4u * repeats; |
|
|
char *enc = (char *)malloc(enc_len); |
|
|
TEST_ASSERT_NOT_NULL(enc); |
|
|
for (int i = 0; i < repeats; i++) { |
|
|
memcpy(enc + 4u * i, "TWFu", 4); |
|
|
} |
|
|
|
|
|
char *out = NULL; |
|
|
size_t out_len = 0; |
|
|
int ec = run_do_decode_case(enc, enc_len, false, &out, &out_len); |
|
|
free(enc); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, ec); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(3u * repeats, out_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_MEMORY("Man", out, 3); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Man", out + (3u * (repeats/2)), 3); |
|
|
TEST_ASSERT_EQUAL_MEMORY("Man", out + out_len - 3, 3); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
|
|
|
RUN_TEST(test_do_decode_base64_simple_no_padding); |
|
|
RUN_TEST(test_do_decode_base64_with_padding); |
|
|
RUN_TEST(test_do_decode_base64_missing_padding_autopad); |
|
|
RUN_TEST(test_do_decode_base64_ignore_garbage); |
|
|
RUN_TEST(test_do_decode_invalid_char_fails); |
|
|
RUN_TEST(test_do_decode_incomplete_length_error); |
|
|
RUN_TEST(test_do_decode_large_multiblock); |
|
|
|
|
|
return UNITY_END(); |
|
|
} |