about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-03-12 12:56:16 -0800
committerEric Wong <normalperson@yhbt.net>2011-03-12 20:56:51 +0000
commit65f46737ad5641841a5e5a89f4651d160dacffc0 (patch)
treeefba2424e0e1ac6a5afd07292a0b90f7cf62ea1c
parentc478549ea7490de2432ed31ffee37a2bfc1d24f3 (diff)
downloadraindrops-65f46737ad5641841a5e5a89f4651d160dacffc0.tar.gz
getaddrinfo() needs to get a list of available interfaces
from the kernel with every single call (since ipv6 could've
been modprobed), so it's a waste of syscalls.
-rw-r--r--ext/raindrops/linux_inet_diag.c37
1 files changed, 21 insertions, 16 deletions
diff --git a/ext/raindrops/linux_inet_diag.c b/ext/raindrops/linux_inet_diag.c
index 13ba4ff..d592b79 100644
--- a/ext/raindrops/linux_inet_diag.c
+++ b/ext/raindrops/linux_inet_diag.c
@@ -373,17 +373,20 @@ out:
 static void parse_addr(struct sockaddr_storage *inet, VALUE addr)
 {
         char *host_ptr;
+        char *check;
         char *colon = NULL;
         char *rbracket = NULL;
+        void *dst;
         long host_len;
-        struct addrinfo hints;
-        struct addrinfo *res;
-        int rc;
+        int af, rc;
+        uint16_t *portdst;
+        unsigned long port;
 
         Check_Type(addr, T_STRING);
         host_ptr = StringValueCStr(addr);
         host_len = RSTRING_LEN(addr);
         if (*host_ptr == '[') { /* ipv6 address format (rfc2732) */
+                struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)inet;
                 rbracket = memchr(host_ptr + 1, ']', host_len - 1);
 
                 if (rbracket == NULL)
@@ -395,28 +398,30 @@ static void parse_addr(struct sockaddr_storage *inet, VALUE addr)
                 colon = rbracket + 1;
                 host_ptr++;
                 *rbracket = 0;
+                inet->ss_family = af = AF_INET6;
+                dst = &in6->sin6_addr;
+                portdst = &in6->sin6_port;
         } else { /* ipv4 */
+                struct sockaddr_in *in = (struct sockaddr_in *)inet;
                 colon = memchr(host_ptr, ':', host_len);
+                inet->ss_family = af = AF_INET;
+                dst = &in->sin_addr;
+                portdst = &in->sin_port;
         }
 
         if (!colon)
                 rb_raise(rb_eArgError, "port not found in: `%s'", host_ptr);
-
-        hints.ai_family = AF_UNSPEC;
-        hints.ai_socktype = SOCK_STREAM;
-        hints.ai_protocol = IPPROTO_TCP;
-        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
-
+        port = strtoul(colon + 1, &check, 10);
         *colon = 0;
-        rc = getaddrinfo(host_ptr, colon + 1, &hints, &res);
+        rc = inet_pton(af, host_ptr, dst);
         *colon = ':';
         if (rbracket) *rbracket = ']';
-        if (rc != 0)
-                rb_raise(rb_eArgError, "getaddrinfo(%s): %s",
-                         host_ptr, gai_strerror(rc));
-
-        memcpy(inet, res->ai_addr, res->ai_addrlen);
-        freeaddrinfo(res);
+        if (*check || ((uint16_t)port != port))
+                rb_raise(rb_eArgError, "invalid port: %s", colon + 1);
+        if (rc != 1)
+                rb_raise(rb_eArgError, "inet_pton failed for: `%s' with %d",
+                         host_ptr, rc);
+        *portdst = ntohs((uint16_t)port);
 }
 
 /* generates inet_diag bytecode to match all addrs */