about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorHleb Valoshka <375gnu@gmail.com>2013-09-12 16:31:13 +0300
committerEric Wong <normalperson@yhbt.net>2013-09-13 16:53:22 +0000
commitf952501c14316f8639949e3537aead6351224bcd (patch)
treeca6af33896111faf042fb3e395f4413e632a488c
parent05cde4657605caa2db2cb9bd3da611e4bc9d79f6 (diff)
downloadraindrops-f952501c14316f8639949e3537aead6351224bcd.tar.gz
	Scoped ipv6 addresses are defined in rfc4007.
	Ruby doesn't support them yet and it's unknown whether it will
	(see http://bugs.ruby-lang.org/issues/8464).
	So we just remove scope ids.

	Tested with MRI and Rubinius.
-rw-r--r--ext/raindrops/linux_inet_diag.c41
1 files changed, 39 insertions, 2 deletions
diff --git a/ext/raindrops/linux_inet_diag.c b/ext/raindrops/linux_inet_diag.c
index cd4a876..7b5bae1 100644
--- a/ext/raindrops/linux_inet_diag.c
+++ b/ext/raindrops/linux_inet_diag.c
@@ -134,12 +134,49 @@ static int st_free_data(st_data_t key, st_data_t value, st_data_t ignored)
         return ST_DELETE;
 }
 
+/*
+ * call-seq:
+ *      remove_scope_id(ip_address)
+ *
+ * Returns copy of IP address with Scope ID removed,
+ * if address has it (only IPv6 actually may have it).
+ */
+static VALUE remove_scope_id(const char *addr)
+{
+        VALUE rv = rb_str_new2(addr);
+        long len = RSTRING_LEN(rv);
+        char *ptr = RSTRING_PTR(rv);
+        char *pct = memchr(ptr, '%', len);
+
+        /*
+         * remove scoped portion
+         * Ruby equivalent: rv.sub!(/%([^\]]*)\]/, "]")
+         */
+        if (pct) {
+                size_t newlen = pct - ptr;
+                char *rbracket = memchr(pct, ']', len - newlen);
+
+                if (rbracket) {
+                        size_t move = len - (rbracket - ptr);
+
+                        memmove(pct, rbracket, move);
+                        newlen += move;
+
+                        rb_str_set_len(rv, newlen);
+                } else {
+                        rb_raise(rb_eArgError,
+                                "']' not found in IPv6 addr=%s", ptr);
+                }
+        }
+        return rv;
+}
+
 static int st_to_hash(st_data_t key, st_data_t value, VALUE hash)
 {
         struct listen_stats *stats = (struct listen_stats *)value;
 
         if (stats->listener_p) {
-                VALUE k = rb_str_new2((const char *)key);
+                VALUE k = remove_scope_id((const char *)key);
                 VALUE v = rb_listen_stats(stats);
 
                 OBJ_FREEZE(k);
@@ -153,7 +190,7 @@ static int st_AND_hash(st_data_t key, st_data_t value, VALUE hash)
         struct listen_stats *stats = (struct listen_stats *)value;
 
         if (stats->listener_p) {
-                VALUE k = rb_str_new2((const char *)key);
+                VALUE k = remove_scope_id((const char *)key);
 
                 if (rb_hash_lookup(hash, k) == Qtrue) {
                         VALUE v = rb_listen_stats(stats);