diff options
author | Ben Pfaff <blp@ovn.org> | 2018-04-11 11:12:21 -0700 |
---|---|---|
committer | Ben Pfaff <blp@ovn.org> | 2018-04-16 14:52:56 -0700 |
commit | 0b043300dbade23ce9435b7dd308473fea41e6fe (patch) | |
tree | 894f3f439c83e17b8d3806644eb7b19f8b13db89 /lib | |
parent | 1bb011218d046b5de765658e9f5fe45289462eea (diff) | |
download | openvswitch-0b043300dbade23ce9435b7dd308473fea41e6fe.tar.gz |
Make <host>:<port> parsing uniform treewide.
I didn't realize until now that the tree had two different ways of parsing
strings in the form <host>:<port> and <port>:<host>. There are the
long-standing inet_parse_active() and inet_parse_passive() functions, and
more recently the ipv46_parse() function. This commit eliminates the
latter and changes the code to use the former.
The two implementations interpreted some input differently. In particular,
the older functions required IPv6 addresses to be [bracketed], but the
newer ones do not. For compatibility this patch changes the merged code to
use the more liberal interpretation.
Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Mark Michelson <mmichels@redhat.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/packets.c | 76 | ||||
-rw-r--r-- | lib/packets.h | 10 | ||||
-rw-r--r-- | lib/socket-util.c | 140 | ||||
-rw-r--r-- | lib/socket-util.h | 3 |
4 files changed, 86 insertions, 143 deletions
diff --git a/lib/packets.c b/lib/packets.c index 8ebae8cc8..38bfb6015 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -652,82 +652,6 @@ ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen) return error; } -/* Parses the string into an IPv4 or IPv6 address. - * The port flags act as follows: - * * PORT_OPTIONAL: A port may be present but is not required - * * PORT_REQUIRED: A port must be present - * * PORT_FORBIDDEN: A port must not be present - */ -char * OVS_WARN_UNUSED_RESULT -ipv46_parse(const char *s, enum port_flags flags, struct sockaddr_storage *ss) -{ - char *error = NULL; - - char *copy; - copy = xstrdup(s); - - char *addr; - char *port; - if (*copy == '[') { - char *end; - - addr = copy + 1; - end = strchr(addr, ']'); - if (!end) { - error = xasprintf("No closing bracket on address %s", s); - goto finish; - } - *end++ = '\0'; - if (*end == ':') { - port = end + 1; - } else { - port = NULL; - } - } else { - addr = copy; - port = strchr(copy, ':'); - if (port) { - if (strchr(port + 1, ':')) { - port = NULL; - } else { - *port++ = '\0'; - } - } - } - - if (port && !*port) { - error = xasprintf("Port is an empty string"); - goto finish; - } - - if (port && flags == PORT_FORBIDDEN) { - error = xasprintf("Port forbidden in address %s", s); - goto finish; - } else if (!port && flags == PORT_REQUIRED) { - error = xasprintf("Port required in address %s", s); - goto finish; - } - - struct addrinfo hints = { - .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV, - .ai_family = AF_UNSPEC, - }; - struct addrinfo *res; - int status; - status = getaddrinfo(addr, port, &hints, &res); - if (status) { - error = xasprintf("Error parsing address %s: %s", - s, gai_strerror(status)); - goto finish; - } - memcpy(ss, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - -finish: - free(copy); - return error; -} - /* Parses string 's', which must be an IPv6 address. Stores the IPv6 address * into '*ip'. Returns true if successful, otherwise false. */ bool diff --git a/lib/packets.h b/lib/packets.h index 9a71aa3ab..b2bf70697 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1362,16 +1362,6 @@ struct in6_addr ipv6_create_mask(int mask); int ipv6_count_cidr_bits(const struct in6_addr *netmask); bool ipv6_is_cidr(const struct in6_addr *netmask); -enum port_flags { - PORT_OPTIONAL, - PORT_REQUIRED, - PORT_FORBIDDEN, -}; - -char *ipv46_parse(const char *s, enum port_flags flags, - struct sockaddr_storage *ss) - OVS_WARN_UNUSED_RESULT; - bool ipv6_parse(const char *s, struct in6_addr *ip); char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask); diff --git a/lib/socket-util.c b/lib/socket-util.c index 223e3780b..0964a015e 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -333,39 +333,89 @@ guess_netmask(ovs_be32 ip_) : htonl(0)); /* ??? */ } -/* This is like strsep() except: - * - * - The separator string is ":". - * - * - Square brackets [] quote ":" separators and are removed from the - * tokens. */ -char * -inet_parse_token(char **pp) +static char * +unbracket(char *s) +{ + if (*s == '[') { + s++; + + char *end = strchr(s, '\0'); + if (end[-1] == ']') { + end[-1] = '\0'; + } + } + return s; +} + +/* 'host_index' is 0 if the host precedes the port within 's', 1 otherwise. */ +static void +inet_parse_tokens__(char *s, int host_index, char **hostp, char **portp) { - char *p = *pp; - - if (p == NULL) { - return NULL; - } else if (*p == '\0') { - *pp = NULL; - return p; - } else if (*p == '[') { - char *start = p + 1; - char *end = start + strcspn(start, "]"); - *pp = (*end == '\0' ? NULL - : end[1] == ':' ? end + 2 - : end + 1); - *end = '\0'; - return start; + char *colon = NULL; + bool in_brackets = false; + int n_colons = 0; + for (char *p = s; *p; p++) { + if (*p == '[') { + in_brackets = true; + } else if (*p == ']') { + in_brackets = false; + } else if (*p == ':' && !in_brackets) { + n_colons++; + colon = p; + } + } + + *hostp = *portp = NULL; + if (n_colons > 1) { + *hostp = s; } else { - char *start = p; - char *end = start + strcspn(start, ":"); - *pp = *end == '\0' ? NULL : end + 1; - *end = '\0'; - return start; + char **tokens[2]; + tokens[host_index] = hostp; + tokens[!host_index] = portp; + + if (colon) { + *colon = '\0'; + *tokens[1] = unbracket(colon + 1); + } + *tokens[0] = unbracket(s); } } +/* Parses 's', a string in the form "<host>[:<port>]", into its (required) host + * and (optional) port components, and stores pointers to them in '*hostp' and + * '*portp' respectively. Always sets '*hostp' nonnull, although possibly to + * an empty string empty string. Can set '*portp' to the null string. + * + * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square + * brackets. Resolves ambiguous cases that might represent an IPv6 address or + * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80" + * is a host but "[::1:2:3:4]:80" is a host and a port. + * + * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it. + */ +void +inet_parse_host_port_tokens(char *s, char **hostp, char **portp) +{ + inet_parse_tokens__(s, 0, hostp, portp); +} + +/* Parses 's', a string in the form "<port>[:<host>]", into its port and host + * components, and stores pointers to them in '*portp' and '*hostp' + * respectively. Both '*portp' and '*hostp' can end up null. + * + * Supports both IPv4 and IPv6. IPv6 addresses may be quoted with square + * brackets. Resolves ambiguous cases that might represent an IPv6 address or + * an IPv6 address and a port as representing just a host, e.g. "::1:2:3:4:80" + * is a host but "[::1:2:3:4]:80" is a host and a port. + * + * Modifies 's' and points '*hostp' and '*portp' (if nonnull) into it. + */ +void +inet_parse_port_host_tokens(char *s, char **portp, char **hostp) +{ + inet_parse_tokens__(s, 1, hostp, portp); +} + static bool parse_sockaddr_components(struct sockaddr_storage *ss, char *host_s, @@ -441,23 +491,16 @@ inet_parse_active(const char *target_, int default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - const char *port; - char *host; - char *p; + char *port, *host; bool ok; - p = target; - host = inet_parse_token(&p); - port = inet_parse_token(&p); + inet_parse_host_port_tokens(target, &host, &port); if (!host) { VLOG_ERR("%s: host must be specified", target_); ok = false; } else if (!port && default_port < 0) { VLOG_ERR("%s: port must be specified", target_); ok = false; - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow host and port", target_); - ok = false; } else { ok = parse_sockaddr_components(ss, host, port, default_port, target_); } @@ -571,20 +614,13 @@ inet_parse_passive(const char *target_, int default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - const char *port; - char *host; - char *p; + char *port, *host; bool ok; - p = target; - port = inet_parse_token(&p); - host = inet_parse_token(&p); + inet_parse_port_host_tokens(target, &port, &host); if (!port && default_port < 0) { VLOG_ERR("%s: port must be specified", target_); ok = false; - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow port and host", target_); - ok = false; } else { ok = parse_sockaddr_components(ss, host, port, default_port, target_); } @@ -707,16 +743,8 @@ bool inet_parse_address(const char *target_, struct sockaddr_storage *ss) { char *target = xstrdup(target_); - char *p = target; - char *host = inet_parse_token(&p); - bool ok = false; - if (!host) { - VLOG_ERR("%s: host must be specified", target_); - } else if (p && p[strspn(p, " \t\r\n")] != '\0') { - VLOG_ERR("%s: unexpected characters follow host", target_); - } else { - ok = parse_sockaddr_components(ss, host, NULL, 0, target_); - } + char *host = unbracket(target); + bool ok = parse_sockaddr_components(ss, host, NULL, 0, target_); if (!ok) { memset(ss, 0, sizeof *ss); } diff --git a/lib/socket-util.h b/lib/socket-util.h index d927d67a0..239d3f220 100644 --- a/lib/socket-util.h +++ b/lib/socket-util.h @@ -46,7 +46,8 @@ int check_connection_completion(int fd); void drain_fd(int fd, size_t n_packets); ovs_be32 guess_netmask(ovs_be32 ip); -char *inet_parse_token(char **); +void inet_parse_host_port_tokens(char *s, char **hostp, char **portp); +void inet_parse_port_host_tokens(char *s, char **portp, char **hostp); bool inet_parse_active(const char *target, int default_port, struct sockaddr_storage *ssp); int inet_open_active(int style, const char *target, int default_port, |