From: Jozsef Kadlecsik Date: Sat, 14 Jan 2012 14:06:00 +0000 (+0100) Subject: Support hostnames and service names with dash X-Git-Tag: v6.11~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=36e483b303b11890e870eba5a8c798b170244cb3;p=ipset Support hostnames and service names with dash The square brackets are introduced as an escape mechanism to enter hostnames or service names with dash in order to avoid mixing up the dash in the name with the range notation. Problem reported by Stephen Hemminger and Marc Guardiola. --- diff --git a/include/libipset/parse.h b/include/libipset/parse.h index 9f64a70..d18e3cc 100644 --- a/include/libipset/parse.h +++ b/include/libipset/parse.h @@ -15,6 +15,8 @@ #define IPSET_ELEM_SEPARATOR "," #define IPSET_NAME_SEPARATOR "," #define IPSET_PROTO_SEPARATOR ":" +#define IPSET_ESCAPE_START "[" +#define IPSET_ESCAPE_END "]" struct ipset_session; struct ipset_arg; diff --git a/lib/parse.c b/lib/parse.c index 0ee34c3..30efdb6 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -59,6 +59,20 @@ ipset_strchr(const char *str, const char *sep) return NULL; } +static char * +escape_range_separator(const char *str) +{ + const char *tmp = NULL; + + if (STRNEQ(str, IPSET_ESCAPE_START, 1)) { + tmp = strstr(str, IPSET_ESCAPE_END); + if (tmp == NULL) + return NULL; + } + + return range_separator(tmp == NULL ? str : tmp); +} + /* * Parser functions, shamelessly taken from iptables.c, ip6tables.c * and parser.c from libnetfilter_conntrack. @@ -194,6 +208,60 @@ error: return syntax_err("cannot parse '%s' as ethernet address", str); } +static char * +ipset_strdup(struct ipset_session *session, const char *str) +{ + char *tmp = strdup(str); + + if (tmp == NULL) + ipset_err(session, + "Cannot allocate memory to duplicate %s.", + str); + return tmp; +} + +static char * +find_range_separator(struct ipset_session *session, char *str) +{ + char *esc; + + if (STRNEQ(str, IPSET_ESCAPE_START, 1)) { + esc = strstr(str, IPSET_ESCAPE_END); + if (esc == NULL) { + syntax_err("cannot find closing escape character " + "'%s' in %s", IPSET_ESCAPE_END, str); + return str; + } + if (esc[1] == '\0') + /* No range separator, just a single escaped elem */ + return NULL; + esc++; + if (!STRNEQ(esc, IPSET_RANGE_SEPARATOR, 1)) { + *esc = '\0'; + syntax_err("range separator expected after " + "'%s'", str); + return str; + } + return esc; + } + return range_separator(str); +} + +static char * +strip_escape(struct ipset_session *session, char * str) +{ + if (STRNEQ(str, IPSET_ESCAPE_START, 1)) { + if (!STREQ(str + strlen(str) - 1, IPSET_ESCAPE_END)) { + syntax_err("cannot find closing escape character " + "'%s' in %s", IPSET_ESCAPE_END, str); + return NULL; + } + str++; + str[strlen(str) - 1] = '\0'; + } + return str; +} + /* * Parse TCP service names or port numbers */ @@ -201,13 +269,25 @@ static int parse_portname(struct ipset_session *session, const char *str, uint16_t *port, const char *proto) { - struct servent *service = getservbyname(str, proto); + char *saved, *tmp; + struct servent *service; + saved = tmp = ipset_strdup(session, str); + if (tmp == NULL) + return -1; + tmp = strip_escape(session, tmp); + if (tmp == NULL) + goto error; + + service = getservbyname(tmp, proto); if (service != NULL) { *port = ntohs((uint16_t) service->s_port); + free(saved); return 0; } +error: + free(saved); return syntax_err("cannot parse '%s' as a %s port", str, proto); } @@ -270,13 +350,16 @@ ipset_parse_tcpudp_port(struct ipset_session *session, assert(opt == IPSET_OPT_PORT); assert(str); - saved = tmp = strdup(str); + saved = tmp = ipset_strdup(session, str); if (tmp == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; + + a = find_range_separator(session, tmp); + if (a == tmp) { + err = -1; + goto error; + } - a = range_separator(tmp); if (a != NULL) { /* port-port */ *a++ = '\0'; @@ -378,11 +461,9 @@ parse_icmp_typecode(struct ipset_session *session, char *a, *saved, *tmp; int err; - saved = tmp = strdup(str); + saved = tmp = ipset_strdup(session, str); if (tmp == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; a = cidr_separator(tmp); if (a == NULL) { free(saved); @@ -485,11 +566,9 @@ ipset_parse_proto_port(struct ipset_session *session, assert(str); data = ipset_session_data(session); - saved = tmp = strdup(str); + saved = tmp = ipset_strdup(session, str); if (tmp == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; a = proto_separator(tmp); if (a != NULL) { @@ -536,7 +615,7 @@ ipset_parse_proto_port(struct ipset_session *session, } goto error; } else { - proto = "TCP"; + proto = "tcp"; err = ipset_data_set(data, IPSET_OPT_PROTO, &p); if (err) goto error; @@ -679,7 +758,7 @@ parse_ipaddr(struct ipset_session *session, { uint8_t m = family == NFPROTO_IPV4 ? 32 : 128; int aerr = EINVAL, err = 0, range = 0; - char *saved = strdup(str); + char *saved = ipset_strdup(session, str); char *a, *tmp = saved; struct addrinfo *info; enum ipset_opt copt, opt2; @@ -693,9 +772,7 @@ parse_ipaddr(struct ipset_session *session, } if (tmp == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; if ((a = cidr_separator(tmp)) != NULL) { /* IP/mask */ *a++ = '\0'; @@ -703,16 +780,33 @@ parse_ipaddr(struct ipset_session *session, if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 || (err = ipset_session_data_set(session, copt, &m)) != 0) goto out; - } else if ((a = range_separator(tmp)) != NULL) { - /* IP-IP */ - *a++ = '\0'; - D("range %s", a); - range++; + } else { + a = find_range_separator(session, tmp); + if (a == tmp) { + err = -1; + goto out; + } + if (a != NULL) { + /* IP-IP */ + *a++ = '\0'; + D("range %s", a); + range++; + } + } + tmp = strip_escape(session, tmp); + if (tmp == NULL) { + err = -1; + goto out; } if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0 || !range) goto out; freeaddrinfo(info); + a = strip_escape(session, a); + if (a == NULL) { + err = -1; + goto out; + } aerr = get_addrinfo(session, opt2, a, &info, family); out: @@ -754,18 +848,18 @@ parse_ip(struct ipset_session *session, switch (addrtype) { case IPADDR_PLAIN: - if (range_separator(str) || + if (escape_range_separator(str) || (cidr_separator(str) && !cidr_hostaddr(str, family))) return syntax_err("plain IP address must be supplied: " "%s", str); break; case IPADDR_NET: - if (!cidr_separator(str) || range_separator(str)) + if (!cidr_separator(str) || escape_range_separator(str)) return syntax_err("IP/netblock must be supplied: %s", str); break; case IPADDR_RANGE: - if (!range_separator(str) || cidr_separator(str)) + if (!escape_range_separator(str) || cidr_separator(str)) return syntax_err("IP-IP range must supplied: %s", str); break; @@ -896,7 +990,7 @@ ipset_parse_netrange(struct ipset_session *session, assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); - if (!(range_separator(str) || cidr_separator(str))) + if (!(escape_range_separator(str) || cidr_separator(str))) return syntax_err("IP/cidr or IP-IP range must be specified: " "%s", str); return parse_ip(session, opt, str, IPADDR_ANY); @@ -949,7 +1043,7 @@ ipset_parse_ipnet(struct ipset_session *session, assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); - if (range_separator(str)) + if (escape_range_separator(str)) return syntax_err("IP address or IP/cidr must be specified: %s", str); return parse_ip(session, opt, str, IPADDR_ANY); @@ -1065,11 +1159,9 @@ ipset_parse_iptimeout(struct ipset_session *session, IPSET_FLAG(IPSET_OPT_TIMEOUT))) return syntax_err("mixed syntax, timeout already specified"); - tmp = saved = strdup(str); + tmp = saved = ipset_strdup(session, str); if (saved == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return 1; a = elem_separator(tmp); if (a == NULL) { @@ -1127,11 +1219,9 @@ ipset_parse_name_compat(struct ipset_session *session, if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) syntax_err("mixed syntax, before|after option already used"); - tmp = saved = strdup(str); + tmp = saved = ipset_strdup(session, str); if (saved == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; if ((a = elem_separator(tmp)) != NULL) { /* setname,[before|after,setname */ *a++ = '\0'; @@ -1565,11 +1655,9 @@ ipset_parse_elem(struct ipset_session *session, return ipset_err(session, "Internal error: set type is unknown!"); - saved = tmp = strdup(str); + saved = tmp = ipset_strdup(session, str); if (tmp == NULL) - return ipset_err(session, - "Cannot allocate memory to duplicate %s.", - str); + return -1; a = elem_separator(tmp); if (type->dimension > IPSET_DIM_ONE) { diff --git a/lib/print.c b/lib/print.c index 662baae..f2f848b 100644 --- a/lib/print.c +++ b/lib/print.c @@ -158,13 +158,23 @@ __getnameinfo4(char *buf, unsigned int len, sizeof(saddr), buf, len, NULL, 0, flags); - if (!(flags & NI_NUMERICHOST) && - (err == EAI_AGAIN || (err == 0 && strchr(buf, '-') != NULL))) + if (!(flags & NI_NUMERICHOST) && (err == EAI_AGAIN)) err = getnameinfo((const struct sockaddr *)&saddr, sizeof(saddr), buf, len, NULL, 0, flags | NI_NUMERICHOST); D("getnameinfo err: %i, errno %i", err, errno); + if (err == 0 && strstr(buf, IPSET_RANGE_SEPARATOR) != NULL) { + const char escape[] = IPSET_ESCAPE_START; + /* Escape hostname */ + if (strlen(buf) + 2 > len) { + err = EAI_OVERFLOW; + return -1; + } + memmove(buf + 1, buf, strlen(buf) + 1); + buf[0] = escape[0]; + strcat(buf, IPSET_ESCAPE_END); + } return (err == 0 ? (int)strlen(buf) : (err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1); } @@ -184,13 +194,23 @@ __getnameinfo6(char *buf, unsigned int len, sizeof(saddr), buf, len, NULL, 0, flags); - if (!(flags & NI_NUMERICHOST) && - (err == EAI_AGAIN || (err == 0 && strchr(buf, '-') != NULL))) + if (!(flags & NI_NUMERICHOST) && (err == EAI_AGAIN)) err = getnameinfo((const struct sockaddr *)&saddr, sizeof(saddr), buf, len, NULL, 0, flags | NI_NUMERICHOST); D("getnameinfo err: %i, errno %i", err, errno); + if (err == 0 && strstr(buf, IPSET_RANGE_SEPARATOR) != NULL) { + const char escape[] = IPSET_ESCAPE_START; + /* Escape hostname */ + if (strlen(buf) + 2 > len) { + err = EAI_OVERFLOW; + return -1; + } + memmove(buf + 1, buf, strlen(buf) + 1); + buf[0] = escape[0]; + strcat(buf, IPSET_ESCAPE_END); + } return (err == 0 ? (int)strlen(buf) : (err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1); } diff --git a/src/ipset.8 b/src/ipset.8 index 3c52048..a2ee39f 100644 --- a/src/ipset.8 +++ b/src/ipset.8 @@ -256,6 +256,12 @@ as \fBnomatch\fR are skipped as if those were no added to the set, which makes possible to build up sets with exceptions. See the example at hash type \fBhash:net\fR below. +If host names or service names with dash in the name are used instead of IP +addresses or service numbers, then the host name or service name must be enclosed +in square brackets. Example: + +.IP +ipset add foo [test\-hostname],[ftp\-data] .SS bitmap:ip The \fBbitmap:ip\fR set type uses a memory range to store either IPv4 host (default) or IPv4 network addresses. A \fBbitmap:ip\fR type of set can store up