summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2018-04-11 11:12:21 -0700
committerBen Pfaff <blp@ovn.org>2018-04-16 14:52:56 -0700
commit0b043300dbade23ce9435b7dd308473fea41e6fe (patch)
tree894f3f439c83e17b8d3806644eb7b19f8b13db89 /lib
parent1bb011218d046b5de765658e9f5fe45289462eea (diff)
downloadopenvswitch-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.c76
-rw-r--r--lib/packets.h10
-rw-r--r--lib/socket-util.c140
-rw-r--r--lib/socket-util.h3
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,