Skip to content
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
30 changes: 26 additions & 4 deletions ipthelper/ip6tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ static const unsigned int inverse_for_options[NUMBER_OF_OPT] =
0,
};

#define opts iptables_globals.opts
#define prog_name iptables_globals.program_name
#define prog_vers iptables_globals.program_version
/* A few hardcoded protocols for 'all' and in case the user has no
Expand Down Expand Up @@ -1439,10 +1438,27 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
demand-load a protocol. */
opterr = 0;

opts = xt_params->orig_opts;
/* Create a malloc'd copy of orig_opts */
if (iptables_globals.opts == NULL) {
size_t num_opts = 0;
struct option *orig_opts = iptables_globals.orig_opts;

/* Count the number of options (including the NULL terminator) */
while (orig_opts[num_opts].name != NULL) {
num_opts++;
}
num_opts++; /* Include the NULL terminator */

/* Allocate memory and copy the options */
iptables_globals.opts = malloc(num_opts * sizeof(struct option));
if (iptables_globals.opts == NULL) {
xtables_error(OTHER_PROBLEM, "malloc failed for options array");
}
memcpy(iptables_globals.opts, iptables_globals.orig_opts, num_opts * sizeof(struct option));
}
while ((cs.c = getopt_long(argc, argv,
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:g:46",
opts, NULL)) != -1) {
iptables_globals.opts?: iptables_globals.orig_opts, NULL)) != -1) {
switch (cs.c) {
/*
* Command selection
Expand Down Expand Up @@ -1774,7 +1790,7 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
continue;
break;
}
cs.invert = 2;
cs.invert = FALSE;
}

for (matchp = cs.matches; matchp; matchp = matchp->next)
Expand Down Expand Up @@ -2017,5 +2033,11 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
free(dmasks);
xtables_free_opts(1);

/* Free the malloc'd copy of opts if it was allocated */
if (iptables_globals.opts != iptables_globals.orig_opts) {
free(iptables_globals.opts);
iptables_globals.opts = NULL;
}

return ret;
}
28 changes: 26 additions & 2 deletions ipthelper/iptables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1017,10 +1017,28 @@ int do_command4(int argc, char *argv[], char **table, void **handle)
demand-load a protocol. */
opterr = 0;

iptables_globals.opts = iptables_globals.orig_opts;
/* Create a malloc'd copy of orig_opts */
if (iptables_globals.opts == NULL) {
size_t num_opts = 0;
struct option *orig_opts = iptables_globals.orig_opts;

/* Count the number of options (including the NULL terminator) */
while (orig_opts[num_opts].name != NULL) {
num_opts++;
}
num_opts++; /* Include the NULL terminator */

/* Allocate memory and copy the options */
iptables_globals.opts = malloc(num_opts * sizeof(struct option));
if (iptables_globals.opts == NULL) {
xtables_error(OTHER_PROBLEM, "malloc failed for options array");
}
memcpy(iptables_globals.opts, iptables_globals.orig_opts, num_opts * sizeof(struct option));
}
while ((cs.c = getopt_long(argc, argv,
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46",
iptables_globals.opts, NULL)) != -1) {
iptables_globals.opts?: iptables_globals.orig_opts,
NULL)) != -1) {
switch (cs.c) {
/*
* Command selection
Expand Down Expand Up @@ -1523,5 +1541,11 @@ int do_command4(int argc, char *argv[], char **table, void **handle)
free(dmasks);
//xtables_free_opts(1);

/* Free the malloc'd copy of opts if it was allocated */
if (iptables_globals.opts != iptables_globals.orig_opts) {
free(iptables_globals.opts);
iptables_globals.opts = NULL;
}

return ret;
}
202 changes: 184 additions & 18 deletions ipthelper/xshared.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif

#include <ctype.h>
#include <getopt.h>
#include <errno.h>
Expand All @@ -14,10 +18,89 @@
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(__unix__) || defined(__APPLE__)
#include <dlfcn.h>
#endif
#include "xtables.h"
#include <math.h>
#include "xshared.h"

#define XS_LONGOPTS_SCAN_LIMIT 4096U

#if defined(__unix__) || defined(__APPLE__)
#define XS_HAVE_DLADDR 1
#else
#define XS_HAVE_DLADDR 0
#endif

static size_t xs_longopts_count(const struct option *opts, const char *ext_name)
{
size_t i;

if (opts == NULL)
return 0;

for (i = 0; i < XS_LONGOPTS_SCAN_LIMIT; ++i) {
if (opts[i].name == NULL)
return i;
}

if (ext_name != NULL)
xtables_error(OTHER_PROBLEM,
"Extension \"%s\" returned an unterminated option table.",
ext_name);

xtables_error(OTHER_PROBLEM,
"xtables option table is missing its terminator.");
return 0;
}

#if XS_HAVE_DLADDR
static bool xs_option_name_pointer_is_valid(const char *name)
{
Dl_info info;

if (name == NULL)
return true;

return dladdr((const void *)name, &info) != 0;
}
#else
static bool xs_option_name_pointer_is_valid(const char *name)
{
if (name == NULL)
return true;

#if INTPTR_MAX > 0xffffffff
if ((uintptr_t)name < 0x100000000ULL)
return false;
#endif
return true;
}
#endif

static void xs_validate_new_longopts(struct option *opts, size_t start,
const char *ext_name)
{
size_t total;
size_t i;

if (opts == NULL || ext_name == NULL)
return;

total = xs_longopts_count(opts, ext_name);
if (start >= total)
return;

for (i = start; i < total; ++i) {
if (!xs_option_name_pointer_is_valid(opts[i].name)) {
xtables_error(OTHER_PROBLEM,
"Extension \"%s\" was built against an incompatible libxtables release (detected corrupt option metadata). Please rebuild the module.",
ext_name);
}
}
}

/*
* Print out any special helps. A user might like to be able to add a --help
* to the commandline, and see expected results. So we call help for all
Expand Down Expand Up @@ -146,6 +229,7 @@ int command_default(struct iptables_command_state *cs,
m = load_proto(cs);
if (m != NULL) {
size_t size;
size_t merge_start;

cs->proto_used = 1;

Expand All @@ -157,18 +241,22 @@ int command_default(struct iptables_command_state *cs,
m->m->u.user.revision = m->revision;
xs_init_match(m);

merge_start = xs_longopts_count(gl->opts, NULL);
if (m->x6_options != NULL)
gl->opts = xtables_options_xfrm(gl->orig_opts,
gl->opts,
m->x6_options,
&m->option_offset);
else
gl->opts = xtables_merge_options(gl->orig_opts,
gl->opts,
m->extra_opts,
&m->option_offset);
gl->opts = xs_merge_options(gl->orig_opts,
gl->opts,
m->extra_opts,
&m->option_offset);
if (gl->opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xs_validate_new_longopts(gl->opts, merge_start,
m->real_name != NULL ?
m->real_name : m->name);
optind--;
/* Indicate to rerun getopt *immediately* */
return 1;
Expand Down Expand Up @@ -564,18 +652,26 @@ void command_match(struct iptables_command_state *cs)
if (m == m->next)
return;
/* Merge options for non-cloned matches */
if (m->x6_options != NULL){
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
{
size_t merge_start = xs_longopts_count(opts, NULL);

if (m->x6_options != NULL) {
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
m->x6_options, &m->option_offset);
int num_orig;
for (num_orig = 0; opts[num_orig].name != NULL; ++num_orig) {}
} else if (m->extra_opts != NULL) {
opts = xs_merge_options(xt_params->orig_opts, opts,
m->extra_opts, &m->option_offset);

} else
return;

if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");

xs_validate_new_longopts(opts, merge_start,
m->real_name != NULL ?
m->real_name : m->name);
}
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xt_params->orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
}

