diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/.gitignore | 4 | ||||
| -rw-r--r-- | src/Makefile | 21 | ||||
| -rw-r--r-- | src/main.c | 376 | ||||
| -rw-r--r-- | src/msgs/cat_flag | 5 | ||||
| -rw-r--r-- | src/msgs/welcome | 2 | ||||
| -rw-r--r-- | src/patches/flagstore1.diff | 17 | ||||
| -rw-r--r-- | src/patches/flagstore2.diff | 11 | ||||
| -rw-r--r-- | src/stlfile.c | 415 | ||||
| -rw-r--r-- | src/stlfile.h | 54 | ||||
| -rw-r--r-- | src/util.c | 146 | ||||
| -rw-r--r-- | src/util.h | 38 |
11 files changed, 1089 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..5f14e4d --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,4 @@ +stldoctor +*.o +vgcore.* +safe_* diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..d7732b3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,21 @@ +CFLAGS = -g -I . + +# fortify source code +CFLAGS += -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 +LDFLAGS = -Wl,-z,now -Wl,-z,relro + +.PHONY: all clean + +all: build/stldoctor + +clean: + rm -rf build + +build: + mkdir build + +build/%.o: %.c %.h | build + $(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS) + +build/stldoctor: build/$(PREFIX)stlfile.o build/$(PREFIX)util.o $(PREFIX)main.c | build + $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..de2bd48 --- /dev/null +++ b/src/main.c @@ -0,0 +1,376 @@ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <time.h> +#include <errno.h> + +#include "stlfile.h" +#include "util.h" + +#define MAXFILESIZE 7000 + +struct command { + const char *name; + void (*func)(const char *); + const char *desc; +}; + +int save_submission(struct parseinfo *info, char *data, int len); + +void cat_cmd(const char *arg); +void help_cmd(const char *arg); +void exit_cmd(const char *arg); +void echo_cmd(const char *arg); +void upload_cmd(const char *arg); +void search_cmd(const char *arg); +void list_cmd(const char *arg); +void auth_cmd(const char *arg); + +void cleanexit(); + +struct command commands[] = { + { "cat", cat_cmd, "Cat cmd go prrrrr." }, + { "help", help_cmd, "You already know what this does." }, + { "exit", exit_cmd, "Closes the session." }, + { "echo", echo_cmd, "Repeat after me!" }, + { "upload", upload_cmd, "Upload an STL file to analyze." }, + { "search", search_cmd, "Search for an STL file by model name." }, + { "list", list_cmd, "List your uploaded files." }, + { "auth", auth_cmd, "Login to upload files to a private dir." } +}; + +struct parseinfo cached; +char *resultdir; +int echo = 0, loggedin = 0; + +int +save_submission(struct parseinfo *info, char *stldata, int stlsize) +{ + DIR *d; + FILE *f = NULL; + char *dirpath = NULL, *infopath = NULL, *modelpath = NULL; + + if (loggedin) + dirpath = aprintf("%s/.%s-%i", resultdir, info->hash, time(NULL)); + else + dirpath = aprintf("%s/%s-%i", resultdir, info->hash, time(NULL)); + if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO)) goto fail; + + modelpath = aprintf("%s/%s", dirpath, "model"); + if (!(f = fopen(modelpath, "w+"))) goto fail; + if (fwrite(stldata, 1, stlsize, f) != stlsize) goto fail; + fclose(f); + f = NULL; + + infopath = aprintf("%s/%s", dirpath, "info"); + if (!(f = fopen(infopath, "w+"))) goto fail; + if (save_info(info, f) != OK) goto fail; + fclose(f); + f = NULL; + + free(dirpath); + free(modelpath); + free(infopath); + + return OK; + +fail: + if (f) fclose(f); + + if (infopath) remove(infopath); + if (modelpath) remove(modelpath); + if (dirpath) remove(dirpath); + + free(dirpath); + free(modelpath); + free(infopath); + + return FAIL; +} + +void +cat_cmd(const char *arg) +{ + if (arg && !strncmp(arg, "flag", 4)) + dump("msgs/cat_flag"); + else + printf("meow\n"); +} + +void +help_cmd(const char *arg) +{ + int i; + + if (arg) { + for (i = 0; i < ARRSIZE(commands); i++) { + if (!strcmp(commands[i].name, arg)) { + printf("%s\n", commands[i].desc); + return; + } + } + } + + printf("Available commands:\n"); + for (i = 0; i < ARRSIZE(commands); i++) + printf("%s%s", i ? " " : "", commands[i].name); + printf("\n"); +} + +void +exit_cmd(const char *arg) +{ + exit(0); +} + +void +echo_cmd(const char *arg) +{ + echo ^= 1; + printf("Echo is %s\n", echo ? "enabled" : "disabled"); +} + +void +upload_cmd(const char *arg) +{ + const char *bufp; + char *end, *contents; + size_t len; + + bufp = ask("How large is your file? "); + len = strtoul(bufp, &end, 10); + if (len <= 0 || len >= MAXFILESIZE || *end) { + fprintf(stderr, "Invalid file length!\n"); + return; + } + + printf("Ok! Im listening..\n"); + contents = checkp(malloc(len + 1)); + if (fread(contents, 1, len, stdin) != len) { + fprintf(stderr, "Hm, I'm missing some bytes.. try again!\n"); + goto cleanup; + } + contents[len] = '\0'; + + if ((cached.valid = parse_file(&cached, contents, len))) { + if (save_submission(&cached, contents, len) != OK) + fprintf(stderr, "Failed to save your submission!\n"); + else + printf("Your file was saved with ID %s!\n", cached.hash); + } + +cleanup: + free(contents); +} + +void +search_cmd(const char *arg) +{ + char *end, *scandir = NULL, *infopath = NULL, *modelpath = NULL; + int i, which, dirstart, ishidden; + const char *hash, *name; + struct dirent *de; + DIR *d = NULL; + FILE *f = NULL; + size_t size; + + if (arg && !strcmp(arg, "last")) { + if (!cached.valid) { + fprintf(stderr, "No cached info report available\n"); + return; + } + hash = cached.hash; + } else { + hash = mhash(arg ? arg : ask("Model name: "), -1); + } + + if (!(d = opendir(resultdir))) return; + + dirstart = telldir(d); + for (i = 0; (de = readdir(d));) { + name = de->d_name; + if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) + || !loggedin && *name != '.' && !strpfcmp(hash, name)) { + printf("%i : %s\n", i, de->d_name); + i++; + } + } + + if (i == 0) { + fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); + goto cleanup; + } else { + which = strtoul(ask("Which of these results? "), &end, 10); + if (which >= i || which < 0 || *end) { + fprintf(stderr, "Invalid index!\n"); + goto cleanup; + } + } + + seekdir(d, dirstart); + for (i = 0; (de = readdir(d));) { + name = de->d_name; + if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) + || !loggedin && *name != '.' && !strpfcmp(hash, name)) { + if (i == which) { + scandir = aprintf("%s/%s", resultdir, de->d_name); + break; + } + i++; + } + } + + /* file got cleaned up during race condition by background task */ + if (!scandir) { + fprintf(stderr, "Selected result spontaneously combusted!\n"); + goto cleanup; + } + + infopath = aprintf("%s/%s", scandir, "info"); + if (!(f = fopen(infopath, "r"))) goto cleanup; + free_info(&cached); + if (load_info(&cached, f) != OK) goto cleanup; + fclose(f); + f = NULL; + + print_info(&cached); + + if (strchr(ask("Download the model? "), 'y')) { + modelpath = aprintf("%s/%s", scandir, "model"); + if (!(f = fopen(modelpath, "r"))) goto cleanup; + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + if (size > MAXFILESIZE) goto cleanup; + printf("Here you go.. (%liB)\n", size); + while ((i = getc(f)) != EOF) + putc(i, stdout); + fclose(f); + f = NULL; + } + +cleanup: + if (f) fclose(f); + closedir(d); + free(scandir); + free(infopath); + free(modelpath); +} + +void +list_cmd(const char *arg) +{ + struct dirent *de; + struct parseinfo info; + char *path; + FILE *f; + DIR *d; + + if (!loggedin) { + fprintf(stderr, "Not logged in!\n"); + return; + } + + if (!(d = opendir(resultdir))) return; + + while ((de = readdir(d))) { + if (*de->d_name == '.' && !strchr(".", de->d_name[1])) { + printf(">> %s\n", de->d_name); + path = aprintf("%s/%s/info", resultdir, de->d_name); + if ((f = fopen(path, "r"))) { + if (load_info(&info, f) != OK) + fprintf(stderr, "Failed to read saved file info!\n"); + else + print_info(&info); + fclose(f); + } + free(path); + } + } +} + +void +auth_cmd(const char *arg) +{ + const char *hash; + char *ndir; + int ret; + + if (loggedin) { + fprintf(stderr, "Already logged in!\n"); + return; + } + + hash = mhash(arg ? arg : ask("Enter a password: "), -1); + ndir = aprintf("%s/.%s", resultdir, hash); + ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO); + if (!ret) { + printf("Success!\n"); + } else if (ret && errno == EEXIST) { + printf("Success!\nWelcome back!\n"); + } else { + fprintf(stderr, "Auth failed!\n"); + return; + } + + free(resultdir); + resultdir = ndir; + loggedin = 1; + cached.valid = 0; +} + +void +cleanexit() +{ + printf("see you later!\n"); + free_info(&cached); + free(resultdir); +} + +int +main() +{ + const char *cmd; + char *cp, *arg; + int exit, i, cmdlen; + + if (!(resultdir = checkp(strdup(getenv("RESULTDIR"))))) { + fprintf(stderr, "RESULTDIR not defined\n"); + return 1; + } + + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + atexit(cleanexit); + + dump("msgs/welcome"); + + exit = 0; + while (!exit) { + errno = 0; + cmd = ask("$ "); + if (!*cmd && errno == EBADMSG) break; + if (!*cmd) continue; + + cp = strchr(cmd, ' '); + arg = cp ? cp + 1 : NULL; + cmdlen = cp ? cp - cmd : strlen(cmd); + + for (i = 0; i < ARRSIZE(commands); i++) { + if (!strncmp(commands[i].name, cmd, cmdlen) + && cmdlen == strlen(commands[i].name)) { + commands[i].func(arg); + break; + } + } + + if (i == ARRSIZE(commands) && strlen(cmd) != 0) + fprintf(stderr, "No such command!\n"); + } +} diff --git a/src/msgs/cat_flag b/src/msgs/cat_flag new file mode 100644 index 0000000..d29ceea --- /dev/null +++ b/src/msgs/cat_flag @@ -0,0 +1,5 @@ + + /\_/\ [ENO] _ + = u u =_______| \\ + _ w __( \__)) + c_____>__(_____)__, diff --git a/src/msgs/welcome b/src/msgs/welcome new file mode 100644 index 0000000..2f77bfb --- /dev/null +++ b/src/msgs/welcome @@ -0,0 +1,2 @@ +Welcome to STLDoctor! +Submit a stl file and we'll analyze it! diff --git a/src/patches/flagstore1.diff b/src/patches/flagstore1.diff new file mode 100644 index 0000000..f0f8d4a --- /dev/null +++ b/src/patches/flagstore1.diff @@ -0,0 +1,17 @@ +--- a/service/src/safe_util.c ++++ b/service/src/safe_util.c +@@ -78,13 +78,12 @@ void + freadstr(FILE *f, char **dst) + { + size_t start, len, tmp; +- char c; + + /* VULN #1: BAD CAST */ + /* see documentation/README.md for more details */ + + start = ftell(f); +- for (len = 0; (c = fgetc(f)) != EOF && c; len++); ++ for (len = 0; fgetc(f) > 0; len++); + fseek(f, start, SEEK_SET); + + *dst = checkp(calloc(1, len + 1)); diff --git a/src/patches/flagstore2.diff b/src/patches/flagstore2.diff new file mode 100644 index 0000000..b34a0c0 --- /dev/null +++ b/src/patches/flagstore2.diff @@ -0,0 +1,11 @@ +--- a/service/src/safe_util.c ++++ b/service/src/safe_util.c +@@ -58,7 +58,7 @@ mhash(const char *str, int len) + srand(v); + + for (bp = buf, i = 0; i < MHASHLEN / 2; i++) +- bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256)); ++ bp += sprintf(bp, "%02x", (unsigned char) str[i % len] ^ (rand() % 256)); + + return buf; + } diff --git a/src/stlfile.c b/src/stlfile.c new file mode 100644 index 0000000..88fc430 --- /dev/null +++ b/src/stlfile.c @@ -0,0 +1,415 @@ +#include "stlfile.h" + +static const char wsset[] = " \r\n\t"; +static const struct { + int code; + const char *str; +} kwmap[] = { + { KW_SOLID_BEGIN, "solid" }, + { KW_SOLID_END, "endsolid" }, + { KW_LOOP_BEGIN, "outer loop" }, + { KW_LOOP_END, "endloop" }, + { KW_FACET_BEGIN, "facet normal" }, + { KW_FACET_END, "endfacet" }, + { KW_VERTEX, "vertex" }, +}; + +void +stack_init(struct stack *stack) +{ + stack->cap = 10; + stack->data = checkp(malloc(sizeof(int) * stack->cap)); + stack->count = 0; +} + +void +stack_push(struct stack *stack, int v) +{ + if (stack->count == stack->cap) { + stack->cap *= 2; + stack->data = checkp(realloc(stack->data, sizeof(int) * stack->cap)); + } + + stack->data[stack->count] = v; + stack->count++; +} + +int +stack_pop(struct stack *stack) +{ + if (stack->count == 0) + return -1; + + stack->count--; + return stack->data[stack->count]; +} + +void +stack_free(struct stack *stack) +{ + free(stack->data); +} + +int +stack_ind(struct stack *stack, int search) +{ + int i; + for (i = 0; i < stack->count; i++) + if (stack->data[i] == search) + return i; + return -1; +} + +int +isws(char c) +{ + return c && strchr(wsset, c); +} + +char* +skipws(char *p) +{ + for (; isws(*p); p++); + return p; +} + +char* +consume_arg(char **start, char **end) +{ + char *c, *tmp; + + *start = skipws(*start); + if (!*start) return NULL; + for (c = *start; *c && !isws(*c); c++); + tmp = *start; + *start = c + 1; + *end = c; + return tmp; +} + +int +consume_keyword(char **start) +{ + char *bp; + int i, len; + + bp = skipws(*start); + + for (i = 0; i < ARRSIZE(kwmap); i++) { + len = strlen(kwmap[i].str); + if (!strncmp(kwmap[i].str, bp, len) && (!bp[len] || isws(bp[len]))) { + // printf("GOT: %s\n", kwmap[i].str); + *start = bp + len + (bp[len] ? 1 : 0); + return kwmap[i].code; + } + } + + return KW_UNKNOWN; +} + +#define PARSE_FAIL(...) \ + do { fprintf(stderr, "FORMAT ERR: " __VA_ARGS__); goto fail; } while (0) + +int +parse_file_ascii(struct parseinfo *info, char *buf, size_t len) +{ + char *bp, *arg, *prev, *tmp, *end; + struct stack states; + float farg; + int i, kw; + + stack_init(&states); + + info->type = TYPE_ASCII; + info->loopcount = 0; + + memset(info->header, 0, 80); + + for (i = 0; i < 3; i++) { + info->bbmin[i] = INFINITY; + info->bbmax[i] = -INFINITY; + } + + bp = prev = buf; + while ((kw = consume_keyword(&bp))) { + switch (kw) { + case KW_SOLID_BEGIN: + stack_push(&states, STATE_SOLID); + if (stack_ind(&states, KW_SOLID_BEGIN) != -1) + PARSE_FAIL("Multiple nested solids!\n"); + tmp = bp; + if (!consume_keyword(&bp) && (arg = consume_arg(&bp, &end))) { + info->solidname = strndup(arg, end - arg); + } else { + bp = tmp; + } + break; + case KW_SOLID_END: + if ((kw = stack_pop(&states)) != STATE_SOLID) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + tmp = bp; + if (info->solidname && !consume_keyword(&bp) + && (arg = consume_arg(&bp, &end))) { + if (strncmp(info->solidname, arg, end - arg)) + PARSE_FAIL("Solid end/begin names do not match!\n"); + } else { + bp = tmp; + } + break; + case KW_LOOP_BEGIN: + stack_push(&states, STATE_LOOP); + if (stack_ind(&states, KW_LOOP_BEGIN) != -1) + PARSE_FAIL("Multiple nested loops!\n"); + break; + case KW_LOOP_END: + if ((kw = stack_pop(&states)) != STATE_LOOP) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + info->loopcount++; + break; + case KW_FACET_BEGIN: + stack_push(&states, STATE_FACET); + if (stack_ind(&states, KW_LOOP_BEGIN) != -1) + PARSE_FAIL("Multiple nested facets!\n"); + for (i = 0; i < 3; i++) { + if (!(arg = consume_arg(&bp, &end))) + PARSE_FAIL("Facet with less than 3 args!\n"); + farg = strtof(arg, &tmp); + if (!isws(*tmp)) + PARSE_FAIL("Facet with invalid arg '%s'!\n", arg); + } + break; + case KW_FACET_END: + if ((kw = stack_pop(&states)) != STATE_FACET) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + break; + case KW_VERTEX: + for (i = 0; i < 3; i++) { + if (!(arg = consume_arg(&bp, &end))) + PARSE_FAIL("Vertex with less than 3 args!\n"); + farg = strtof(arg, &tmp); + if (!isws(*tmp)) + PARSE_FAIL("Vertex with invalid arg '%s'!\n", arg); + info->bbmin[i] = MIN(info->bbmin[i], farg); + info->bbmax[i] = MAX(info->bbmax[i], farg); + } + break; + case KW_UNKNOWN: + prev = skipws(prev); + PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, prev); + } + prev = bp; + } + + if (states.count) + PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, bp); + + stack_free(&states); + return OK; + +fail: + stack_free(&states); + return FAIL; +} + +int +parse_file_bin(struct parseinfo *info, char *buf, size_t len) +{ + char *bp, *end = buf + len; + int i, k, m; + float v; + + info->type = TYPE_BIN; + + if (len < 84) + PARSE_FAIL("Truncated data! (header missing)\n"); + + memcpy(info->header, buf, 80); + + if (strlen(buf + 1)) + info->solidname = checkp(strdup(buf + 1)); + + bp = buf + 80; + info->loopcount = le32toh(*(uint32_t*)bp); + + if (!info->loopcount) { + memset(info->bbmax, 0, sizeof(float) * 3); + memset(info->bbmin, 0, sizeof(float) * 3); + return OK; + } + + for (i = 0; i < 3; i++) { + info->bbmin[i] = INFINITY; + info->bbmax[i] = -INFINITY; + } + + for (i = 0; i < info->loopcount; i++) { + if (bp + 50 > end) + PARSE_FAIL("Truncated data! (loops missing)\n"); + bp += 12; + for (k = 0; k < 3; k++, bp += 12) { + for (m = 0; m < 3; m++) { + v = fle32toh(*(float*)(bp + 4 * m)); + info->bbmin[m] = MIN(info->bbmin[m], v); + info->bbmax[m] = MAX(info->bbmax[m], v); + } + } + bp += 2; + } + + return OK; + +fail: + return FAIL; +} + +int +parse_file(struct parseinfo *info, char *buf, size_t len) +{ + int status; + const char *resp; + char *bp; + + if (info->valid) free_info(info); + + if (len < 7) { + fprintf(stderr, "File too small!\n"); + return FAIL; + } + + info->filesize = len; + + /* check bin vs ascii with first keyword */ + for (bp = buf; isws(*bp); bp++); + status = !strncmp("solid", bp, 5) && isws(bp[5]) + ? parse_file_ascii(info, buf, len) + : parse_file_bin(info, buf, len); + if (status == FAIL) return FAIL; + + if (!info->solidname) info->solidname = checkp(strdup("")); + + if (!info->modelname) { + resp = ask("Please enter your model name: "); + if (strlen(resp) < 4) { + fprintf(stderr, "Model name is too short!\n"); + return FAIL; + } + info->modelname = checkp(strdup(resp)); + } + + info->hash = checkp(strdup(mhash(info->modelname, -1))); + + return OK; +} + +int +save_info(struct parseinfo *info, FILE *f) +{ + size_t nwrote = 0; + int i; + + nwrote += fwrite(&info->type, sizeof(int), 1, f); + nwrote += fwrite(&info->loopcount, sizeof(int), 1, f); + nwrote += fwrite(&info->filesize, sizeof(unsigned), 1, f); + + for (i = 0; i < 3; i++) { + nwrote += fwrite(&info->bbmin[i], sizeof(float), 1, f); + nwrote += fwrite(&info->bbmax[i], sizeof(float), 1, f); + } + + nwrote += fwrite(info->header, 80, 1, f); + + if (nwrote != 10) return FAIL; + + fputstr(f, info->solidname); + fputstr(f, info->hash); + fputstr(f, info->modelname); + + return OK; +} + +int +load_info(struct parseinfo *info, FILE *f) +{ + size_t nread = 0; + int i; + + nread += fread(&info->type, sizeof(int), 1, f); + nread += fread(&info->loopcount, sizeof(int), 1, f); + nread += fread(&info->filesize, sizeof(unsigned), 1, f); + + for (i = 0; i < 3; i++) { + nread += fread(&info->bbmin[i], sizeof(float), 1, f); + nread += fread(&info->bbmax[i], sizeof(float), 1, f); + } + + nread += fread(info->header, 80, 1, f); + + if (nread != 10) return FAIL; + + freadstr(f, &info->solidname); + freadstr(f, &info->hash); + freadstr(f, &info->modelname); + + info->valid = 1; + + return OK; +} + +void +print_info(struct parseinfo *info) +{ + int i, k; + +#define FILTERCHAR(c) ((c) >= 32 ? (c) : ' ') + + printf(" === Model info === \n"); + + printf(" File Size: %u\n", info->filesize); + + if (info->type == TYPE_BIN) { + printf(" Header:\n"); + for (i = 0; i < 80; i += k) { + printf(" "); + for (k = 0; k < MIN(80 - i, 20); k++) + printf(" %02x", (uint8_t) info->header[i+k]); + printf(" | "); + for (k = 0; k < MIN(80 - i, 20); k++) + printf("%c", FILTERCHAR(info->header[i+k])); + printf("\n"); + } + } + + printf(" Model ID: %s\n", info->hash); + printf(" Model Name: %s\n", info->modelname); + printf(" Solid Name: %s\n", info->solidname); + printf(" Triangle Count: %i\n", info->loopcount); + printf(" Bounding Box Size: %.2f x %.2f x %.2f\n", + info->bbmax[0] - info->bbmin[0], + info->bbmax[1] - info->bbmin[1], + info->bbmax[2] - info->bbmin[2]); + printf(" Bounding Box Origin: %.2f x %.2f x %.2f\n", + info->bbmin[0], info->bbmin[1], info->bbmin[2]); + + printf(" ================== \n"); +} + +void +free_info(struct parseinfo *info) +{ + NULLFREE(info->hash); + NULLFREE(info->modelname); + NULLFREE(info->solidname); + info->valid = 0; +} + +float fle32toh(float v) +{ + union { + uint32_t u; + float f; + } conv; + + conv.f = v; + conv.u = le32toh(conv.u); + return conv.f; +} diff --git a/src/stlfile.h b/src/stlfile.h new file mode 100644 index 0000000..d321282 --- /dev/null +++ b/src/stlfile.h @@ -0,0 +1,54 @@ +#ifndef STLFILE_H +#define STLFILE_H + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <endian.h> +#include <math.h> + +#include "util.h" + +enum { + KW_INVALID = -1, + KW_UNKNOWN, + KW_SOLID_BEGIN, + KW_SOLID_END, + KW_FACET_BEGIN, + KW_FACET_END, + KW_LOOP_BEGIN, + KW_LOOP_END, + KW_VERTEX +}; + +enum { + STATE_SOLID, + STATE_FACET, + STATE_LOOP +}; + +enum { + TYPE_ASCII, + TYPE_BIN +}; + +struct stack { + int *data; + size_t count, cap; +}; + +struct parseinfo { + char header[80], *hash, *modelname, *solidname; + uint32_t loopcount; + unsigned filesize; + float bbmin[3], bbmax[3]; + int type, valid; +}; + +int parse_file(struct parseinfo *info, char *buf, size_t len); +int save_info(struct parseinfo *info, FILE *f); +int load_info(struct parseinfo *info, FILE *f); +void print_info(struct parseinfo *info); +void free_info(struct parseinfo *info); + +#endif /* STLFILE_H */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..ce22c4e --- /dev/null +++ b/src/util.c @@ -0,0 +1,146 @@ +#include "util.h" + +void* +checkp(void *p) +{ + if (!p) die("pointer assertion failed, OOM?\n"); + return p; +} + +void* +die(const char *fmtstr, ...) +{ + va_list ap; + + va_start(ap, fmtstr); + vfprintf(stderr, fmtstr, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +char* +aprintf(const char *fmtstr, ...) +{ + va_list ap, cpy; + size_t nb; + char *str; + + va_copy(cpy, ap); + + va_start(cpy, fmtstr); + nb = vsnprintf(NULL, 0, fmtstr, cpy); + va_end(cpy); + + if (nb <= 0) die("Invalid fmtstr!\n"); + str = checkp(malloc(nb+1)); + + va_start(ap, fmtstr); + nb = vsnprintf(str, nb+1, fmtstr, ap); + va_end(ap); + + return str; +} + +const char* +mhash(const char *str, int len) +{ + static char buf[MHASHLEN + 1]; + int i, k, v; + char c, *bp; + + /* VULN #2: BUFFER OVERFLOW */ + /* see documentation/README.md for more details */ + + if (len == -1) len = strlen(str); + + for (v = 0, i = 0; i < len; i++) v += str[i]; + + srand(v); + for (bp = buf, i = 0; i < MHASHLEN / 2; i++) + bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256)); + + return buf; +} + +int +checkalph(const char *str, const char *alph) +{ + int i; + + for (i = 0; i < strlen(str); i++) + if (str[i] && !strchr(alph, str[i])) return 0; + + return 1; +} + +void +freadstr(FILE *f, char **dst) +{ + size_t start, len, tmp; + char c; + + /* VULN #1: BAD CAST */ + /* see documentation/README.md for more details */ + + start = ftell(f); + for (len = 0; (c = fgetc(f)) != EOF && c; len++); + fseek(f, start, SEEK_SET); + + *dst = checkp(calloc(1, len + 1)); + tmp = fread(*dst, len, 1, f); + fgetc(f); +} + +void +fputstr(FILE *f, char *s) +{ + fprintf(f, "%s", s); + fputc(0, f); +} + +const char* +ask(const char *fmtstr, ...) +{ + static char linebuf[256]; + va_list ap; + int fail; + + va_start(ap, fmtstr); + vprintf(fmtstr, ap); + va_end(ap); + + fail = !fgets(linebuf, sizeof(linebuf), stdin); + + if (!fail && *linebuf) { + if (linebuf[strlen(linebuf)-1] == '\n') + linebuf[strlen(linebuf)-1] = '\0'; + if (echo) printf("%s\n", linebuf); + } + + if (fail) errno = EBADMSG; + + return fail ? "" : linebuf; +} + +void +dump(const char *filename) +{ + char buf[256]; + FILE *f; + int nb; + + if (!(f = fopen(filename, "r"))) return; + + while ((nb = fread(buf, 1, sizeof(buf) - 1, f))) + printf("%.*s\n", nb, buf); + + fclose(f); +} + +int +strpfcmp(const char *prefix, const char *str) +{ + return strncmp(prefix, str, strlen(prefix)); +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..c0e9064 --- /dev/null +++ b/src/util.h @@ -0,0 +1,38 @@ +#ifndef UTIL_H +#define UTIL_H + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#define ARRSIZE(x) (sizeof(x)/sizeof((x)[0])) +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MAX(x,y) ((x) < (y) ? (y) : (x)) + +#define NULLFREE(p) do { free(p); p = NULL; } while (0) + +#define MHASHLEN 40 + +enum { FAIL = 0, OK = 1 }; + +void* checkp(void *p); +void* die(const char *fmtstr, ...); +char* aprintf(const char *fmtstr, ...); + +const char* mhash(const char *filename, int len); +int checkalph(const char *str, const char *alph); + +void freadstr(FILE *f, char **dst); +void fputstr(FILE *f, char *s); + +const char* ask(const char *fmtstr, ...); +void dump(const char *filepath); +int strpfcmp(const char *prefix, const char *str); + +float fle32toh(float v); + +extern int echo; + +#endif /* UTIL_H */ |
