]> granicus.if.org Git - ipset/commitdiff
Support hostnames and service names with dash
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Sat, 14 Jan 2012 14:06:00 +0000 (15:06 +0100)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Sat, 14 Jan 2012 14:06:00 +0000 (15:06 +0100)
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.

include/libipset/parse.h
lib/parse.c
lib/print.c
src/ipset.8

index 9f64a7098dc16e7f3e514daf092050e603d583ac..d18e3cc9c5a4f02b17b0a9da43a863cee8ebc0f8 100644 (file)
@@ -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;
index 0ee34c3aecbcdc5fe723fa0c523afc5bd44dd7d5..30efdb6e2346b905210d40de77eb2cc88df0beb9 100644 (file)
@@ -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) {
index 662baaef569620b274560dd088734f8445d8ebdc..f2f848bf2d9bc76925652c94a77ba8c4cda8eff1 100644 (file)
@@ -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);
 }
index 3c520481bcb25b59bff3e027f357c1694edb31f5..a2ee39ff4f5e4348cf2c40d888b5903104a9f174 100644 (file)
@@ -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