diff options
Diffstat (limited to 'lib/parse.c')
-rw-r--r-- | lib/parse.c | 121 |
1 files changed, 93 insertions, 28 deletions
diff --git a/lib/parse.c b/lib/parse.c index 31a619d..4d2d8b3 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -41,6 +41,9 @@ #define syntax_err(fmt, args...) \ ipset_err(session, "Syntax error: " fmt , ## args) +#define syntax_err_ll(errtype, fmt, args...) \ + ipset_session_report(session, errtype, "Syntax error: " fmt , ## args) + static char * ipset_strchr(const char *str, const char *sep) { @@ -87,7 +90,8 @@ string_to_number_ll(struct ipset_session *session, const char *str, unsigned long long min, unsigned long long max, - unsigned long long *ret) + unsigned long long *ret, + enum ipset_err_type errtype) { unsigned long long number = 0; char *end; @@ -104,23 +108,24 @@ string_to_number_ll(struct ipset_session *session, errno = ERANGE; } if (errno == ERANGE && max) - return syntax_err("'%s' is out of range %llu-%llu", - str, min, max); + return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu", + str, min, max); else if (errno == ERANGE) - return syntax_err("'%s' is out of range %llu-%llu", - str, min, ULLONG_MAX); + return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu", + str, min, ULLONG_MAX); else - return syntax_err("'%s' is invalid as number", str); + return syntax_err_ll(errtype, "'%s' is invalid as number", str); } static int string_to_u8(struct ipset_session *session, - const char *str, uint8_t *ret) + const char *str, uint8_t *ret, + enum ipset_err_type errtype) { int err; unsigned long long num = 0; - err = string_to_number_ll(session, str, 0, 255, &num); + err = string_to_number_ll(session, str, 0, 255, &num, errtype); *ret = num; return err; @@ -130,7 +135,7 @@ static int string_to_cidr(struct ipset_session *session, const char *str, uint8_t min, uint8_t max, uint8_t *ret) { - int err = string_to_u8(session, str, ret); + int err = string_to_u8(session, str, ret, IPSET_ERROR); if (!err && (*ret < min || *ret > max)) return syntax_err("'%s' is out of range %u-%u", @@ -141,12 +146,13 @@ string_to_cidr(struct ipset_session *session, static int string_to_u16(struct ipset_session *session, - const char *str, uint16_t *ret) + const char *str, uint16_t *ret, + enum ipset_err_type errtype) { int err; unsigned long long num = 0; - err = string_to_number_ll(session, str, 0, USHRT_MAX, &num); + err = string_to_number_ll(session, str, 0, USHRT_MAX, &num, errtype); *ret = num; return err; @@ -159,7 +165,8 @@ string_to_u32(struct ipset_session *session, int err; unsigned long long num = 0; - err = string_to_number_ll(session, str, 0, UINT_MAX, &num); + err = string_to_number_ll(session, str, 0, UINT_MAX, &num, + IPSET_ERROR); *ret = num; return err; @@ -274,7 +281,10 @@ parse_portname(struct ipset_session *session, const char *str, uint16_t *port, const char *proto) { char *saved, *tmp; + const char *protoname; + const struct protoent *protoent; struct servent *service; + uint8_t protonum = 0; saved = tmp = ipset_strdup(session, str); if (tmp == NULL) @@ -283,7 +293,15 @@ parse_portname(struct ipset_session *session, const char *str, if (tmp == NULL) goto error; - service = getservbyname(tmp, proto); + protoname = proto; + if (string_to_u8(session, proto, &protonum, IPSET_WARNING) == 0) { + protoent = getprotobynumber(protonum); + if (protoent == NULL) + goto error; + protoname = protoent->p_name; + } + + service = getservbyname(tmp, protoname); if (service != NULL) { *port = ntohs((uint16_t) service->s_port); free(saved); @@ -319,11 +337,11 @@ ipset_parse_port(struct ipset_session *session, assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO); assert(str); - if (parse_portname(session, str, &port, proto) == 0) { + if (string_to_u16(session, str, &port, IPSET_WARNING) == 0) { return ipset_session_data_set(session, opt, &port); } /* Error is stored as warning in session report */ - if (string_to_u16(session, str, &port) == 0) { + if (parse_portname(session, str, &port, proto) == 0) { /* No error, so reset false error messages */ ipset_session_report_reset(session); return ipset_session_data_set(session, opt, &port); @@ -469,21 +487,23 @@ ipset_parse_proto(struct ipset_session *session, { const struct protoent *protoent; uint8_t proto = 0; + uint8_t protonum = 0; assert(session); assert(opt == IPSET_OPT_PROTO); assert(str); + if (string_to_u8(session, str, &protonum, IPSET_WARNING) == 0) + return ipset_session_data_set(session, opt, &protonum); + + /* No error, so reset false error messages */ + ipset_session_report_reset(session); protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0 ? "ipv6-icmp" : str); - if (protoent == NULL) { - uint8_t protonum = 0; - if (string_to_u8(session, str, &protonum) || - (protoent = getprotobynumber(protonum)) == NULL) - return syntax_err("cannot parse '%s' " - "as a protocol", str); - } + if (protoent == NULL) + return syntax_err("cannot parse '%s' " + "as a protocol", str); proto = protoent->p_proto; if (!proto) return syntax_err("Unsupported protocol '%s'", str); @@ -513,8 +533,8 @@ parse_icmp_typecode(struct ipset_session *session, str, family); } *a++ = '\0'; - if ((err = string_to_u8(session, tmp, &type)) != 0 || - (err = string_to_u8(session, a, &code)) != 0) + if ((err = string_to_u8(session, tmp, &type, IPSET_ERROR)) != 0 || + (err = string_to_u8(session, a, &code, IPSET_ERROR)) != 0) goto error; typecode = (type << 8) | code; @@ -1335,7 +1355,8 @@ ipset_parse_timeout(struct ipset_session *session, assert(opt == IPSET_OPT_TIMEOUT); assert(str); - err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum); + err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum, + IPSET_ERROR); if (err == 0) { /* Timeout is expected to be 32bits wide, so we have to convert it here */ @@ -1579,7 +1600,8 @@ ipset_parse_uint64(struct ipset_session *session, assert(session); assert(str); - err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value); + err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value, + IPSET_ERROR); if (err) return err; @@ -1623,7 +1645,7 @@ ipset_parse_uint16(struct ipset_session *session, assert(session); assert(str); - err = string_to_u16(session, str, &value); + err = string_to_u16(session, str, &value, IPSET_ERROR); if (err == 0) return ipset_session_data_set(session, opt, &value); @@ -1651,7 +1673,7 @@ ipset_parse_uint8(struct ipset_session *session, assert(session); assert(str); - if ((err = string_to_u8(session, str, &value)) == 0) + if ((err = string_to_u8(session, str, &value, IPSET_ERROR)) == 0) return ipset_session_data_set(session, opt, &value); return err; @@ -1682,6 +1704,9 @@ ipset_parse_netmask(struct ipset_session *session, assert(str); data = ipset_session_data(session); + if (ipset_data_test(data, IPSET_OPT_BITMASK)) + return syntax_err("bitmask and netmask are mutually exclusive, provide only one"); + family = ipset_data_family(data); if (family == NFPROTO_UNSPEC) { family = NFPROTO_IPV4; @@ -1701,6 +1726,46 @@ ipset_parse_netmask(struct ipset_session *session, } /** + * ipset_parse_bitmask - parse string as a bitmask + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a bitmask value, depending on family type. + * If family is not set yet, INET is assumed. + * The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_bitmask(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + uint8_t family; + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_BITMASK); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_test(data, IPSET_OPT_NETMASK)) + return syntax_err("bitmask and netmask are mutually exclusive, provide only one"); + + family = ipset_data_family(data); + if (family == NFPROTO_UNSPEC) { + family = NFPROTO_IPV4; + ipset_data_set(data, IPSET_OPT_FAMILY, &family); + } + + if (parse_ipaddr(session, opt, str, family)) + return syntax_err("bitmask is not valid for family = %s", + family == NFPROTO_IPV4 ? "inet" : "inet6"); + + return 0; +} + +/** * ipset_parse_flag - "parse" option flags * @session: session structure * @opt: option kind of the data |