Expand Down Expand Up @@ -628,15 +724,85 @@ void command_jump(struct iptables_command_state *cs)
cs->target->t->u.user.revision = cs->target->revision;
xs_init_target(cs->target);

if (cs->target->x6_options != NULL)
size_t merge_start = xs_longopts_count(opts, NULL);

if (cs->target->x6_options != NULL) {
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(xt_params->orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
} else if (cs->target->extra_opts != NULL)
opts = xs_merge_options(xt_params->orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
else
return;
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xs_validate_new_longopts(opts, merge_start,
cs->target->real_name != NULL ?
cs->target->real_name : cs->jumpto);
xt_params->opts = opts;
}


struct option *
xs_merge_options(struct option *orig_opts, struct option *oldopts,
const struct option *newopts, unsigned int *option_offset)
{
unsigned int num_orig = 0, num_old = 0, num_new = 0, i;
struct option *merge;
struct option *mp;
struct option *old_base = oldopts;

if (newopts == NULL || newopts->name == NULL)
return oldopts;

if (orig_opts != NULL)
for (; orig_opts[num_orig].name != NULL; ++num_orig)
;

if (oldopts != NULL)
for (; oldopts[num_old].name != NULL; ++num_old)
;

for (; newopts[num_new].name != NULL; ++num_new)
;

if (oldopts != NULL && num_old >= num_orig) {
oldopts += num_orig;
num_old -= num_orig;
} else {
oldopts = NULL;
num_old = 0;
}

merge = malloc(sizeof(*merge) * (num_orig + num_new + num_old + 1));
if (merge == NULL)
return NULL;

if (num_orig != 0)
memcpy(merge, orig_opts, sizeof(*merge) * num_orig);
mp = merge + num_orig;

xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
*option_offset = xt_params->option_offset;

for (i = 0; i < num_new; ++i, ++mp) {
mp->name = newopts[i].name;
mp->has_arg = newopts[i].has_arg;
mp->flag = newopts[i].flag;
mp->val = newopts[i].val + *option_offset;
}

if (oldopts != NULL && num_old != 0) {
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
}

memset(mp, 0, sizeof(*mp));

if (old_base != NULL && old_base != orig_opts && old_base != xt_params->orig_opts)
free(old_base);

return merge;
}
4 changes: 4 additions & 0 deletions ipthelper/xshared.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,9 @@ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
void command_match(struct iptables_command_state *cs);
const char *xt_parse_target(const char *targetname);
void command_jump(struct iptables_command_state *cs);
struct option *xs_merge_options(struct option *orig_opts,
struct option *oldopts,
const struct option *newopts,
unsigned int *option_offset);

#endif /* IPTABLES_XSHARED_H */
3 changes: 2 additions & 1 deletion ipthelper/xtoptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
}
xtables_free_opts(0);
if (xt_params->opts != NULL && xt_params->opts != xt_params->orig_opts)
xtables_free_opts(0);

/* Clear trailing entry */
memset(mp, 0, sizeof(*mp));
Expand Down
Loading