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