| /* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu) |
| * Patrick Schaaf (bof@bof.de) |
| * Copyright 2003-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> /* fprintf, fgets */ |
| #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."); |
| return; |
| } |
| 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!"); |
| return; |
| } |
| 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; |
| } |
| 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_arg *args, bool family) |
| { |
| int ret = 0, i = 1; |
| const struct ipset_arg *arg; |
| const char *optstr; |
| |
| /* Currently CREATE and ADT may have got additional arguments */ |
| if (!args && *argc > 1) |
| goto err_unknown; |
| while (*argc > i) { |
| ret = -1; |
| for (arg = args; arg->opt; arg++) { |
| 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: |
| 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->mandatory[cmd]; |
| const struct ipset_arg *arg = type->args[cmd]; |
| |
| /* 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 (!arg) { |
| exit_error(OTHER_PROBLEM, |
| "There are missing mandatory flags " |
| "but can't check them. " |
| "It's a bug, please report the problem."); |
| return; |
| } |
| |
| for (; arg->opt; arg++) |
| 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->full[cmd]; |
| uint64_t cmdflags = command == IPSET_CMD_CREATE |
| ? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS; |
| const struct ipset_arg *arg = type->args[cmd]; |
| enum ipset_opt i; |
| |
| /* 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 (!arg) { |
| exit_error(OTHER_PROBLEM, |
| "There are not allowed options (%u) " |
| "but option list is NULL. " |
| "It's a bug, please report the problem.", i); |
| return; |
| } |
| for (; arg->opt; arg++) { |
| 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; |
| } |
| |
| /* 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) { |
| /* 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%s", |
| type->name, 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->args[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->args[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->args[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; |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| 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."); |
| |
| ret = parse_commandline(argc, argv); |
| |
| ipset_session_fini(session); |
| if (fd) |
| fclose(fd); |
| |
| return ret; |
| } |