Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix relative include #208

Merged
merged 6 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions src/zone.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
#include <stdlib.h>
#include <limits.h>
#include <stddef.h>
#if _WIN32
# include <direct.h>
#else
# include <unistd.h>
#endif

#include "zone.h"

Expand All @@ -34,6 +39,10 @@ static const char not_a_file[] = "<string>";
#include "config.h"
#include "isadetection.h"

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

#if HAVE_HASWELL
extern int32_t zone_haswell_parse(parser_t *);
#endif
Expand Down Expand Up @@ -140,23 +149,16 @@ static int32_t resolve_path(
// support relative non-rooted paths only
if (*includer && is_relative(include) && !is_rooted(include)) {
assert(!is_relative(includer));
const char *separator = include;
for (const char *p = include; *p; p++)
if (is_separator((unsigned char)*p))
separator = p;
if (separator - include > INT_MAX)
return ZONE_OUT_OF_MEMORY;
char buffer[16];
int offset = (int)(separator - includer);
int length = snprintf(
buffer, sizeof(buffer), "%.*s/%s", offset, includer, include);
buffer, sizeof(buffer), "%s/%s", includer, include);
if (length < 0)
return ZONE_OUT_OF_MEMORY;
char *absolute;
if (!(absolute = malloc(length + 1)))
return ZONE_OUT_OF_MEMORY;
(void)snprintf(
absolute, (size_t)length + 1, "%.*s/%s", offset, includer, include);
absolute, (size_t)length + 1, "%s/%s", includer, include);
*path = _fullpath(NULL, absolute, 0);
free(absolute);
} else {
Expand All @@ -174,20 +176,16 @@ static int32_t resolve_path(
{
if (*includer && *include != '/') {
assert(*includer == '/');
const char *separator = strrchr(includer, '/');
if (separator - include > INT_MAX)
return ZONE_OUT_OF_MEMORY;
char buffer[16];
int offset = (int)(separator - includer);
int length = snprintf(
buffer, sizeof(buffer), "%.*s/%s", offset, includer, include);
buffer, sizeof(buffer), "%s/%s", includer, include);
if (length < 0)
return ZONE_OUT_OF_MEMORY;
char *absolute;
if (!(absolute = malloc((size_t)length + 1)))
return ZONE_OUT_OF_MEMORY;
(void)snprintf(
absolute, (size_t)length + 1, "%.*s/%s", offset, includer, include);
absolute, (size_t)length + 1, "%s/%s", includer, include);
*path = realpath(absolute, NULL);
free(absolute);
} else {
Expand All @@ -212,6 +210,11 @@ static void close_file(
assert(!is_string || file->handle == NULL);
(void)parser;

const bool is_stdin = file->name &&
file->name != not_a_file &&
strcmp(file->name, "-") == 0;
assert(!is_stdin || (!file->handle || file->handle == stdin));

if (file->buffer.data && !is_string)
free(file->buffer.data);
file->buffer.data = NULL;
Expand All @@ -221,7 +224,8 @@ static void close_file(
if (file->path && file->path != not_a_file)
free((char *)file->path);
file->path = NULL;
if (file->handle)
// stdin is not opened, it must not be closed
if (file->handle && file->handle != stdin)
(void)fclose(file->handle);
file->handle = NULL;
}
Expand Down Expand Up @@ -286,14 +290,22 @@ static int32_t open_file(
file->fields.tape[0] = &file->buffer.data[0];
file->fields.tape[1] = &file->buffer.data[0];

const char *includer = "";
if (file != &parser->first)
includer = parser->file->path;
if(strcmp(file->name, "-") == 0) {
if(file == &parser->first && strcmp(file->name, "-") == 0) {
if(!(file->path = strdup(file->name)))
return ZONE_OUT_OF_MEMORY;
} else if ((code = resolve_path(includer, file->name, &file->path)))
return (void)close_file(parser, file), code;
return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY;
} else {
/* The file is resolved relative to the working directory. */
char workdir[PATH_MAX];
#if _WIN32
if(!_getcwd(workdir, sizeof(workdir)))
return (void)close_file(parser, file), ZONE_NOT_A_FILE;
#else
if(!getcwd(workdir, sizeof(workdir)))
return (void)close_file(parser, file), ZONE_NOT_A_FILE;
#endif
if ((code = resolve_path(workdir, file->name, &file->path)))
return (void)close_file(parser, file), code;
}

if(strcmp(file->path, "-") == 0) {
file->handle = stdin;
Expand Down
127 changes: 126 additions & 1 deletion tests/include.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@
#include <stdlib.h>
#include <cmocka.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#if _WIN32
#include <process.h>
#include <direct.h>
#else
#include <unistd.h>
#endif

#include "zone.h"
#include "diagnostic.h"

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

typedef struct input input_t;
struct input {
struct {
Expand Down Expand Up @@ -160,6 +167,14 @@ static char *generate_include(const char *text)
return NULL;
}

diagnostic_push()
msvc_diagnostic_ignored(4996)
static void remove_include(const char *path)
{
unlink(path);
}
diagnostic_pop()

static int32_t parse(
const zone_options_t *options, const char *text, void *user_data)
{
Expand Down Expand Up @@ -451,6 +466,8 @@ void in_too_deep(void **state)
assert_int_equal(code, ZONE_SUCCESS);
assert_int_equal(records, 1u);

remove_include(deep);
remove_include(deeper);
free(inception);
free(deep);
free(deeper);
Expand Down Expand Up @@ -487,8 +504,10 @@ void been_there_done_that(void **state)
int result = fputs(include, handle);
assert_true(result >= 0);
(void)fclose(handle);
free(path);
code = parse(&options, include, &count);

remove_include(path);
free(path);
free(include);
assert_int_equal(code, ZONE_SEMANTIC_ERROR);
}
Expand All @@ -497,3 +516,109 @@ void been_there_done_that(void **state)
// x. test $INCLUDE is denied for files if disabled all together
//

/*!cmocka */
void include_relative(void **state)
{
(void)state;
/* Test with a $INCLUDE from a subdirectory. Is it resolved relative
* to the working directory, and not relative to the includer file. */

zone_parser_t parser;
zone_options_t options;
zone_name_buffer_t name;
zone_rdata_buffer_t rdata;
zone_buffers_t buffers = { 1, &name, &rdata };

memset(&options, 0, sizeof(options));
options.accept.callback = &no_such_file_accept;
options.log.callback = &no_such_file_log;
options.origin.octets = origin;
options.origin.length = sizeof(origin);
options.default_ttl = 3600;
options.default_class = 1;
options.include_limit = 1;

#if _WIN32
int pid = _getpid();
#else
pid_t pid = getpid();
#endif

char* inc1file = "content.inc";
char* inc2file = "example.com.zone";
char dir1[128], dir2[128];
snprintf(dir1, sizeof(dir1), "testdir.1.%d", (int)pid);
snprintf(dir2, sizeof(dir2), "testdir.2.%d", (int)pid);

if(
#if _WIN32
_mkdir(dir1)
#else
mkdir(dir1, 0755)
#endif
!= 0) {
#if _WIN32
printf("mkdir %s failed\n", dir1);
#else
printf("mkdir %s failed: %s\n", dir1, strerror(errno));
#endif
fail();
}
if(
#if _WIN32
_mkdir(dir2)
#else
mkdir(dir2, 0755)
#endif
!= 0) {
#if _WIN32
printf("mkdir %s failed\n", dir2);
#else
printf("mkdir %s failed: %s\n", dir2, strerror(errno));
#endif
fail();
}

char fname1[PATH_MAX], fname2[PATH_MAX];
snprintf(fname1, sizeof(fname1), "%s/%s", dir1, inc1file);
snprintf(fname2, sizeof(fname2), "%s/%s", dir2, inc2file);

FILE* handle = fopen(fname1, "wb");
assert_non_null(handle);
int result = fputs(
"www A 1.2.3.4\n",
handle);
assert_true(result >= 0);
(void)fclose(handle);

FILE* handle2 = fopen(fname2, "wb");
assert_non_null(handle2);
char zonetext[1024+PATH_MAX];
snprintf(zonetext, sizeof(zonetext),
"; perform relative include\n"
"example.com. IN SOA ns host 1 3600 300 7200 3600\n"
"$INCLUDE %s\n"
"mail A 1.2.3.5\n",
fname1);
result = fputs(zonetext, handle2);
assert_true(result >= 0);
(void)fclose(handle2);

no_file_test_t test;
memset(&test, 0, sizeof(test));
int32_t code;
code = zone_parse(&parser, &options, &buffers, fname2, &test);
assert_int_equal(code, ZONE_SUCCESS);
assert_true(test.log_count == 0);
assert_true(test.accept_count == 3);

remove_include(fname1);
remove_include(fname2);
#if _WIN32
(void)_rmdir(dir1);
(void)_rmdir(dir2);
#else
(void)rmdir(dir1);
(void)rmdir(dir2);
#endif
}
5 changes: 4 additions & 1 deletion tests/syntax.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,9 +787,12 @@ void bad_includes(void **state)
int result = fputs(include, handle);
assert_true(result >= 0);
(void)fclose(handle);
free(path);
code = parse(include, &count);

remove_include(path);
free(path);
free(include);

assert_int_equal(code, ZONE_SYNTAX_ERROR);
}

Expand Down