summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Make_global.am2
-rw-r--r--include/libipset/Makefile.am2
-rw-r--r--include/libipset/ipset.h90
-rw-r--r--include/libipset/parse.h2
-rw-r--r--include/libipset/session.h36
-rw-r--r--include/libipset/ui.h56
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/ipset.c1472
-rw-r--r--lib/ipset_hash_ipport.c2
-rw-r--r--lib/ipset_hash_ipportip.c2
-rw-r--r--lib/ipset_hash_ipportnet.c2
-rw-r--r--lib/ipset_hash_netiface.c2
-rw-r--r--lib/ipset_hash_netport.c2
-rw-r--r--lib/ipset_hash_netportnet.c2
-rw-r--r--lib/libipset.3242
-rw-r--r--lib/libipset.map21
-rw-r--r--lib/mnl.c2
-rw-r--r--lib/parse.c34
-rw-r--r--lib/print.c2
-rw-r--r--lib/session.c316
-rw-r--r--lib/ui.c42
-rw-r--r--src/Makefile.am2
-rw-r--r--src/ipset.c889
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;
diff --git a/lib/mnl.c b/lib/mnl.c
index 4e075cf..4ce90b4 100644
--- a/lib/mnl.c
+++ b/lib/mnl.c
@@ -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;
}