Add another function to parse the common address:port combination formats into a sockaddr.

svn:r984
This commit is contained in:
Nick Mathewson 2009-01-02 20:46:26 +00:00
parent 0d9d5cfe22
commit cfbd168008
3 changed files with 172 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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();
}