diff options
-rw-r--r-- | Make_global.am | 2 | ||||
-rw-r--r-- | include/libipset/Makefile.am | 2 | ||||
-rw-r--r-- | include/libipset/ipset.h | 90 | ||||
-rw-r--r-- | include/libipset/parse.h | 2 | ||||
-rw-r--r-- | include/libipset/session.h | 36 | ||||
-rw-r--r-- | include/libipset/ui.h | 56 | ||||
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/ipset.c | 1472 | ||||
-rw-r--r-- | lib/ipset_hash_ipport.c | 2 | ||||
-rw-r--r-- | lib/ipset_hash_ipportip.c | 2 | ||||
-rw-r--r-- | lib/ipset_hash_ipportnet.c | 2 | ||||
-rw-r--r-- | lib/ipset_hash_netiface.c | 2 | ||||
-rw-r--r-- | lib/ipset_hash_netport.c | 2 | ||||
-rw-r--r-- | lib/ipset_hash_netportnet.c | 2 | ||||
-rw-r--r-- | lib/libipset.3 | 242 | ||||
-rw-r--r-- | lib/libipset.map | 21 | ||||
-rw-r--r-- | lib/mnl.c | 2 | ||||
-rw-r--r-- | lib/parse.c | 34 | ||||
-rw-r--r-- | lib/print.c | 2 | ||||
-rw-r--r-- | lib/session.c | 316 | ||||
-rw-r--r-- | lib/ui.c | 42 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/ipset.c | 889 |
23 files changed, 2160 insertions, 1066 deletions
diff --git a/Make_global.am b/Make_global.am index f695c4d..e666986 100644 --- a/Make_global.am +++ b/Make_global.am @@ -69,7 +69,7 @@ # interface. # curr:rev:age -LIBVERSION = 12:0:1 +LIBVERSION = 13:0:0 AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include diff --git a/include/libipset/Makefile.am b/include/libipset/Makefile.am index 79a1357..c7f7b2b 100644 --- a/include/libipset/Makefile.am +++ b/include/libipset/Makefile.am @@ -16,7 +16,7 @@ pkginclude_HEADERS = \ session.h \ transport.h \ types.h \ - ui.h \ + ipset.h \ utils.h EXTRA_DIST = debug.h icmp.h icmpv6.h diff --git a/include/libipset/ipset.h b/include/libipset/ipset.h new file mode 100644 index 0000000..7eba74e --- /dev/null +++ b/include/libipset/ipset.h @@ -0,0 +1,90 @@ +/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef LIBIPSET_IPSET_H +#define LIBIPSET_IPSET_H + +#include <stdbool.h> /* bool */ +#include <libipset/linux_ip_set.h> /* enum ipset_cmd */ +#include <libipset/session.h> /* ipset_session_* */ +#include <libipset/types.h> /* ipset_load_types */ + +#define IPSET_CMD_ALIASES 3 + +/* Commands in userspace */ +struct ipset_commands { + enum ipset_cmd cmd; + int has_arg; + const char *name[IPSET_CMD_ALIASES]; + const char *help; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct ipset_commands ipset_commands[]; + +struct ipset_session; +struct ipset_data; +struct ipset; + + +/* Environment options */ +struct ipset_envopts { + int flag; + int has_arg; + const char *name[2]; + const char *help; + int (*parse)(struct ipset *ipset, int flag, const char *str); + int (*print)(char *buf, unsigned int len, + const struct ipset_data *data, int flag, uint8_t env); +}; + +extern const struct ipset_envopts ipset_envopts[]; + +extern bool ipset_match_cmd(const char *arg, const char * const name[]); +extern bool ipset_match_option(const char *arg, const char * const name[]); +extern bool ipset_match_envopt(const char *arg, const char * const name[]); +extern void ipset_port_usage(void); +extern int ipset_parse_filename(struct ipset *ipset, int opt, const char *str); +extern int ipset_parse_output(struct ipset *ipset, + int opt, const char *str); +extern int ipset_envopt_parse(struct ipset *ipset, + int env, const char *str); + +enum ipset_exittype { + IPSET_NO_PROBLEM = 0, + IPSET_OTHER_PROBLEM, + IPSET_PARAMETER_PROBLEM, + IPSET_VERSION_PROBLEM, + IPSET_SESSION_PROBLEM, +}; + +typedef int (*ipset_custom_errorfn)(struct ipset *ipset, void *p, + int status, const char *msg, ...) + __attribute__ ((format (printf, 4, 5))); +typedef int (*ipset_standard_errorfn)(struct ipset *ipset, void *p); + +extern struct ipset_session * ipset_session(struct ipset *ipset); +extern bool ipset_is_interactive(struct ipset *ipset); +extern int ipset_custom_printf(struct ipset *ipset, + ipset_custom_errorfn custom_error, + ipset_standard_errorfn standard_error, + ipset_print_outfn outfn, + void *p); + +extern int ipset_parse_argv(struct ipset *ipset, int argc, char *argv[]); +extern int ipset_parse_line(struct ipset *ipset, char *line); +extern int ipset_parse_stream(struct ipset *ipset, FILE *f); +extern struct ipset * ipset_init(void); +extern int ipset_fini(struct ipset *ipset); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBIPSET_IPSET_H */ diff --git a/include/libipset/parse.h b/include/libipset/parse.h index 9223eeb..4bd5df5 100644 --- a/include/libipset/parse.h +++ b/include/libipset/parse.h @@ -104,8 +104,6 @@ extern int ipset_parse_skbmark(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_skbprio(struct ipset_session *session, enum ipset_opt opt, const char *str); -extern int ipset_parse_output(struct ipset_session *session, - int opt, const char *str); extern int ipset_parse_ignored(struct ipset_session *session, enum ipset_opt opt, const char *str); extern int ipset_parse_elem(struct ipset_session *session, diff --git a/include/libipset/session.h b/include/libipset/session.h index bac4d19..825d2c8 100644 --- a/include/libipset/session.h +++ b/include/libipset/session.h @@ -19,7 +19,6 @@ struct ipset_session; struct ipset_data; -struct ipset_handle; #ifdef __cplusplus extern "C" { @@ -33,6 +32,7 @@ extern const struct ipset_type * ipset_saved_type(const struct ipset_session *session); extern void ipset_session_lineno(struct ipset_session *session, uint32_t lineno); +extern void * ipset_session_printf_private(struct ipset_session *session); enum ipset_err_type { IPSET_ERROR, @@ -80,10 +80,12 @@ enum ipset_envopt { IPSET_ENV_LIST_HEADER = (1 << IPSET_ENV_BIT_LIST_HEADER), }; -extern int ipset_envopt_parse(struct ipset_session *session, - int env, const char *str); extern bool ipset_envopt_test(struct ipset_session *session, enum ipset_envopt env); +extern void ipset_envopt_set(struct ipset_session *session, + enum ipset_envopt env); +extern void ipset_envopt_unset(struct ipset_session *session, + enum ipset_envopt env); enum ipset_output_mode { IPSET_LIST_NONE, @@ -99,12 +101,30 @@ extern int ipset_commit(struct ipset_session *session); extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno); -typedef int (*ipset_outfn)(const char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); +typedef int (*ipset_print_outfn)(struct ipset_session *session, + void *p, const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); -extern int ipset_session_outfn(struct ipset_session *session, - ipset_outfn outfn); -extern struct ipset_session *ipset_session_init(ipset_outfn outfn); +extern int ipset_session_print_outfn(struct ipset_session *session, + ipset_print_outfn outfn, + void *p); + +enum ipset_io_type { + IPSET_IO_INPUT, + IPSET_IO_OUTPUT, +}; + +extern int ipset_session_io_full(struct ipset_session *session, + const char *filename, enum ipset_io_type what); +extern int ipset_session_io_normal(struct ipset_session *session, + const char *filename, enum ipset_io_type what); +extern FILE * ipset_session_io_stream(struct ipset_session *session, + enum ipset_io_type what); +extern int ipset_session_io_close(struct ipset_session *session, + enum ipset_io_type what); + +extern struct ipset_session *ipset_session_init(ipset_print_outfn outfn, + void *p); extern int ipset_session_fini(struct ipset_session *session); extern void ipset_debug_msg(const char *dir, void *buffer, int len); diff --git a/include/libipset/ui.h b/include/libipset/ui.h deleted file mode 100644 index 4846dc1..0000000 --- a/include/libipset/ui.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef LIBIPSET_UI_H -#define LIBIPSET_UI_H - -#include <stdbool.h> /* bool */ -#include <libipset/linux_ip_set.h> /* enum ipset_cmd */ - -#define IPSET_CMD_ALIASES 3 - -/* Commands in userspace */ -struct ipset_commands { - enum ipset_cmd cmd; - int has_arg; - const char *name[IPSET_CMD_ALIASES]; - const char *help; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -extern const struct ipset_commands ipset_commands[]; - -struct ipset_session; -struct ipset_data; - -/* Environment options */ -struct ipset_envopts { - int flag; - int has_arg; - const char *name[2]; - const char *help; - int (*parse)(struct ipset_session *s, int flag, const char *str); - int (*print)(char *buf, unsigned int len, - const struct ipset_data *data, int flag, uint8_t env); -}; - -extern const struct ipset_envopts ipset_envopts[]; - -extern bool ipset_match_cmd(const char *arg, const char * const name[]); -extern bool ipset_match_option(const char *arg, const char * const name[]); -extern bool ipset_match_envopt(const char *arg, const char * const name[]); -extern void ipset_shift_argv(int *argc, char *argv[], int from); -extern void ipset_port_usage(void); -extern int ipset_parse_file(struct ipset_session *s, int opt, const char *str); - -#ifdef __cplusplus -} -#endif - -#endif /* LIBIPSET_UI_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am index d85d5bb..281f693 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -37,7 +37,7 @@ libipset_la_SOURCES = \ print.c \ session.c \ types.c \ - ui.c \ + ipset.c \ types_init.c EXTRA_libipset_la_SOURCES = \ @@ -48,6 +48,8 @@ EXTRA_DIST = $(IPSET_SETTYPE_LIST) libipset.map pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libipset.pc +dist_man_MANS = libipset.3 + sparse-check: $(libipset_la_SOURCES:.c=.d) %.d: %.c diff --git a/lib/ipset.c b/lib/ipset.c new file mode 100644 index 0000000..a52f3d6 --- /dev/null +++ b/lib/ipset.c @@ -0,0 +1,1472 @@ +/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <assert.h> /* assert */ +#include <ctype.h> /* isspace */ +#include <errno.h> /* errno */ +#include <stdarg.h> /* va_* */ +#include <stdbool.h> /* bool */ +#include <stdio.h> /* printf */ +#include <stdlib.h> /* exit */ +#include <string.h> /* str* */ + +#include <config.h> + +#include <libipset/debug.h> /* D() */ +#include <libipset/linux_ip_set.h> /* IPSET_CMD_* */ +#include <libipset/icmp.h> /* id_to_icmp */ +#include <libipset/icmpv6.h> /* id_to_icmpv6 */ +#include <libipset/data.h> /* enum ipset_data */ +#include <libipset/types.h> /* IPSET_*_ARG */ +#include <libipset/session.h> /* ipset_envopt_parse */ +#include <libipset/parse.h> /* ipset_parse_family */ +#include <libipset/print.h> /* ipset_print_family */ +#include <libipset/utils.h> /* STREQ */ +#include <libipset/ipset.h> /* prototypes */ + +static char program_name[] = PACKAGE; +static char program_version[] = PACKAGE_VERSION; + +#define MAX_CMDLINE_CHARS 1024 +#define MAX_ARGS 32 + +/* The ipset structure */ +struct ipset { + ipset_custom_errorfn custom_error; + /* Custom error message function */ + ipset_standard_errorfn standard_error; + /* Standard error message function */ + struct ipset_session *session; /* Session */ + uint32_t restore_line; /* Restore lineno */ + bool interactive; /* "Interactive" CLI */ + bool full_io; /* Use session ios */ + bool no_vhi; /* No version/help/interactive */ + char cmdline[MAX_CMDLINE_CHARS]; /* For restore mode */ + char *newargv[MAX_ARGS]; + int newargc; + const char *filename; /* Input/output filename */ +}; + +/* Commands and environment options */ + +const struct ipset_commands ipset_commands[] = { + /* Order is important */ + + { /* c[reate], --create, n[ew], -N */ + .cmd = IPSET_CMD_CREATE, + .name = { "create", "new", "-N" }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "SETNAME TYPENAME [type-specific-options]\n" + " Create a new set", + }, + { /* a[dd], --add, -A */ + .cmd = IPSET_CMD_ADD, + .name = { "add", "-A", NULL }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "SETNAME ENTRY\n" + " Add entry to the named set", + }, + { /* d[el], --del, -D */ + .cmd = IPSET_CMD_DEL, + .name = { "del", "-D", NULL }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "SETNAME ENTRY\n" + " Delete entry from the named set", + }, + { /* t[est], --test, -T */ + .cmd = IPSET_CMD_TEST, + .name = { "test", "-T", NULL }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "SETNAME ENTRY\n" + " Test entry in the named set", + }, + { /* des[troy], --destroy, x, -X */ + .cmd = IPSET_CMD_DESTROY, + .name = { "destroy", "x", "-X" }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " Destroy a named set or all sets", + }, + { /* l[ist], --list, -L */ + .cmd = IPSET_CMD_LIST, + .name = { "list", "-L", NULL }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " List the entries of a named set or all sets", + }, + { /* s[save], --save, -S */ + .cmd = IPSET_CMD_SAVE, + .name = { "save", "-S", NULL }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " Save the named set or all sets to stdout", + }, + { /* r[estore], --restore, -R */ + .cmd = IPSET_CMD_RESTORE, + .name = { "restore", "-R", NULL }, + .has_arg = IPSET_NO_ARG, + .help = "\n" + " Restore a saved state", + }, + { /* f[lush], --flush, -F */ + .cmd = IPSET_CMD_FLUSH, + .name = { "flush", "-F", NULL }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[SETNAME]\n" + " Flush a named set or all sets", + }, + { /* ren[ame], --rename, e, -E */ + .cmd = IPSET_CMD_RENAME, + .name = { "rename", "e", "-E" }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "FROM-SETNAME TO-SETNAME\n" + " Rename two sets", + }, + { /* sw[ap], --swap, w, -W */ + .cmd = IPSET_CMD_SWAP, + .name = { "swap", "w", "-W" }, + .has_arg = IPSET_MANDATORY_ARG2, + .help = "FROM-SETNAME TO-SETNAME\n" + " Swap the contect of two existing sets", + }, + { /* h[elp, --help, -H */ + .cmd = IPSET_CMD_HELP, + .name = { "help", "-h", "-H" }, + .has_arg = IPSET_OPTIONAL_ARG, + .help = "[TYPENAME]\n" + " Print help, and settype specific help", + }, + { /* v[ersion], --version, -V */ + .cmd = IPSET_CMD_VERSION, + .name = { "version", "-v", "-V" }, + .has_arg = IPSET_NO_ARG, + .help = "\n" + " Print version information", + }, + { /* q[uit] */ + .cmd = IPSET_CMD_QUIT, + .name = { "quit", NULL }, + .has_arg = IPSET_NO_ARG, + .help = "\n" + " Quit interactive mode", + }, + { }, +}; + +/** + * ipset_match_cmd - try to match as a prefix or letter-command + * @arg: possible command string + * @name: command and it's aliases + * + * Returns true if @arg is a known command. + */ +bool +ipset_match_cmd(const char *arg, const char * const name[]) +{ + size_t len, skip = 0; + int i; + + assert(arg); + assert(name && name[0]); + + /* Ignore two leading dashes */ + if (arg[0] == '-' && arg[1] == '-') + skip = 2; + + len = strlen(arg); + if (len <= skip || (len == 1 && arg[0] == '-')) + return false; + + for (i = 0; i < IPSET_CMD_ALIASES && name[i] != NULL; i++) { + /* New command name options */ + if (STRNEQ(arg + skip, name[i], len - skip)) + return true; + } + return false; +} + +/* Used up so far + * + * -A add + * -D del + * -E rename + * -f -file + * -F flush + * -h help + * -H help + * -L list + * -n -name + * -N create + * -o -output + * -r -resolve + * -R restore + * -s -sorted + * -S save + * -t -terse + * -T test + * -q -quiet + * -X destroy + * -v version + * -V version + * -W swap + * -! -exist + */ + +const struct ipset_envopts ipset_envopts[] = { + { .name = { "-o", "-output" }, + .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, + .parse = ipset_parse_output, + .help = "plain|save|xml\n" + " Specify output mode for listing sets.\n" + " Default value for \"list\" command is mode \"plain\"\n" + " and for \"save\" command is mode \"save\".", + }, + { .name = { "-s", "-sorted" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_SORTED, + .help = "\n" + " Print elements sorted (if supported by the set type).", + }, + { .name = { "-q", "-quiet" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_QUIET, + .help = "\n" + " Suppress any notice or warning message.", + }, + { .name = { "-r", "-resolve" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_RESOLVE, + .help = "\n" + " Try to resolve IP addresses in the output (slow!)", + }, + { .name = { "-!", "-exist" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_EXIST, + .help = "\n" + " Ignore errors when creating or adding sets or\n" + " elements that do exist or when deleting elements\n" + " that don't exist.", + }, + { .name = { "-n", "-name" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_SETNAME, + .help = "\n" + " When listing, just list setnames from the kernel.\n", + }, + { .name = { "-t", "-terse" }, + .parse = ipset_envopt_parse, + .has_arg = IPSET_NO_ARG, .flag = IPSET_ENV_LIST_HEADER, + .help = "\n" + " When listing, list setnames and set headers\n" + " from kernel only.", + }, + { .name = { "-f", "-file" }, + .parse = ipset_parse_filename, + .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, + .help = "\n" + " Read from the given file instead of standard\n" + " input (restore) or write to given file instead\n" + " of standard output (list/save).", + }, + { }, +}; + +/** + * ipset_match_option - strict option matching + * @arg: possible option string + * @name: known option and it's alias + * + * Two leading dashes are ignored. + * + * Returns true if @arg is a known option. + */ +bool +ipset_match_option(const char *arg, const char * const name[]) +{ + assert(arg); + assert(name && name[0]); + + /* Skip two leading dashes */ + if (arg[0] == '-' && arg[1] == '-') + arg++, arg++; + + return STREQ(arg, name[0]) || + (name[1] != NULL && STREQ(arg, name[1])); +} + +/** + * ipset_match_envopt - strict envopt matching + * @arg: possible envopt string + * @name: known envopt and it's alias + * + * One leading dash is ignored. + * + * Returns true if @arg is a known envopt. + */ +bool +ipset_match_envopt(const char *arg, const char * const name[]) +{ + assert(arg); + assert(name && name[0]); + + /* Skip one leading dash */ + if (arg[0] == '-' && arg[1] == '-') + arg++; + + return STREQ(arg, name[0]) || + (name[1] != NULL && STREQ(arg, name[1])); +} + +static void +ipset_shift_argv(int *argc, char *argv[], int from) +{ + int i; + + assert(*argc >= from + 1); + + for (i = from + 1; i <= *argc; i++) + argv[i-1] = argv[i]; + (*argc)--; + return; +} + +/** + * ipset_port_usage - prints the usage for the port parameter + * + * Print the usage for the port parameter to stdout. + */ +void +ipset_port_usage(void) +{ + int i; + const char *name; + + printf(" [PROTO:]PORT is a valid pattern of the following:\n" + " PORTNAME TCP port name from /etc/services\n" + " PORTNUMBER TCP port number identifier\n" + " tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n" + " icmp:CODENAME supported ICMP codename\n" + " icmp:TYPE/CODE ICMP type/code value\n" + " icmpv6:CODENAME supported ICMPv6 codename\n" + " icmpv6:TYPE/CODE ICMPv6 type/code value\n" + " PROTO:0 all other protocols\n\n"); + + printf(" Supported ICMP codenames:\n"); + i = 0; + while ((name = id_to_icmp(i++)) != NULL) + printf(" %s\n", name); + printf(" Supported ICMPv6 codenames:\n"); + i = 0; + while ((name = id_to_icmpv6(i++)) != NULL) + printf(" %s\n", name); +} + +/** + * ipset_parse_filename - parse filename + * @ipset: ipset structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse filename of "-file" option, which can be used once only. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_filename(struct ipset *ipset, + int opt UNUSED, const char *str) +{ + void *p = ipset_session_printf_private(ipset->session); + + if (ipset->filename) + return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "-file option cannot be used when full io is activated"); + ipset->filename = str; + + return 0; +} + +/** + * ipset_parse_output - parse output format name + * @ipset: ipset structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse output format names and set session mode. + * The value is stored in the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_output(struct ipset *ipset, + int opt UNUSED, const char *str) +{ + struct ipset_session *session; + + assert(ipset); + assert(str); + + session = ipset_session(ipset); + if (STREQ(str, "plain")) + return ipset_session_output(session, IPSET_LIST_PLAIN); + else if (STREQ(str, "xml")) + return ipset_session_output(session, IPSET_LIST_XML); + else if (STREQ(str, "save")) + return ipset_session_output(session, IPSET_LIST_SAVE); + + return ipset_err(session, + "Syntax error: unknown output mode '%s'", str); +} + +/** + * ipset_envopt_parse - parse/set environment option + * @ipset: ipset structure + * @opt: environment option + * @arg: option argument (unused) + * + * Parse and set an environment option. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_envopt_parse(struct ipset *ipset, int opt, + const char *arg UNUSED) +{ + struct ipset_session *session; + assert(ipset); + + session = ipset_session(ipset); + switch (opt) { + case IPSET_ENV_SORTED: + case IPSET_ENV_QUIET: + case IPSET_ENV_RESOLVE: + case IPSET_ENV_EXIST: + case IPSET_ENV_LIST_SETNAME: + case IPSET_ENV_LIST_HEADER: + ipset_envopt_set(session, opt); + return 0; + default: + break; + } + return -1; +} + +static int __attribute__((format(printf, 4, 5))) +default_custom_error(struct ipset *ipset, void *p UNUSED, + int status, const char *msg, ...) +{ + struct ipset_session *session = ipset_session(ipset); + bool is_interactive = ipset_is_interactive(ipset); + bool quiet = !is_interactive && + session && + ipset_envopt_test(session, IPSET_ENV_QUIET); + + if (status && msg && !quiet) { + va_list args; + + fprintf(stderr, "%s v%s: ", program_name, program_version); + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + if (status != IPSET_SESSION_PROBLEM) + fprintf(stderr, "\n"); + + if (status == IPSET_PARAMETER_PROBLEM) + fprintf(stderr, + "Try `%s help' for more information.\n", + program_name); + } + /* Ignore errors in interactive mode */ + if (status && is_interactive) { + if (session) + ipset_session_report_reset(session); + return -1; + } + + D("status: %u", status); + ipset_fini(ipset); + exit(status > IPSET_VERSION_PROBLEM ? IPSET_OTHER_PROBLEM : status); + /* Unreached */ + return -1; +} + +static int +default_standard_error(struct ipset *ipset, void *p) +{ + struct ipset_session *session = ipset_session(ipset); + bool is_interactive = ipset_is_interactive(ipset); + + if (ipset_session_warning(session) && + !ipset_envopt_test(session, IPSET_ENV_QUIET)) + fprintf(stderr, "Warning: %s\n", + ipset_session_warning(session)); + if (ipset_session_error(session)) + return ipset->custom_error(ipset, p, + IPSET_SESSION_PROBLEM, "%s", + ipset_session_error(session)); + + if (!is_interactive) { + ipset_fini(ipset); + exit(IPSET_OTHER_PROBLEM); + } + + ipset_session_report_reset(session); + return -1; +} + +static void +default_help(void) +{ + const struct ipset_commands *c; + const struct ipset_envopts *opt = ipset_envopts; + + printf("%s v%s\n\n" + "Usage: %s [options] COMMAND\n\nCommands:\n", + program_name, program_version, program_name); + + for (c = ipset_commands; c->cmd; c++) + printf("%s %s\n", c->name[0], c->help); + printf("\nOptions:\n"); + + while (opt->flag) { + if (opt->help) + printf("%s %s\n", opt->name[0], opt->help); + opt++; + } +} + +static void +reset_argv(struct ipset *ipset) +{ + int i; + + /* Reset */ + for (i = 1; i < ipset->newargc; i++) { + if (ipset->newargv[i]) + free(ipset->newargv[i]); + ipset->newargv[i] = NULL; + } + ipset->newargc = 1; +} + +/* Build fake argv from parsed line */ +static int +build_argv(struct ipset *ipset, char *buffer) +{ + void *p = ipset_session_printf_private(ipset->session); + char *tmp, *arg; + int i; + bool quoted = false; + + reset_argv(ipset); + arg = calloc(strlen(buffer) + 1, sizeof(*buffer)); + if (!arg) + return ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "Cannot allocate memory."); + for (tmp = buffer, i = 0; *tmp; tmp++) { + if ((ipset->newargc + 1) == + (int)(sizeof(ipset->newargv)/sizeof(char *))) { + free(arg); + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "Line is too long to parse."); + } + switch (*tmp) { + case '"': + quoted = !quoted; + if (*(tmp+1)) + continue; + break; + case ' ': + case '\r': + case '\n': + case '\t': + if (!quoted) + break; + arg[i++] = *tmp; + continue; + default: + arg[i++] = *tmp; + if (*(tmp+1)) + continue; + break; + } + if (!*(tmp+1) && quoted) { + free(arg); + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "Missing close quote!"); + } + if (!*arg) + continue; + ipset->newargv[ipset->newargc] = + calloc(strlen(arg) + 1, sizeof(*arg)); + if (!ipset->newargv[ipset->newargc]) { + free(arg); + return ipset->custom_error(ipset, + p, IPSET_OTHER_PROBLEM, + "Cannot allocate memory."); + } + ipset_strlcpy(ipset->newargv[ipset->newargc++], + arg, strlen(arg) + 1); + memset(arg, 0, strlen(arg) + 1); + i = 0; + } + + free(arg); + return 0; +} + +static int +restore(struct ipset *ipset) +{ + struct ipset_session *session = ipset_session(ipset); + int ret = 0; + FILE *f = stdin; /* Default from stdin */ + + if (ipset->filename) { + ret = ipset_session_io_normal(session, ipset->filename, + IPSET_IO_INPUT); + if (ret) + return ret; + f = ipset_session_io_stream(session, IPSET_IO_INPUT); + } + return ipset_parse_stream(ipset, f); +} + +static bool do_parse(const struct ipset_arg *arg, bool family) +{ + return !((family == true) ^ (arg->opt == IPSET_OPT_FAMILY)); +} + +static int +call_parser(struct ipset *ipset, int *argc, char *argv[], + const struct ipset_type *type, enum ipset_adt cmd, bool family) +{ + void *p = ipset_session_printf_private(ipset->session); + const struct ipset_arg *arg; + const char *optstr; + const struct ipset_type *t = type; + uint8_t revision = type->revision; + int ret = 0, i = 1, j; + + /* Currently CREATE and ADT may have got additional arguments */ + if (type->cmd[cmd].args[0] == IPSET_ARG_NONE && *argc > 1) + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "Unknown argument: `%s'", argv[i]); + + while (*argc > i) { + ret = -1; + for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { + arg = ipset_keyword(type->cmd[cmd].args[j]); + D("argc: %u, %s vs %s", i, argv[i], arg->name[0]); + if (!(ipset_match_option(argv[i], arg->name))) + continue; + + optstr = argv[i]; + /* Matched option */ + D("match %s, argc %u, i %u, %s", + arg->name[0], *argc, i + 1, + do_parse(arg, family) ? "parse" : "skip"); + i++; + ret = 0; + switch (arg->has_arg) { + case IPSET_MANDATORY_ARG: + if (*argc - i < 1) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Missing mandatory argument " + "of option `%s'", + arg->name[0]); + /* Fall through */ + case IPSET_OPTIONAL_ARG: + if (*argc - i >= 1) { + if (do_parse(arg, family)) { + ret = ipset_call_parser( + ipset->session, + arg, argv[i]); + if (ret < 0) + return ret; + } + i++; + break; + } + /* Fall through */ + default: + if (do_parse(arg, family)) { + ret = ipset_call_parser( + ipset->session, arg, optstr); + if (ret < 0) + return ret; + } + } + break; + } + if (ret < 0) + goto err_unknown; + } + if (!family) + *argc = 0; + return ret; + +err_unknown: + while ((type = ipset_type_higher_rev(t)) != t) { + for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { + arg = ipset_keyword(type->cmd[cmd].args[j]); + D("argc: %u, %s vs %s", i, argv[i], arg->name[0]); + if (ipset_match_option(argv[i], arg->name)) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Argument `%s' is supported in the kernel module " + "of the set type %s starting from the revision %u " + "and you have installed revision %u only. " + "Your kernel is behind your ipset utility.", + argv[i], type->name, + type->revision, revision); + } + t = type; + } + return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "Unknown argument: `%s'", argv[i]); +} + +static enum ipset_adt +cmd2cmd(int cmd) +{ + switch (cmd) { + case IPSET_CMD_ADD: + return IPSET_ADD; + case IPSET_CMD_DEL: + return IPSET_DEL; + case IPSET_CMD_TEST: + return IPSET_TEST; + case IPSET_CMD_CREATE: + return IPSET_CREATE; + default: + return 0; + } +} + +static void +check_mandatory(struct ipset *ipset, + const struct ipset_type *type, enum ipset_cmd command) +{ + enum ipset_adt cmd = cmd2cmd(command); + struct ipset_session *session = ipset->session; + void *p = ipset_session_printf_private(session); + uint64_t flags = ipset_data_flags(ipset_session_data(session)); + uint64_t mandatory = type->cmd[cmd].need; + const struct ipset_arg *arg; + int i; + + /* Range can be expressed by ip/cidr */ + if (flags & IPSET_FLAG(IPSET_OPT_CIDR)) + flags |= IPSET_FLAG(IPSET_OPT_IP_TO); + + mandatory &= ~flags; + if (!mandatory) + return; + if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) { + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "There are missing mandatory flags " + "but can't check them. " + "It's a bug, please report the problem."); + return; + } + + for (i = 0; type->cmd[cmd].args[i] != IPSET_ARG_NONE; i++) { + arg = ipset_keyword(type->cmd[cmd].args[i]); + if (mandatory & IPSET_FLAG(arg->opt)) { + ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "Mandatory option `%s' is missing", + arg->name[0]); + return; + } + } +} + +static const char * +cmd2name(enum ipset_cmd cmd) +{ + const struct ipset_commands *c; + + for (c = ipset_commands; c->cmd; c++) + if (cmd == c->cmd) + return c->name[0]; + return "unknown command"; +} + +static const char * +session_family(struct ipset_session *session) +{ + switch (ipset_data_family(ipset_session_data(session))) { + case NFPROTO_IPV4: + return "inet"; + case NFPROTO_IPV6: + return "inet6"; + default: + return "unspec"; + } +} + +static void +check_allowed(struct ipset *ipset, + const struct ipset_type *type, enum ipset_cmd command) +{ + struct ipset_session *session = ipset->session; + void *p = ipset_session_printf_private(session); + uint64_t flags = ipset_data_flags(ipset_session_data(session)); + enum ipset_adt cmd = cmd2cmd(command); + uint64_t allowed = type->cmd[cmd].full; + uint64_t cmdflags = command == IPSET_CMD_CREATE + ? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS; + const struct ipset_arg *arg; + enum ipset_opt i; + int j; + + /* Range can be expressed by ip/cidr or from-to */ + if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO)) + allowed |= IPSET_FLAG(IPSET_OPT_CIDR); + + for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) { + if (!(cmdflags & IPSET_FLAG(i)) || + (allowed & IPSET_FLAG(i)) || + !(flags & IPSET_FLAG(i))) + continue; + /* Not allowed element-expressions */ + switch (i) { + case IPSET_OPT_CIDR: + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "IP/CIDR range is not allowed in command %s " + "with set type %s and family %s", + cmd2name(command), type->name, + session_family(ipset->session)); + return; + case IPSET_OPT_IP_TO: + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "FROM-TO IP range is not allowed in command %s " + "with set type %s and family %s", + cmd2name(command), type->name, + session_family(ipset->session)); + return; + case IPSET_OPT_PORT_TO: + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "FROM-TO port range is not allowed in command %s " + "with set type %s and family %s", + cmd2name(command), type->name, + session_family(ipset->session)); + return; + default: + break; + } + /* Other options */ + if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) { + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "There are not allowed options (%u) " + "but option list is empty. " + "It's a bug, please report the problem.", i); + return; + } + for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { + arg = ipset_keyword(type->cmd[cmd].args[j]); + if (arg->opt != i) + continue; + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "%s parameter is not allowed in command %s " + "with set type %s and family %s", + arg->name[0], + cmd2name(command), type->name, + session_family(ipset->session)); + return; + } + ipset->custom_error(ipset, p, IPSET_OTHER_PROBLEM, + "There are not allowed options (%u) " + "but can't resolve them. " + "It's a bug, please report the problem.", i); + return; + } +} + +static const struct ipset_type * +type_find(const char *name) +{ + const struct ipset_type *t = ipset_types(); + + while (t) { + if (ipset_match_typename(name, t)) + return t; + t = t->next; + } + return NULL; +} + +static enum ipset_adt cmd_help_order[] = { + IPSET_CREATE, + IPSET_ADD, + IPSET_DEL, + IPSET_TEST, + IPSET_CADT_MAX, +}; + +static const char *cmd_prefix[] = { + [IPSET_CREATE] = "create SETNAME", + [IPSET_ADD] = "add SETNAME", + [IPSET_DEL] = "del SETNAME", + [IPSET_TEST] = "test SETNAME", +}; + +/* Workhorses */ + +/** + * ipset_parse_argv - parse and argv array and execute the command + * @ipset: ipset structure + * @argc: length of the array + * @argv: array of strings + * + * Parse an array of strings and execute the ipset command. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[]) +{ + int ret = 0; + enum ipset_cmd cmd = IPSET_CMD_NONE; + int i; + char *arg0 = NULL, *arg1 = NULL; + const struct ipset_envopts *opt; + const struct ipset_commands *command; + const struct ipset_type *type; + struct ipset_session *session = ipset->session; + void *p = ipset_session_printf_private(session); + int argc = oargc; + char *argv[MAX_ARGS] = {}; + + /* We need a local copy because of ipset_shift_argv */ + memcpy(argv, oargv, sizeof(char *) * argc); + + /* Set session lineno to report parser errors correctly */ + ipset_session_lineno(session, ipset->restore_line); + + /* Commandline parsing, somewhat similar to that of 'ip' */ + + /* First: parse core options */ + for (opt = ipset_envopts; opt->flag; opt++) { + for (i = 1; i < argc; ) { + if (!ipset_match_envopt(argv[i], opt->name)) { + i++; + continue; + } + /* Shift off matched option */ + ipset_shift_argv(&argc, argv, i); + switch (opt->has_arg) { + case IPSET_MANDATORY_ARG: + if (i + 1 > argc) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Missing mandatory argument " + "to option %s", + opt->name[0]); + /* Fall through */ + case IPSET_OPTIONAL_ARG: + if (i + 1 <= argc) { + ret = opt->parse(ipset, opt->flag, + argv[i]); + if (ret < 0) + return ipset->standard_error(ipset, p); + ipset_shift_argv(&argc, argv, i); + } + break; + case IPSET_NO_ARG: + ret = opt->parse(ipset, opt->flag, + opt->name[0]); + if (ret < 0) + return ipset->standard_error(ipset, p); + break; + default: + break; + } + } + } + + /* Second: parse command */ + for (command = ipset_commands; + argc > 1 && command->cmd && cmd == IPSET_CMD_NONE; + command++) { + if (!ipset_match_cmd(argv[1], command->name)) + continue; + + if (ipset->restore_line != 0 && + (command->cmd == IPSET_CMD_RESTORE || + command->cmd == IPSET_CMD_VERSION || + command->cmd == IPSET_CMD_HELP)) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Command `%s' is invalid " + "in restore mode.", + command->name[0]); + if (ipset->interactive && command->cmd == IPSET_CMD_RESTORE) { + printf("Restore command is not supported " + "in interactive mode\n"); + return 0; + } + + /* Shift off matched command arg */ + ipset_shift_argv(&argc, argv, 1); + cmd = command->cmd; + switch (command->has_arg) { + case IPSET_MANDATORY_ARG: + case IPSET_MANDATORY_ARG2: + if (argc < 2) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Missing mandatory argument " + "to command %s", + command->name[0]); + /* Fall through */ + case IPSET_OPTIONAL_ARG: + arg0 = argv[1]; + if (argc >= 2) + /* Shift off first arg */ + ipset_shift_argv(&argc, argv, 1); + break; + default: + break; + } + if (command->has_arg == IPSET_MANDATORY_ARG2) { + if (argc < 2) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Missing second mandatory " + "argument to command %s", + command->name[0]); + arg1 = argv[1]; + /* Shift off second arg */ + ipset_shift_argv(&argc, argv, 1); + } + break; + } + + /* Third: catch interactive mode, handle help, version */ + switch (cmd) { + case IPSET_CMD_NONE: + if (ipset->interactive) { + printf("No command specified\n"); + if (session) + ipset_envopt_parse(ipset, 0, "reset"); + return 0; + } + if (argc > 1 && STREQ(argv[1], "-")) { + if (ipset->no_vhi) + return 0; + ipset->interactive = true; + printf("%s> ", program_name); + while (fgets(ipset->cmdline, + sizeof(ipset->cmdline), stdin)) { + /* Execute line: ignore soft errors */ + if (ipset_parse_line(ipset, ipset->cmdline) < 0) + ipset->standard_error(ipset, p); + printf("%s> ", program_name); + } + return ipset->custom_error(ipset, p, + IPSET_NO_PROBLEM, NULL); + } + if (argc > 1) + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "No command specified: unknown argument %s", + argv[1]); + return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "No command specified."); + case IPSET_CMD_VERSION: + if (ipset->no_vhi) + return 0; + printf("%s v%s, protocol version: %u\n", + program_name, program_version, IPSET_PROTOCOL); + if (ipset->interactive) + return 0; + return ipset->custom_error(ipset, p, IPSET_NO_PROBLEM, NULL); + case IPSET_CMD_HELP: + if (ipset->no_vhi) + return 0; + default_help(); + + if (ipset->interactive || + !ipset_envopt_test(session, IPSET_ENV_QUIET)) { + if (arg0) { + const struct ipset_arg *arg; + int k; + + /* Type-specific help, without kernel checking */ + type = type_find(arg0); + if (!type) + return ipset->custom_error(ipset, p, + IPSET_PARAMETER_PROBLEM, + "Unknown settype: `%s'", arg0); + printf("\n%s type specific options:\n\n", type->name); + for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) { + cmd = cmd_help_order[i]; + printf("%s %s %s\n", + cmd_prefix[cmd], type->name, type->cmd[cmd].help); + for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) { + arg = ipset_keyword(type->cmd[cmd].args[k]); + if (!arg->help || arg->help[0] == '\0') + continue; + printf(" %s\n", arg->help); + } + } + printf("\n%s\n", type->usage); + if (type->usagefn) + type->usagefn(); + if (type->family == NFPROTO_UNSPEC) + printf("\nType %s is family neutral.\n", + type->name); + else if (type->family == NFPROTO_IPSET_IPV46) + printf("\nType %s supports inet " + "and inet6.\n", + type->name); + else + printf("\nType %s supports family " + "%s only.\n", + type->name, + type->family == NFPROTO_IPV4 + ? "inet" : "inet6"); + } else { + printf("\nSupported set types:\n"); + type = ipset_types(); + while (type) { + printf(" %s\t%s%u\t%s\n", + type->name, + strlen(type->name) < 12 ? "\t" : "", + type->revision, + type->description); + type = type->next; + } + } + } + if (ipset->interactive) + return 0; + return ipset->custom_error(ipset, p, IPSET_NO_PROBLEM, NULL); + case IPSET_CMD_QUIT: + return ipset->custom_error(ipset, p, IPSET_NO_PROBLEM, NULL); + default: + break; + } + + /* Forth: parse command args and issue the command */ + switch (cmd) { + case IPSET_CMD_CREATE: + /* Args: setname typename [type specific options] */ + ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); + if (ret < 0) + return ipset->standard_error(ipset, p); + + ret = ipset_parse_typename(session, IPSET_OPT_TYPENAME, arg1); + if (ret < 0) + return ipset->standard_error(ipset, p); + + type = ipset_type_get(session, cmd); + if (type == NULL) + return ipset->standard_error(ipset, p); + + /* Parse create options: first check INET family */ + ret = call_parser(ipset, &argc, argv, type, IPSET_CREATE, true); + if (ret < 0) + return ipset->standard_error(ipset, p); + else if (ret) + return ret; + + /* Parse create options: then check all options */ + ret = call_parser(ipset, &argc, argv, type, IPSET_CREATE, false); + if (ret < 0) + return ipset->standard_error(ipset, p); + else if (ret) + return ret; + + /* Check mandatory, then allowed options */ + check_mandatory(ipset, type, cmd); + check_allowed(ipset, type, cmd); + + break; + case IPSET_CMD_LIST: + case IPSET_CMD_SAVE: + if (ipset->filename != NULL) { + ret = ipset_session_io_normal(session, + ipset->filename, IPSET_IO_OUTPUT); + if (!ret) + return ret; + } + case IPSET_CMD_DESTROY: + case IPSET_CMD_FLUSH: + /* Args: [setname] */ + if (arg0) { + ret = ipset_parse_setname(session, + IPSET_SETNAME, arg0); + if (ret < 0) + return ipset->standard_error(ipset, p); + } + break; + + case IPSET_CMD_RENAME: + case IPSET_CMD_SWAP: + /* Args: from-setname to-setname */ + ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); + if (ret < 0) + return ipset->standard_error(ipset, p); + ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1); + if (ret < 0) + return ipset->standard_error(ipset, p); + break; + + case IPSET_CMD_RESTORE: + /* Restore mode */ + if (argc > 1) + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); + return restore(ipset); + case IPSET_CMD_ADD: + case IPSET_CMD_DEL: + case IPSET_CMD_TEST: + D("ADT: setname %s", arg0); + /* Args: setname ip [options] */ + ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); + if (ret < 0) + return ipset->standard_error(ipset, p); + + type = ipset_type_get(session, cmd); + if (type == NULL) + return ipset->standard_error(ipset, p); + + ret = ipset_parse_elem(session, type->last_elem_optional, arg1); + if (ret < 0) + return ipset->standard_error(ipset, p); + + /* Parse additional ADT options */ + ret = call_parser(ipset, &argc, argv, type, cmd2cmd(cmd), false); + if (ret < 0) + return ipset->standard_error(ipset, p); + else if (ret) + return ret; + + /* Check mandatory, then allowed options */ + check_mandatory(ipset, type, cmd); + check_allowed(ipset, type, cmd); + + break; + default: + break; + } + + if (argc > 1) + return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); + ret = ipset_cmd(session, cmd, ipset->restore_line); + D("ret %d", ret); + /* Special case for TEST and non-quiet mode */ + if (cmd == IPSET_CMD_TEST && ipset_session_warning(session)) { + if (!ipset_envopt_test(session, IPSET_ENV_QUIET)) + fprintf(stderr, "%s", ipset_session_warning(session)); + ipset_session_report_reset(session); + } + if (ret < 0) + ipset->standard_error(ipset, p); + + return ret; +} + +/** + * ipset_parse_line - parse a string as a command line and execute it + * @ipset: ipset structure + * @line: string of line + * + * Parse a string as a command line and execute the ipset command. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_line(struct ipset *ipset, char *line) +{ + char *c = line; + int ret; + + reset_argv(ipset); + + while (isspace(c[0])) + c++; + if (c[0] == '\0' || c[0] == '#') { + if (ipset->interactive) + printf("%s> ", program_name); + return 0; + } + /* Build fake argv, argc */ + ret = build_argv(ipset, c); + if (ret < 0) + return ret; + /* Parse and execute line */ + return ipset_parse_argv(ipset, ipset->newargc, ipset->newargv); +} + +/** + * ipset_parse_stream - parse an stream and execute the commands + * @ipset: ipset structure + * @f: stream + * + * Parse an already opened file as stream and execute the commands. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_stream(struct ipset *ipset, FILE *f) +{ + struct ipset_session *session = ipset_session(ipset); + void *p = ipset_session_printf_private(session); + int ret = 0; + char *c; + + while (fgets(ipset->cmdline, sizeof(ipset->cmdline), f)) { + ipset->restore_line++; + c = ipset->cmdline; + while (isspace(c[0])) + c++; + if (c[0] == '\0' || c[0] == '#') + continue; + else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) { + ret = ipset_commit(ipset->session); + if (ret < 0) + ipset->standard_error(ipset, p); + continue; + } + /* Build faked argv, argc */ + ret = build_argv(ipset, c); + if (ret < 0) + return ret; + + /* Execute line */ + ret = ipset_parse_argv(ipset, ipset->newargc, ipset->newargv); + if (ret < 0) + ipset->standard_error(ipset, p); + } + /* implicit "COMMIT" at EOF */ + ret = ipset_commit(ipset->session); + if (ret < 0) + ipset->standard_error(ipset, p); + + return ret; +} + +/** + * ipset_session - returns the session pointer of an ipset structure + * @ipset: ipset structure + * + * Returns the session pointer of an ipset structure. + */ +struct ipset_session * +ipset_session(struct ipset *ipset) +{ + return ipset->session; +} + +/** + * ipset_is_interactive - is the interactive mode enabled? + * @ipset: ipset structure + * + * Returns true if the interactive mode is enabled. + */ +bool +ipset_is_interactive(struct ipset *ipset) +{ + return ipset->interactive; +} + +/** + * ipset_custom_printf - set custom print functions + * @ipset: ipset structure + * @custom_error: custom error function + * @standard_error: standard error function + * @print_outfn: output/printing function + * @p: pointer to private data area + * + * The function makes possible to set custom error and + * output functions for the library. The private data + * pointer can be used to pass arbitrary data to these functions. + * If a function argument is NULL, the default printing function is set. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_custom_printf(struct ipset *ipset, + ipset_custom_errorfn custom_error, + ipset_standard_errorfn standard_error, + ipset_print_outfn print_outfn, + void *p) +{ + ipset->no_vhi = !!(custom_error || standard_error || print_outfn); + ipset->custom_error = + custom_error ? custom_error : default_custom_error; + ipset->standard_error = + standard_error ? standard_error : default_standard_error; + + return ipset_session_print_outfn(ipset->session, print_outfn, p); +} + +/** + * ipset_init - initialize ipset library interface + * + * Initialize the ipset library interface. + * + * Returns the created ipset structure for success or NULL for failure. + */ +struct ipset * +ipset_init(void) +{ + struct ipset *ipset; + + ipset = calloc(1, sizeof(struct ipset)); + if (ipset == NULL) + return NULL; + ipset->newargv[0] = + calloc(strlen(program_name) + 1, sizeof(*program_name)); + if (!ipset->newargv[0]) { + free(ipset); + return NULL; + } + ipset_strlcpy(ipset->newargv[0], program_name, + strlen(program_name) + 1); + ipset->newargc = 1; + ipset->session = ipset_session_init(NULL, NULL); + if (ipset->session == NULL) { + free(ipset->newargv[0]); + free(ipset); + return NULL; + } + ipset_custom_printf(ipset, NULL, NULL, NULL, NULL); + return ipset; +} + +/** + * ipset_fini - destroy an ipset library interface + * @ipset: ipset structure + * + * Destroys an ipset library interface + * + * Returns 0 on success or a negative error code. + */ +int +ipset_fini(struct ipset *ipset) +{ + assert(ipset); + + if (ipset->session) + ipset_session_fini(ipset->session); + reset_argv(ipset); + if (ipset->newargv[0]) + free(ipset->newargv[0]); + + free(ipset); + return 0; +} diff --git a/lib/ipset_hash_ipport.c b/lib/ipset_hash_ipport.c index 870a02a..c505412 100644 --- a/lib/ipset_hash_ipport.c +++ b/lib/ipset_hash_ipport.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* SCTP and UDPLITE support */ diff --git a/lib/ipset_hash_ipportip.c b/lib/ipset_hash_ipportip.c index c7fc153..b8e14a8 100644 --- a/lib/ipset_hash_ipportip.c +++ b/lib/ipset_hash_ipportip.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* SCTP and UDPLITE support */ diff --git a/lib/ipset_hash_ipportnet.c b/lib/ipset_hash_ipportnet.c index e0e9eb1..bcc3c7b 100644 --- a/lib/ipset_hash_ipportnet.c +++ b/lib/ipset_hash_ipportnet.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* SCTP and UDPLITE support */ diff --git a/lib/ipset_hash_netiface.c b/lib/ipset_hash_netiface.c index 9a4e7fa..1d829a3 100644 --- a/lib/ipset_hash_netiface.c +++ b/lib/ipset_hash_netiface.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* Initial revision */ diff --git a/lib/ipset_hash_netport.c b/lib/ipset_hash_netport.c index e6d9aa9..f6409e2 100644 --- a/lib/ipset_hash_netport.c +++ b/lib/ipset_hash_netport.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* SCTP and UDPLITE support */ diff --git a/lib/ipset_hash_netportnet.c b/lib/ipset_hash_netportnet.c index 3e19718..4d04dd8 100644 --- a/lib/ipset_hash_netportnet.c +++ b/lib/ipset_hash_netportnet.c @@ -7,7 +7,7 @@ #include <libipset/data.h> /* IPSET_OPT_* */ #include <libipset/parse.h> /* parser functions */ #include <libipset/print.h> /* printing functions */ -#include <libipset/ui.h> /* ipset_port_usage */ +#include <libipset/ipset.h> /* ipset_port_usage */ #include <libipset/types.h> /* prototypes */ /* initial revision */ diff --git a/lib/libipset.3 b/lib/libipset.3 new file mode 100644 index 0000000..840db06 --- /dev/null +++ b/lib/libipset.3 @@ -0,0 +1,242 @@ +.\" Man page written by Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.TH libipset 3 "Oct 16, 2018" "Jozsef Kadlecsik" "" +.SH NAME +libipset \- A library for using ipset +.SH SYNOPSIS +.nf +#include <libipset/ipset.h> +.sp +void ipset_load_types(void) +.sp +struct ipset * ipset_init(void) +int ipset_parse_argv(struct ipset *ipset, int argc, char *argv[]) +.sp +int ipset_parse_line(struct ipset *ipset, char *line) +.sp +int ipset_parse_stream(struct ipset *ipset, FILE *f) +.sp +int ipset_fini(struct ipset *ipset) +.sp +int ipset_custom_printf(struct ipset *ipset, + ipset_custom_errorfn custom_error, + ipset_standard_errorfn standard_error, + ipset_print_outfn outfn, + void *p) +.sp +struct ipset_session * ipset_session(struct ipset *ipset) +.sp +int ipset_session_full_io(struct ipset_session *session, + const char *filename, + enum ipset_io_type what) +.sp +int ipset_session_normal_io(struct ipset_session *session, + const char *filename, + enum ipset_io_type what) +.sp +FILE * ipset_session_io_stream(struct ipset_session *session, + enum ipset_io_type what) +.sp +int ipset_session_io_close(struct ipset_session *session, + enum ipset_io_type what) +.SH DESCRIPTION +libipset provides a library interface to +.BR ipset(8). +The integer return valued functions return 0 on success and a negative +value on failure. +.TP +ipset_load_types +Loads in the supported ipset types in the library and make them +available for the ipset interface. + +.TP +ipset_init +Initializes the ipset interface: allocates and initializes the required +internal structures, opens up the netlink channel. The function returns +the library interface structure of type +.B +struct ipset * +or +.B NULL +on failure. + +.TP +ipset_parse_argv +Parses the +.B argc +lenght of array of strings +.B argv +with the already initialized +.B +ipset +library structure. +If the command is successfully parsed, it is then submitted to the kernel +to execute. In the case of error, the textual error message is printed +and a negative number is returned. + +.TP +ipset_parse_line +Parses the string +.B line +with the already initialized +.B +ipset +library structure. The line is supposed to contain +a single ipset command in restore format. If the command is successfully +parsed, it is then submitted to the kernel to execute. In the case of +error, the textual error message is printed and a negative number is +returned. + +.TP +ipset_parse_stream +Parses the stream +.B f +with the already initialized +.B +ipset +library structure. The stream may contain multiple newline +separated ipset commands in restore format. The commands are parsed +and then submitted to the kernel in batches for efficiecy. In the case of +error, the textual error message is printed and a negative number is +returned. + +.TP +ipset_fini +Closes the netlink channel, closes opened streams and releases allocated +structures holding by the +.B ipset +library structure. + +.PP +The following functions makes possible to customize the interface. +.TP +ipset_custom_printf +Sets or resets the print functions for the +.B +ipset +library structure, where +.B +custom_error +is the custom error print function for the internal library errors, +.B +standard_error +is the print function for the netlink/kernel related errors and +.B +outfn +is the output function to print the result of list/save commands. +The +.B +p +pointer makes possible to pass arbitrary structure to the custom +print functions. If +.B +NULL +is passed instead of a function pointer, the default print function +is set for the given task. If any of the print functions is non-default, +then the +.I +version, +.I +help, +.I +interactive +ipset commands are ignored. + +.TP +ipset_session +The function returns the session structure +of the +.B +ipset +library structure, in order to manipulate the IO parameters. + +.TP +ipset_session_full_io +You can controll the full IO, i.e. input (restore) and output (save) +separatedly by the function. The +.B +session +parameter is the session structure of the library interface, +.B +filename +is the filename you want to use for input or output +and +.B +what +tells the function you want to set input or output file. +If there's an already opened file for the given IO mode, it is closed. +The function returns an error if normal mode is in use. If +.B +NULL +is passed instead of a filename, already opened file is closed +and the normal stream is set for the given IO mode (stdin for input, +stdout for output). Input/output files can be set separatedly. + +.TP +ipset_session_normal_io +You can controll the normal IO, which corresponds to the interface +provided by +.B +ipset(8) +itself. +.B +session +parameter is the session structure of the library interface, +.B +filename +is the filename you want to use for input or output +and +.B +what +tells the function you want to set input or output file. +If there's an already opened file for input/output, it is closed. +The function returns an error if full mode is in use. If +.B +NULL +is passed instead of a filename, already opened file is closed +and the normal stream is set for the given IO mode (stdin for input, +stdout for output). Input/output files cannot be set separatedly. + +.TP +ipset_session_io_stream +The function returns the stream set for the +.B +session +where +.B +what +tells the funtion you want to get the input or the output stream. + +.TP +ipset_session_io_close +The function closes the stream for the +.B +session +where +.B +what +tells the funtion you want to close the input or the output +stream. After closing, the standard streams are set: stdin for input, +stdout for output. + +.SH AUTHORS +ipset/libipset was designed and written by Jozsef Kadlecsik. + +.SH SEE ALSO +.BR ipset(8), +.br +/usr/include/libipset/ipset.h +/usr/include/libipset/session.h diff --git a/lib/libipset.map b/lib/libipset.map index 475fae5..a2383f3 100644 --- a/lib/libipset.map +++ b/lib/libipset.map @@ -173,3 +173,24 @@ LIBIPSET_4.7 { global: ipset_session_warning_as_error; } LIBIPSET_4.6; + +LIBIPSET_4.8 { +global: + ipset_parse_filename; + ipset_session; + ipset_is_interactive; + ipset_custom_printf; + ipset_parse_argv; + ipset_parse_line; + ipset_parse_stream; + ipset_init; + ipset_fini; + ipset_session_printf_private; + ipset_envopt_set; + ipset_envopt_unset; + ipset_session_print_outfn; + ipset_session_io_full; + ipset_session_io_normal; + ipset_session_io_stream; + ipset_session_io_close; +} LIBIPSET_4.7; @@ -13,7 +13,7 @@ #include <libipset/linux_ip_set.h> /* enum ipset_cmd */ #include <libipset/debug.h> /* D() */ #include <libipset/session.h> /* ipset_session_handle */ -#include <libipset/ui.h> /* IPSET_ENV_EXIST */ +#include <libipset/ipset.h> /* IPSET_ENV_EXIST */ #include <libipset/utils.h> /* UNUSED */ #include <libipset/mnl.h> /* prototypes */ diff --git a/lib/parse.c b/lib/parse.c index 4963d51..a88b9e2 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -1396,11 +1396,11 @@ ipset_parse_iptimeout(struct ipset_session *session, #define check_setname(str, saved) \ do { \ if (strlen(str) > IPSET_MAXNAMELEN - 1) { \ - int err; \ - err = syntax_err("setname '%s' is longer than %u characters",\ + int __err; \ + __err = syntax_err("setname '%s' is longer than %u characters",\ str, IPSET_MAXNAMELEN - 1); \ free(saved); \ - return err; \ + return __err; \ } \ } while (0) @@ -1876,34 +1876,6 @@ ipset_parse_skbprio(struct ipset_session *session, } /** - * ipset_parse_output - parse output format name - * @session: session structure - * @opt: option kind of the data - * @str: string to parse - * - * Parse output format names and set session mode. - * The value is stored in the session. - * - * Returns 0 on success or a negative error code. - */ -int -ipset_parse_output(struct ipset_session *session, - int opt UNUSED, const char *str) -{ - assert(session); - assert(str); - - if (STREQ(str, "plain")) - return ipset_session_output(session, IPSET_LIST_PLAIN); - else if (STREQ(str, "xml")) - return ipset_session_output(session, IPSET_LIST_XML); - else if (STREQ(str, "save")) - return ipset_session_output(session, IPSET_LIST_SAVE); - - return syntax_err("unknown output mode '%s'", str); -} - -/** * ipset_parse_ignored - "parse" ignored option * @session: session structure * @opt: option kind of the data diff --git a/lib/print.c b/lib/print.c index 7dd229e..02ffe41 100644 --- a/lib/print.c +++ b/lib/print.c @@ -23,7 +23,7 @@ #include <libipset/types.h> /* ipset set types */ #include <libipset/session.h> /* IPSET_FLAG_ */ #include <libipset/utils.h> /* UNUSED */ -#include <libipset/ui.h> /* IPSET_ENV_* */ +#include <libipset/ipset.h> /* IPSET_ENV_* */ #include <libipset/print.h> /* prototypes */ /* Print data (to output buffer). All function must follow snprintf. */ diff --git a/lib/session.c b/lib/session.c index 16b5549..e782573 100644 --- a/lib/session.c +++ b/lib/session.c @@ -10,6 +10,7 @@ #include <setjmp.h> /* setjmp, longjmp */ #include <stdio.h> /* snprintf */ #include <stdarg.h> /* va_* */ +#include <stdbool.h> /* bool */ #include <stdlib.h> /* free */ #include <string.h> /* str* */ #include <unistd.h> /* getpagesize */ @@ -25,7 +26,7 @@ #include <libipset/transport.h> /* transport */ #include <libipset/mnl.h> /* default backend */ #include <libipset/utils.h> /* STREQ */ -#include <libipset/ui.h> /* IPSET_ENV_* */ +#include <libipset/ipset.h> /* IPSET_ENV_* */ #include <libipset/session.h> /* prototypes */ #define IPSET_NEST_MAX 4 @@ -47,7 +48,11 @@ struct ipset_session { /* Output buffer */ char outbuf[IPSET_OUTBUFLEN]; /* Output buffer */ enum ipset_output_mode mode; /* Output mode */ - ipset_outfn outfn; /* Output function */ + ipset_print_outfn print_outfn; /* Output function to file */ + void *p; /* Private data for print_outfn */ + /* Session IO */ + bool normal_io, full_io; /* Default/normal/full IO */ + FILE *istream, *ostream; /* Session input/output stream */ /* Error/warning reporting */ char report[IPSET_ERRORBUFLEN]; /* Error/report buffer */ char *errmsg; @@ -115,41 +120,24 @@ ipset_session_lineno(struct ipset_session *session, uint32_t lineno) session->lineno = lineno; } -/* - * Environment options - */ - /** - * ipset_envopt_parse - parse/set environment option + * ipset_session_printf_private - returns the session private pointer * @session: session structure - * @opt: environment option - * @arg: option argument (unused) - * - * Parse and set an environment option. * - * Returns 0 on success or a negative error code. + * Returns the private pointer in the session structure, + * for private/custom print fuctions. */ -int -ipset_envopt_parse(struct ipset_session *session, int opt, - const char *arg UNUSED) +void * +ipset_session_printf_private(struct ipset_session *session) { assert(session); - - switch (opt) { - case IPSET_ENV_SORTED: - case IPSET_ENV_QUIET: - case IPSET_ENV_RESOLVE: - case IPSET_ENV_EXIST: - case IPSET_ENV_LIST_SETNAME: - case IPSET_ENV_LIST_HEADER: - session->envopts |= opt; - return 0; - default: - break; - } - return -1; + return session->p; } +/* + * Environment options + */ + /** * ipset_envopt_test - test environment option * @session: session structure @@ -167,6 +155,34 @@ ipset_envopt_test(struct ipset_session *session, enum ipset_envopt opt) } /** + * ipset_envopt_set - set environment option + * @session: session structure + * @opt: environment option + * + * Set an environment option of the session. + */ +void +ipset_envopt_set(struct ipset_session *session, enum ipset_envopt opt) +{ + assert(session); + session->envopts |= opt; +} + +/** + * ipset_envopt_unset - unset environment option + * @session: session structure + * @opt: environment option + * + * Unset an environment option of the session. + */ +void +ipset_envopt_unset(struct ipset_session *session, enum ipset_envopt opt) +{ + assert(session); + session->envopts &= ~opt; +} + +/** * ipset_session_output - set the session output mode * @session: session structure * @mode: output mode @@ -722,7 +738,8 @@ static const char cmd2name[][9] = { static inline int call_outfn(struct ipset_session *session) { - int ret = session->outfn("%s", session->outbuf); + int ret = session->print_outfn(session, session->p, + "%s", session->outbuf); session->outbuf[0] = '\0'; @@ -2034,29 +2051,57 @@ cleanup: return ret; } +static +int __attribute__ ((format (printf, 3, 4))) +default_print_outfn(struct ipset_session *session, void *p UNUSED, + const char *fmt, ...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = vfprintf(session->ostream, fmt, args); + va_end(args); + + return len; +} + /** - * ipset_session_outfn - set session output printing function + * ipset_session_print_outfn - set session output printing function + * @session: session structure + * @outfn: output printing function + * @p: pointer to private area * - * Set the session printing function. + * Set the session output printing function. If the @outfn is NULL, + * then the default output function is configured. You can set + * the @p pointer to a private area: the output printing function + * is called with @p in one of its arguments. * + * Returns 0 on success or a negative error code. */ int -ipset_session_outfn(struct ipset_session *session, ipset_outfn outfn) +ipset_session_print_outfn(struct ipset_session *session, + ipset_print_outfn outfn, + void *p) { - session->outfn = outfn ? outfn : printf; + session->print_outfn = outfn ? outfn : default_print_outfn; + session->p = p; return 0; } /** * ipset_session_init - initialize an ipset session + * @outfn: output printing function + * @p: pointer to private area * * Initialize an ipset session by allocating a session structure - * and filling out with the initialization data. + * and filling out with the initialization data. The function + * calls ipset_session_print_outfn() to set @print_outfn, @p. * * Returns the created session sctructure on success or NULL. */ struct ipset_session * -ipset_session_init(ipset_outfn outfn) +ipset_session_init(ipset_print_outfn print_outfn, void *p) { struct ipset_session *session; size_t bufsize = getpagesize(); @@ -2067,12 +2112,14 @@ ipset_session_init(ipset_outfn outfn) return NULL; session->bufsize = bufsize; session->buffer = session + 1; + session->istream = stdin; + session->ostream = stdout; /* The single transport method yet */ session->transport = &ipset_mnl_transport; /* Output function */ - session->outfn = outfn; + ipset_session_print_outfn(session, print_outfn, p); /* Initialize data structures */ session->data = ipset_data_init(); @@ -2088,6 +2135,197 @@ free_session: } /** + * ipset_session_io_full - set full IO for the session + * @session: session structure + * @filename: filename + * @what: operate on input/output + * + * The normal "-file" CLI interface does not provide an interface + * to set both the input (restore) and output (list/save) for + * a session. This function makes it possible to configure those. + * + * When a filename for input is passed, then the file will be opened + * for reading. + * When a filename for output is passed, then the file will be opened + * for writing. + * Previously opened files are closed. + * If NULL is passed as filename, stdin/stdout is set. + * Input/output files can be set separatedly. + * The function returns error if the file cannot be opened or + * normal IO mode is already set. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_session_io_full(struct ipset_session *session, const char *filename, + enum ipset_io_type what) +{ + FILE *f; + + assert(session); + + if (session->normal_io) + return ipset_err(session, + "Normal IO is in use, full IO cannot be selected"); + + switch (what) { + case IPSET_IO_INPUT: + if (session->istream != stdin) + fclose(session->istream); + if (!filename) { + session->istream = stdin; + } else { + f = fopen(filename, "r"); + if (!f) + return ipset_err(session, + "Cannot open %s for reading: %s", + filename, strerror(errno)); + session->istream = f; + } + break; + case IPSET_IO_OUTPUT: + if (session->ostream != stdout) + fclose(session->ostream); + if (!filename) { + session->ostream = stdout; + } else { + f = fopen(filename, "w"); + if (!f) + return ipset_err(session, + "Cannot open %s for writing: %s", + filename, strerror(errno)); + session->ostream = f; + } + break; + default: + return ipset_err(session, + "Library error, invalid ipset_io_type"); + } + session->full_io = !(session->istream == stdin && + session->ostream == stdout); + return 0; +} + +/** + * ipset_session_io_normal - set normal IO for the session + * @session: session structure + * @filename: filename + * @what: operate on input/output + * + * The normal "-file" CLI interface to set either the input (restore) + * or output (list/save) for a session. This function does not make + * possible to set both independently. + * + * When a filename for input is passed, then the file will be opened + * for reading. + * When a filename for output is passed, then the file will be opened + * for writing. + * Previously opened files are closed. + * If NULL is passed as filename, stdin/stdout is set. + * Input/output files cannot be set separatedly. + * The function returns error if the file cannot be opened or + * full IO mode is already set. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_session_io_normal(struct ipset_session *session, const char *filename, + enum ipset_io_type what) +{ + FILE *f; + + assert(session); + assert(filename); + + if (session->full_io) + return ipset_err(session, + "Full IO is in use, normal IO cannot be selected"); + if (session->istream != stdin) { + fclose(session->istream); + session->istream = stdin; + } + if (session->ostream != stdout) { + fclose(session->ostream); + session->ostream = stdout; + } + switch (what) { + case IPSET_IO_INPUT: + f = fopen(filename, "r"); + if (!f) + return ipset_err(session, + "Cannot open %s for reading: %s", + filename, strerror(errno)); + session->istream = f; + break; + case IPSET_IO_OUTPUT: + f = fopen(filename, "w"); + if (!f) + return ipset_err(session, + "Cannot open %s for writing: %s", + filename, strerror(errno)); + session->ostream = f; + break; + default: + return ipset_err(session, + "Library error, invalid ipset_io_type"); + } + session->normal_io = !(session->istream == stdin && + session->ostream == stdout); + return 0; +} + +/** + * ipset_session_io_stream - returns the input or output stream + * @what: operate on input/output + * + * Returns the input or output stream of the session. + */ +FILE * +ipset_session_io_stream(struct ipset_session *session, + enum ipset_io_type what) +{ + switch (what) { + case IPSET_IO_INPUT: + return session->istream; + case IPSET_IO_OUTPUT: + return session->ostream; + default: + return NULL; + } +} + +/** + * ipset_session_io_close - closes the input or output stream + * @what: operate on input/output + * + * Closes the input or output stream of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_session_io_close(struct ipset_session *session, + enum ipset_io_type what) +{ + switch (what) { + case IPSET_IO_INPUT: + if (session->istream != stdin) { + fclose(session->istream); + session->istream = stdin; + } + break; + case IPSET_IO_OUTPUT: + if (session->ostream != stdout) { + fclose(session->ostream); + session->ostream = stdout; + } + break; + default: + break; + } + return 0; +} + +/** * ipset_session_fini - destroy an ipset session * @session: session structure * @@ -2104,6 +2342,10 @@ ipset_session_fini(struct ipset_session *session) session->transport->fini(session->handle); if (session->data) ipset_data_fini(session->data); + if (session->istream != stdin) + fclose(session->istream); + if (session->ostream != stdout) + fclose(session->ostream); ipset_cache_fini(); free(session); diff --git a/lib/ui.c b/lib/ui.c deleted file mode 100644 index 7cb4bc2..0000000 --- a/lib/ui.c +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <stdio.h> /* printf */ -#include <libipset/icmp.h> /* id_to_icmp */ -#include <libipset/icmpv6.h> /* id_to_icmpv6 */ -#include <libipset/ui.h> /* prototypes */ - -/** - * ipset_port_usage - prints the usage for the port parameter - * - * Print the usage for the port parameter to stdout. - */ -void -ipset_port_usage(void) -{ - int i; - const char *name; - - printf(" [PROTO:]PORT is a valid pattern of the following:\n" - " PORTNAME TCP port name from /etc/services\n" - " PORTNUMBER TCP port number identifier\n" - " tcp|sctp|udp|udplite:PORTNAME|PORTNUMBER\n" - " icmp:CODENAME supported ICMP codename\n" - " icmp:TYPE/CODE ICMP type/code value\n" - " icmpv6:CODENAME supported ICMPv6 codename\n" - " icmpv6:TYPE/CODE ICMPv6 type/code value\n" - " PROTO:0 all other protocols\n\n"); - - printf(" Supported ICMP codenames:\n"); - i = 0; - while ((name = id_to_icmp(i++)) != NULL) - printf(" %s\n", name); - printf(" Supported ICMPv6 codenames:\n"); - i = 0; - while ((name = id_to_icmpv6(i++)) != NULL) - printf(" %s\n", name); -} diff --git a/src/Makefile.am b/src/Makefile.am index 810efb7..438fcec 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ include $(top_srcdir)/Make_global.am sbin_PROGRAMS = ipset -ipset_SOURCES = ipset.c ui.c +ipset_SOURCES = ipset.c ipset_LDADD = ../lib/libipset.la if ENABLE_SETTYPE_MODULES diff --git a/src/ipset.c b/src/ipset.c index 14a351a..fcb2247 100644 --- a/src/ipset.c +++ b/src/ipset.c @@ -7,898 +7,31 @@ * published by the Free Software Foundation. */ #include <assert.h> /* assert */ -#include <ctype.h> /* isspace */ -#include <errno.h> /* errno */ -#include <stdarg.h> /* va_* */ -#include <stdbool.h> /* bool */ -#include <stdio.h> /* fprintf, fgets */ +#include <stdio.h> /* fprintf */ #include <stdlib.h> /* exit */ -#include <string.h> /* str* */ #include <config.h> - -#include <libipset/debug.h> /* D() */ -#include <libipset/data.h> /* enum ipset_data */ -#include <libipset/parse.h> /* ipset_parse_* */ -#include <libipset/session.h> /* ipset_session_* */ -#include <libipset/types.h> /* struct ipset_type */ -#include <libipset/ui.h> /* core options, commands */ -#include <libipset/utils.h> /* STREQ */ - -static char program_name[] = PACKAGE; -static char program_version[] = PACKAGE_VERSION; - -static struct ipset_session *session; -static uint32_t restore_line; -static bool interactive; -static char cmdline[1024]; -static char *newargv[255]; -static int newargc; -static FILE *fd = NULL; -static const char *filename = NULL; - -enum exittype { - NO_PROBLEM = 0, - OTHER_PROBLEM, - PARAMETER_PROBLEM, - VERSION_PROBLEM, - SESSION_PROBLEM, -}; - -static int __attribute__((format(printf, 2, 3))) -exit_error(int status, const char *msg, ...) -{ - bool quiet = !interactive && - session && - ipset_envopt_test(session, IPSET_ENV_QUIET); - - if (status && msg && !quiet) { - va_list args; - - fprintf(stderr, "%s v%s: ", program_name, program_version); - va_start(args, msg); - vfprintf(stderr, msg, args); - va_end(args); - if (status != SESSION_PROBLEM) - fprintf(stderr, "\n"); - - if (status == PARAMETER_PROBLEM) - fprintf(stderr, - "Try `%s help' for more information.\n", - program_name); - } - /* Ignore errors in interactive mode */ - if (status && interactive) { - if (session) - ipset_session_report_reset(session); - return -1; - } - - if (session) - ipset_session_fini(session); - - D("status: %u", status); - if (fd) - fclose(fd); - exit(status > VERSION_PROBLEM ? OTHER_PROBLEM : status); - /* Unreached */ - return -1; -} - -static int -handle_error(void) -{ - if (ipset_session_warning(session) && - !ipset_envopt_test(session, IPSET_ENV_QUIET)) - fprintf(stderr, "Warning: %s\n", - ipset_session_warning(session)); - if (ipset_session_error(session)) - return exit_error(SESSION_PROBLEM, "%s", - ipset_session_error(session)); - - if (!interactive) { - ipset_session_fini(session); - if (fd) - fclose(fd); - exit(OTHER_PROBLEM); - } - - ipset_session_report_reset(session); - return -1; -} - -static void -help(void) -{ - const struct ipset_commands *c; - const struct ipset_envopts *opt = ipset_envopts; - - printf("%s v%s\n\n" - "Usage: %s [options] COMMAND\n\nCommands:\n", - program_name, program_version, program_name); - - for (c = ipset_commands; c->cmd; c++) - printf("%s %s\n", c->name[0], c->help); - printf("\nOptions:\n"); - - while (opt->flag) { - if (opt->help) - printf("%s %s\n", opt->name[0], opt->help); - opt++; - } -} - -int -ipset_parse_file(struct ipset_session *s UNUSED, - int opt UNUSED, const char *str) -{ - if (filename != NULL) - return exit_error(PARAMETER_PROBLEM, - "-file option can be specified once"); - filename = str; - - return 0; -} - -static -int __attribute__ ((format (printf, 1, 2))) -ipset_print_file(const char *fmt, ...) -{ - int len; - va_list args; - - assert(fd != NULL); - va_start(args, fmt); - len = vfprintf(fd, fmt, args); - va_end(args); - - return len; -} - -/* Build faked argv from parsed line */ -static void -build_argv(char *buffer) -{ - char *tmp, *arg; - int i; - bool quoted = false; - - /* Reset */ - for (i = 1; i < newargc; i++) { - if (newargv[i]) - free(newargv[i]); - newargv[i] = NULL; - } - newargc = 1; - - arg = calloc(strlen(buffer) + 1, sizeof(*buffer)); - for (tmp = buffer, i = 0; *tmp; tmp++) { - if ((newargc + 1) == (int)(sizeof(newargv)/sizeof(char *))) { - exit_error(PARAMETER_PROBLEM, - "Line is too long to parse."); - goto out; - } - switch (*tmp) { - case '"': - quoted = !quoted; - if (*(tmp+1)) - continue; - break; - case ' ': - case '\r': - case '\n': - case '\t': - if (!quoted) - break; - arg[i++] = *tmp; - continue; - default: - arg[i++] = *tmp; - if (*(tmp+1)) - continue; - break; - } - if (!*(tmp+1) && quoted) { - exit_error(PARAMETER_PROBLEM, "Missing close quote!"); - goto out; - } - if (!*arg) - continue; - newargv[newargc] = calloc(strlen(arg) + 1, sizeof(*arg)); - ipset_strlcpy(newargv[newargc++], arg, strlen(arg) + 1); - memset(arg, 0, strlen(arg) + 1); - i = 0; - } -out: - free(arg); -} - -/* Main parser function, workhorse */ -int parse_commandline(int argc, char *argv[]); - -/* - * Performs a restore from stdin - */ -static int -restore(char *argv0) -{ - int ret = 0; - char *c; - FILE *rfd = stdin; - - /* Initialize newargv/newargc */ - newargc = 0; - newargv[newargc] = calloc(strlen(argv0) + 1, sizeof(*argv0)); - ipset_strlcpy(newargv[newargc++], argv0, strlen(argv0) + 1); - if (filename) { - fd = fopen(filename, "r"); - if (!fd) { - return exit_error(OTHER_PROBLEM, - "Cannot open %s for reading: %s", - filename, strerror(errno)); - } - rfd = fd; - } - - while (fgets(cmdline, sizeof(cmdline), rfd)) { - restore_line++; - c = cmdline; - while (isspace(c[0])) - c++; - if (c[0] == '\0' || c[0] == '#') - continue; - else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) { - ret = ipset_commit(session); - if (ret < 0) - handle_error(); - continue; - } - /* Build faked argv, argc */ - build_argv(c); - - /* Execute line */ - ret = parse_commandline(newargc, newargv); - if (ret < 0) - handle_error(); - } - /* implicit "COMMIT" at EOF */ - ret = ipset_commit(session); - if (ret < 0) - handle_error(); - - free(newargv[0]); - return ret; -} - -static bool do_parse(const struct ipset_arg *arg, bool family) -{ - return !((family == true) ^ (arg->opt == IPSET_OPT_FAMILY)); -} - -static int -call_parser(int *argc, char *argv[], const struct ipset_type *type, - enum ipset_adt cmd, bool family) -{ - const struct ipset_arg *arg; - const char *optstr; - const struct ipset_type *t = type; - uint8_t revision = type->revision; - int ret = 0, i = 1, j; - - /* Currently CREATE and ADT may have got additional arguments */ - if (type->cmd[cmd].args[0] == IPSET_ARG_NONE && *argc > 1) - return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", - argv[i]); - - while (*argc > i) { - ret = -1; - for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { - arg = ipset_keyword(type->cmd[cmd].args[j]); - D("argc: %u, %s vs %s", i, argv[i], arg->name[0]); - if (!(ipset_match_option(argv[i], arg->name))) - continue; - - optstr = argv[i]; - /* Matched option */ - D("match %s, argc %u, i %u, %s", - arg->name[0], *argc, i + 1, - do_parse(arg, family) ? "parse" : "skip"); - i++; - ret = 0; - switch (arg->has_arg) { - case IPSET_MANDATORY_ARG: - if (*argc - i < 1) - return exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument " - "of option `%s'", - arg->name[0]); - /* Fall through */ - case IPSET_OPTIONAL_ARG: - if (*argc - i >= 1) { - if (do_parse(arg, family)) { - ret = ipset_call_parser( - session, arg, argv[i]); - if (ret < 0) - return ret; - } - i++; - break; - } - /* Fall through */ - default: - if (do_parse(arg, family)) { - ret = ipset_call_parser( - session, arg, optstr); - if (ret < 0) - return ret; - } - } - break; - } - if (ret < 0) - goto err_unknown; - } - if (!family) - *argc = 0; - return ret; - -err_unknown: - while ((type = ipset_type_higher_rev(t)) != t) { - for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { - arg = ipset_keyword(type->cmd[cmd].args[j]); - D("argc: %u, %s vs %s", i, argv[i], arg->name[0]); - if (ipset_match_option(argv[i], arg->name)) - return exit_error(PARAMETER_PROBLEM, - "Argument `%s' is supported in the kernel module " - "of the set type %s starting from the revision %u " - "and you have installed revision %u only. " - "Your kernel is behind your ipset utility.", - argv[i], type->name, - type->revision, revision); - } - t = type; - } - return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[i]); -} - -static enum ipset_adt -cmd2cmd(int cmd) -{ - switch (cmd) { - case IPSET_CMD_ADD: - return IPSET_ADD; - case IPSET_CMD_DEL: - return IPSET_DEL; - case IPSET_CMD_TEST: - return IPSET_TEST; - case IPSET_CMD_CREATE: - return IPSET_CREATE; - default: - return 0; - } -} - -static void -check_mandatory(const struct ipset_type *type, enum ipset_cmd command) -{ - enum ipset_adt cmd = cmd2cmd(command); - uint64_t flags = ipset_data_flags(ipset_session_data(session)); - uint64_t mandatory = type->cmd[cmd].need; - const struct ipset_arg *arg; - int i; - - /* Range can be expressed by ip/cidr */ - if (flags & IPSET_FLAG(IPSET_OPT_CIDR)) - flags |= IPSET_FLAG(IPSET_OPT_IP_TO); - - mandatory &= ~flags; - if (!mandatory) - return; - if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) { - exit_error(OTHER_PROBLEM, - "There are missing mandatory flags " - "but can't check them. " - "It's a bug, please report the problem."); - return; - } - - for (i = 0; type->cmd[cmd].args[i] != IPSET_ARG_NONE; i++) { - arg = ipset_keyword(type->cmd[cmd].args[i]); - if (mandatory & IPSET_FLAG(arg->opt)) { - exit_error(PARAMETER_PROBLEM, - "Mandatory option `%s' is missing", - arg->name[0]); - return; - } - } -} - -static const char * -cmd2name(enum ipset_cmd cmd) -{ - const struct ipset_commands *c; - - for (c = ipset_commands; c->cmd; c++) - if (cmd == c->cmd) - return c->name[0]; - return "unknown command"; -} - -static const char * -session_family(void) -{ - switch (ipset_data_family(ipset_session_data(session))) { - case NFPROTO_IPV4: - return "inet"; - case NFPROTO_IPV6: - return "inet6"; - default: - return "unspec"; - } -} - -static void -check_allowed(const struct ipset_type *type, enum ipset_cmd command) -{ - uint64_t flags = ipset_data_flags(ipset_session_data(session)); - enum ipset_adt cmd = cmd2cmd(command); - uint64_t allowed = type->cmd[cmd].full; - uint64_t cmdflags = command == IPSET_CMD_CREATE - ? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS; - const struct ipset_arg *arg; - enum ipset_opt i; - int j; - - /* Range can be expressed by ip/cidr or from-to */ - if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO)) - allowed |= IPSET_FLAG(IPSET_OPT_CIDR); - - for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) { - if (!(cmdflags & IPSET_FLAG(i)) || - (allowed & IPSET_FLAG(i)) || - !(flags & IPSET_FLAG(i))) - continue; - /* Not allowed element-expressions */ - switch (i) { - case IPSET_OPT_CIDR: - exit_error(OTHER_PROBLEM, - "IP/CIDR range is not allowed in command %s " - "with set type %s and family %s", - cmd2name(command), type->name, - session_family()); - return; - case IPSET_OPT_IP_TO: - exit_error(OTHER_PROBLEM, - "FROM-TO IP range is not allowed in command %s " - "with set type %s and family %s", - cmd2name(command), type->name, - session_family()); - return; - case IPSET_OPT_PORT_TO: - exit_error(OTHER_PROBLEM, - "FROM-TO port range is not allowed in command %s " - "with set type %s and family %s", - cmd2name(command), type->name, - session_family()); - return; - default: - break; - } - /* Other options */ - if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) { - exit_error(OTHER_PROBLEM, - "There are not allowed options (%u) " - "but option list is empty. " - "It's a bug, please report the problem.", i); - return; - } - for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) { - arg = ipset_keyword(type->cmd[cmd].args[j]); - if (arg->opt != i) - continue; - exit_error(OTHER_PROBLEM, - "%s parameter is not allowed in command %s " - "with set type %s and family %s", - arg->name[0], - cmd2name(command), type->name, - session_family()); - return; - } - exit_error(OTHER_PROBLEM, - "There are not allowed options (%u) " - "but can't resolve them. " - "It's a bug, please report the problem.", i); - return; - } -} - -static const struct ipset_type * -type_find(const char *name) -{ - const struct ipset_type *t = ipset_types(); - - while (t) { - if (ipset_match_typename(name, t)) - return t; - t = t->next; - } - return NULL; -} - -static enum ipset_adt cmd_help_order[] = { - IPSET_CREATE, - IPSET_ADD, - IPSET_DEL, - IPSET_TEST, - IPSET_CADT_MAX, -}; - -static const char *cmd_prefix[] = { - [IPSET_CREATE] = "create SETNAME", - [IPSET_ADD] = "add SETNAME", - [IPSET_DEL] = "del SETNAME", - [IPSET_TEST] = "test SETNAME", -}; - -/* Workhorse */ -int -parse_commandline(int argc, char *argv[]) -{ - int ret = 0; - enum ipset_cmd cmd = IPSET_CMD_NONE; - int i; - char *arg0 = NULL, *arg1 = NULL, *c; - const struct ipset_envopts *opt; - const struct ipset_commands *command; - const struct ipset_type *type; - - /* Set session lineno to report parser errors correctly */ - ipset_session_lineno(session, restore_line); - - /* Commandline parsing, somewhat similar to that of 'ip' */ - - /* First: parse core options */ - for (opt = ipset_envopts; opt->flag; opt++) { - for (i = 1; i < argc; ) { - if (!ipset_match_envopt(argv[i], opt->name)) { - i++; - continue; - } - /* Shift off matched option */ - ipset_shift_argv(&argc, argv, i); - switch (opt->has_arg) { - case IPSET_MANDATORY_ARG: - if (i + 1 > argc) - return exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument " - "to option %s", - opt->name[0]); - /* Fall through */ - case IPSET_OPTIONAL_ARG: - if (i + 1 <= argc) { - ret = opt->parse(session, opt->flag, - argv[i]); - if (ret < 0) - return handle_error(); - ipset_shift_argv(&argc, argv, i); - } - break; - case IPSET_NO_ARG: - ret = opt->parse(session, opt->flag, - opt->name[0]); - if (ret < 0) - return handle_error(); - break; - default: - break; - } - } - } - - /* Second: parse command */ - for (command = ipset_commands; - argc > 1 && command->cmd && cmd == IPSET_CMD_NONE; - command++) { - if (!ipset_match_cmd(argv[1], command->name)) - continue; - - if (restore_line != 0 && - (command->cmd == IPSET_CMD_RESTORE || - command->cmd == IPSET_CMD_VERSION || - command->cmd == IPSET_CMD_HELP)) - return exit_error(PARAMETER_PROBLEM, - "Command `%s' is invalid " - "in restore mode.", - command->name[0]); - if (interactive && command->cmd == IPSET_CMD_RESTORE) { - printf("Restore command ignored " - "in interactive mode\n"); - return 0; - } - - /* Shift off matched command arg */ - ipset_shift_argv(&argc, argv, 1); - cmd = command->cmd; - switch (command->has_arg) { - case IPSET_MANDATORY_ARG: - case IPSET_MANDATORY_ARG2: - if (argc < 2) - return exit_error(PARAMETER_PROBLEM, - "Missing mandatory argument " - "to command %s", - command->name[0]); - /* Fall through */ - case IPSET_OPTIONAL_ARG: - arg0 = argv[1]; - if (argc >= 2) - /* Shift off first arg */ - ipset_shift_argv(&argc, argv, 1); - break; - default: - break; - } - if (command->has_arg == IPSET_MANDATORY_ARG2) { - if (argc < 2) - return exit_error(PARAMETER_PROBLEM, - "Missing second mandatory " - "argument to command %s", - command->name[0]); - arg1 = argv[1]; - /* Shift off second arg */ - ipset_shift_argv(&argc, argv, 1); - } - break; - } - - /* Third: catch interactive mode, handle help, version */ - switch (cmd) { - case IPSET_CMD_NONE: - if (interactive) { - printf("No command specified\n"); - if (session) - ipset_envopt_parse(session, 0, "reset"); - return 0; - } - if (argc > 1 && STREQ(argv[1], "-")) { - interactive = true; - printf("%s> ", program_name); - /* Initialize newargv/newargc */ - newargv[newargc++] = program_name; - while (fgets(cmdline, sizeof(cmdline), stdin)) { - c = cmdline; - while (isspace(c[0])) - c++; - if (c[0] == '\0' || c[0] == '#') { - printf("%s> ", program_name); - continue; - } - /* Build fake argv, argc */ - build_argv(c); - /* Execute line: ignore soft errors */ - if (parse_commandline(newargc, newargv) < 0) - handle_error(); - printf("%s> ", program_name); - } - return exit_error(NO_PROBLEM, NULL); - } - if (argc > 1) - return exit_error(PARAMETER_PROBLEM, - "No command specified: unknown argument %s", - argv[1]); - return exit_error(PARAMETER_PROBLEM, "No command specified."); - case IPSET_CMD_VERSION: - printf("%s v%s, protocol version: %u\n", - program_name, program_version, IPSET_PROTOCOL); - if (interactive) - return 0; - return exit_error(NO_PROBLEM, NULL); - case IPSET_CMD_HELP: - help(); - - if (interactive || - !ipset_envopt_test(session, IPSET_ENV_QUIET)) { - if (arg0) { - const struct ipset_arg *arg; - int k; - - /* Type-specific help, without kernel checking */ - type = type_find(arg0); - if (!type) - return exit_error(PARAMETER_PROBLEM, - "Unknown settype: `%s'", arg0); - printf("\n%s type specific options:\n\n", type->name); - for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) { - cmd = cmd_help_order[i]; - printf("%s %s %s\n", - cmd_prefix[cmd], type->name, type->cmd[cmd].help); - for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) { - arg = ipset_keyword(type->cmd[cmd].args[k]); - if (!arg->help || arg->help[0] == '\0') - continue; - printf(" %s\n", arg->help); - } - } - printf("\n%s\n", type->usage); - if (type->usagefn) - type->usagefn(); - if (type->family == NFPROTO_UNSPEC) - printf("\nType %s is family neutral.\n", - type->name); - else if (type->family == NFPROTO_IPSET_IPV46) - printf("\nType %s supports inet " - "and inet6.\n", - type->name); - else - printf("\nType %s supports family " - "%s only.\n", - type->name, - type->family == NFPROTO_IPV4 - ? "inet" : "inet6"); - } else { - printf("\nSupported set types:\n"); - type = ipset_types(); - while (type) { - printf(" %s\t%s%u\t%s\n", - type->name, - strlen(type->name) < 12 ? "\t" : "", - type->revision, - type->description); - type = type->next; - } - } - } - if (interactive) - return 0; - return exit_error(NO_PROBLEM, NULL); - case IPSET_CMD_QUIT: - return exit_error(NO_PROBLEM, NULL); - default: - break; - } - - /* Forth: parse command args and issue the command */ - switch (cmd) { - case IPSET_CMD_CREATE: - /* Args: setname typename [type specific options] */ - ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); - if (ret < 0) - return handle_error(); - - ret = ipset_parse_typename(session, IPSET_OPT_TYPENAME, arg1); - if (ret < 0) - return handle_error(); - - type = ipset_type_get(session, cmd); - if (type == NULL) - return handle_error(); - - /* Parse create options: first check INET family */ - ret = call_parser(&argc, argv, type, IPSET_CREATE, true); - if (ret < 0) - return handle_error(); - else if (ret) - return ret; - - /* Parse create options: then check all options */ - ret = call_parser(&argc, argv, type, IPSET_CREATE, false); - if (ret < 0) - return handle_error(); - else if (ret) - return ret; - - /* Check mandatory, then allowed options */ - check_mandatory(type, cmd); - check_allowed(type, cmd); - - break; - case IPSET_CMD_LIST: - case IPSET_CMD_SAVE: - if (filename != NULL) { - fd = fopen(filename, "w"); - if (!fd) - return exit_error(OTHER_PROBLEM, - "Cannot open %s for writing: " - "%s", filename, - strerror(errno)); - ipset_session_outfn(session, ipset_print_file); - } - case IPSET_CMD_DESTROY: - case IPSET_CMD_FLUSH: - /* Args: [setname] */ - if (arg0) { - ret = ipset_parse_setname(session, - IPSET_SETNAME, arg0); - if (ret < 0) - return handle_error(); - } - break; - - case IPSET_CMD_RENAME: - case IPSET_CMD_SWAP: - /* Args: from-setname to-setname */ - ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); - if (ret < 0) - return handle_error(); - ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1); - if (ret < 0) - return handle_error(); - break; - - case IPSET_CMD_RESTORE: - /* Restore mode */ - if (argc > 1) - return exit_error(PARAMETER_PROBLEM, - "Unknown argument %s", argv[1]); - return restore(argv[0]); - case IPSET_CMD_ADD: - case IPSET_CMD_DEL: - case IPSET_CMD_TEST: - D("ADT: setname %s", arg0); - /* Args: setname ip [options] */ - ret = ipset_parse_setname(session, IPSET_SETNAME, arg0); - if (ret < 0) - return handle_error(); - - type = ipset_type_get(session, cmd); - if (type == NULL) - return handle_error(); - - ret = ipset_parse_elem(session, type->last_elem_optional, arg1); - if (ret < 0) - return handle_error(); - - /* Parse additional ADT options */ - ret = call_parser(&argc, argv, type, cmd2cmd(cmd), false); - if (ret < 0) - return handle_error(); - else if (ret) - return ret; - - /* Check mandatory, then allowed options */ - check_mandatory(type, cmd); - check_allowed(type, cmd); - - break; - default: - break; - } - - if (argc > 1) - return exit_error(PARAMETER_PROBLEM, - "Unknown argument %s", argv[1]); - ret = ipset_cmd(session, cmd, restore_line); - D("ret %d", ret); - /* Special case for TEST and non-quiet mode */ - if (cmd == IPSET_CMD_TEST && ipset_session_warning(session)) { - if (!ipset_envopt_test(session, IPSET_ENV_QUIET)) - fprintf(stderr, "%s", ipset_session_warning(session)); - ipset_session_report_reset(session); - } - if (ret < 0) - handle_error(); - - return ret; -} +#include <libipset/ipset.h> /* ipset library */ int main(int argc, char *argv[]) { + struct ipset *ipset; int ret; /* Load set types */ ipset_load_types(); - /* Initialize session */ - session = ipset_session_init(printf); - if (session == NULL) - return exit_error(OTHER_PROBLEM, - "Cannot initialize ipset session, aborting."); + /* Initialize ipset library */ + ipset = ipset_init(); + if (ipset == NULL) { + fprintf(stderr, "Cannot initialize ipset, aborting."); + exit(1); + } - ret = parse_commandline(argc, argv); + ret = ipset_parse_argv(ipset, argc, argv); - ipset_session_fini(session); - if (fd) - fclose(fd); + ipset_fini(ipset); return ret; } |