From cfbd1680080cc917f0d15d2a840dd05d9f462217 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 2 Jan 2009 20:46:26 +0000 Subject: [PATCH] Add another function to parse the common address:port combination formats into a sockaddr. svn:r984 --- evutil.c | 92 +++++++++++++++++++++++++++++++++++++++++++ include/event2/util.h | 3 ++ test/regress_util.c | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) diff --git a/evutil.c b/evutil.c index bf161be2..55321923 100644 --- a/evutil.c +++ b/evutil.c @@ -567,3 +567,95 @@ evutil_inet_pton(int af, const char *src, void *dst) } #endif } + +int +evutil_parse_sockaddr_port(const char *ip_as_string, struct sockaddr *out, int outlen) +{ + int port; + char buf[128]; + const char *cp, *addr_part, *port_part; + int is_ipv6; + /* recognized formats are: + * [ipv6]:port + * ipv6 + * [ipv6] + * ipv4:port + * ipv4 + */ + + cp = strchr(ip_as_string, ':'); + if (*ip_as_string == '[') { + int len; + if (!(cp = strchr(ip_as_string, ']'))) { + return -1; + } + len = cp-(ip_as_string + 1); + if (len > (int)sizeof(buf)-1) { + return -1; + } + memcpy(buf, ip_as_string+1, len); + buf[len] = '\0'; + addr_part = buf; + if (cp[1] == ':') + port_part = cp+2; + else + port_part = NULL; + is_ipv6 = 1; + } else if (cp && strchr(cp+1, ':')) { + is_ipv6 = 1; + addr_part = ip_as_string; + port_part = NULL; + } else if (cp) { + is_ipv6 = 0; + if (cp - ip_as_string > (int)sizeof(buf)-1) { + return -1; + } + memcpy(buf, ip_as_string, cp-ip_as_string); + buf[cp-ip_as_string] = '\0'; + addr_part = buf; + port_part = cp+1; + } else { + addr_part = ip_as_string; + port_part = NULL; + is_ipv6 = 0; + } + + if (port_part == NULL) { + port = 0; + } else { + port = atoi(port_part); + if (port <= 0 || port > 65535) { + return -1; + } + } + + if (!addr_part) + return -1; /* Should be impossible. */ + if (is_ipv6) { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(port); + if (1 != evutil_inet_pton(AF_INET6, addr_part, &sin6.sin6_addr)) + return -1; + if (sizeof(sin6) > outlen) + return -1; + memset(out, 0, outlen); + memcpy(out, &sin6, sizeof(sin6)); + return 0; + } else { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + if (1 != evutil_inet_pton(AF_INET, addr_part, &sin.sin_addr)) + return -1; + if (sizeof(sin) > outlen) + return -1; + memset(out, 0, outlen); + memcpy(out, &sin, sizeof(sin)); + return 0; + } +} diff --git a/include/event2/util.h b/include/event2/util.h index ddbe689f..7735a157 100644 --- a/include/event2/util.h +++ b/include/event2/util.h @@ -215,6 +215,9 @@ int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap); const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); int evutil_inet_pton(int af, const char *src, void *dst); +struct sockaddr; +int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int outlen); + #ifdef __cplusplus } diff --git a/test/regress_util.c b/test/regress_util.c index 5d9b4597..990685b0 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -217,10 +217,87 @@ regress_ipv6_parse(void) #endif } +static struct sa_port_ent { + const char *parse; + int sa_family; + const char *addr; + int port; +} sa_port_ents[] = { + { "[ffff::1]:1000", AF_INET6, "ffff::1", 1000 }, + { "[ffff::1]", AF_INET6, "ffff::1", 0 }, + { "[ffff::1", 0, NULL, 0 }, + { "::1", AF_INET6, "::1", 0 }, + { "1:2::1", AF_INET6, "1:2::1", 0 }, + { "192.168.0.1:50", AF_INET, "192.168.0.1", 50 }, + { "1.2.3.4", AF_INET, "1.2.3.4", 0 }, + { NULL, 0, NULL, 0 }, +}; + +static void +regress_sockaddr_port_parse(void) +{ + struct sockaddr_storage ss; + int ok = 1; + int i, r; + + for (i = 0; sa_port_ents[i].parse; ++i) { + struct sa_port_ent *ent = &sa_port_ents[i]; + memset(&ss, 0, sizeof(ss)); + r = evutil_parse_sockaddr_port(ent->parse, (struct sockaddr*)&ss, sizeof(ss)); + if (r < 0) { + if (ent->sa_family) { + printf("Couldn't parse %s!\n", ent->parse); + ok = 0; + } + continue; + } else if (! ent->sa_family) { + printf("Shouldn't have been able to parse %s!\n", + ent->parse); + ok = 0; + continue; + } + if (ent->sa_family == AF_INET) { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(ent->port); + r = evutil_inet_pton(AF_INET, ent->addr, &sin.sin_addr); + if (1 != r) { + printf("Couldn't parse ipv4 target %s.\n", ent->addr); + ok = 0; + } else if (memcmp(&sin, &ss, sizeof(sin))) { + printf("Parse for %s was not as expected.\n", ent->parse); + ok = 0; + } + } else { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(ent->port); + r = evutil_inet_pton(AF_INET6, ent->addr, &sin6.sin6_addr); + if (1 != r) { + printf("Couldn't parse ipv6 target %s.\n", ent->addr); + ok = 0; + } else if (memcmp(&sin6, &ss, sizeof(sin6))) { + printf("Parse for %s was not as expected.\n", ent->parse); + ok = 0; + } + } + } + + if (!ok) { + printf("FAILED\n"); + exit(1); + } + printf("OK\n"); +} void util_suite(void) { regress_ipv4_parse(); regress_ipv6_parse(); + regress_sockaddr_port_parse(); }