Index: ext/socket/extconf.rb =================================================================== --- ext/socket/extconf.rb (revision 39716) +++ ext/socket/extconf.rb (working copy) @@ -118,6 +118,7 @@ EOF have_func("getpeerucred", headers) have_func("if_indextoname", headers) + have_func("if_nametoindex", headers) have_func("hsterror", headers) have_func("getipnodebyname", headers) @@ -463,6 +464,7 @@ EOS "udpsocket.#{$OBJEXT}", "unixsocket.#{$OBJEXT}", "unixserver.#{$OBJEXT}", + "interface.#{$OBJEXT}", "option.#{$OBJEXT}", "ancdata.#{$OBJEXT}", "raddrinfo.#{$OBJEXT}" Index: ext/socket/init.c =================================================================== --- ext/socket/init.c (revision 39716) +++ ext/socket/init.c (working copy) @@ -620,6 +620,7 @@ rsock_init_socket_init() rsock_init_udpsocket(); rsock_init_unixsocket(); rsock_init_unixserver(); + rsock_init_sockif(); rsock_init_sockopt(); rsock_init_ancdata(); rsock_init_addrinfo(); Index: ext/socket/interface.c =================================================================== --- ext/socket/interface.c (revision 0) +++ ext/socket/interface.c (working copy) @@ -0,0 +1,154 @@ +#include "rubysocket.h" + +VALUE rb_cSockIf; + +static ID id_name, id_index; + +/* call-seq: + * Socket::Interface.list => array + * + * Returns an array of all interfaces. + */ +static VALUE +sockif_s_list(VALUE self) +{ + struct if_nameindex *inp = NULL; + VALUE list, index; + + inp = if_nameindex(); + + if (NULL == inp) + rb_sys_fail("if_nameindex(3)"); + + list = rb_ary_new(); + + for (off_t i = 0; inp[i].if_index; i++) { + index = UINT2NUM(inp[i].if_index); + rb_ary_push(list, rb_class_new_instance(1, &index, self)); + } + + if_freenameindex(inp); + + return list; +} + +/* + * call-seq: + * Socket::Interface.new(name) => sockif + * Socket::Interface.new(index) => sockif + * + * Returns a new Socket::Interface object. + */ + +static VALUE +sockif_initialize(VALUE self, VALUE interface) +{ + unsigned int index; + char name[IFNAMSIZ]; + + if (FIXNUM_P(interface)) { + if (if_indextoname(FIX2UINT(interface), name) == NULL) + rb_sys_fail("if_indextoname(3)"); + + rb_ivar_set(self, id_index, interface); + rb_ivar_set(self, id_name, rb_str_new_cstr(name)); + } else { + interface = rb_String(interface); + + index = if_nametoindex(StringValueCStr(interface)); + + if (index == 0) + rb_sys_fail("if_nametoindex(3)"); + + rb_ivar_set(self, id_index, UINT2NUM(index)); + rb_ivar_set(self, id_name, rb_String(interface)); + } + + return self; +} + +/* + * call-seq: + * sockif == other => true or false + * + * Compares this interface with +other+. + */ +static VALUE +sockif_eq(VALUE self, VALUE other) +{ + VALUE index1, index2; + + if (!rb_obj_is_kind_of(other, rb_cSockIf)) + return Qfalse; + + index1 = rb_attr_get(self, id_index); + index2 = rb_attr_get(other, id_index); + + return index1 == index2 ? Qtrue : Qfalse; +} + +/* + * call-seq: + * sockif.index => Integer + * + * Returns the interface index as an Integer + * + * p Socket::Interface.new("lo0").index + * #=> 0 + */ +static VALUE +sockif_index(VALUE self) +{ + return rb_attr_get(self, id_index); +} + +static VALUE +sockif_inspect(VALUE self) +{ + VALUE _name = rb_attr_get(self, id_name); + unsigned int index = NUM2UINT(rb_attr_get(self, id_index)); + char * name = StringValueCStr(_name); + + return rb_sprintf("#<%s %s (index %d)>", + rb_obj_classname(self), name, index); +} + +/* + * call-seq: + * sockif.name => String + * + * Returns the interface name as a String. + * + * p Socket::Interface.new(0).name + * #=> "lo0" + */ +static VALUE +sockif_name(VALUE self) +{ + return rb_attr_get(self, id_name); +} + +void +rsock_init_sockif(void) +{ + id_name = rb_intern("name"); + id_index = rb_intern("index"); + + /* + * Document-class: Socket::Interface + * + * Socket::Interface represents an interface packets may be sent or + * received on. + */ + rb_cSockIf = rb_define_class_under(rb_cSocket, "Interface", rb_cObject); + + rb_define_singleton_method(rb_cSockIf, "list", sockif_s_list, 0); + + rb_define_method(rb_cSockIf, "initialize", sockif_initialize, 1); + + rb_define_method(rb_cSockIf, "==", sockif_eq, 1); + rb_define_method(rb_cSockIf, "name", sockif_name, 0); + rb_define_method(rb_cSockIf, "index", sockif_index, 0); + rb_define_method(rb_cSockIf, "inspect", sockif_inspect, 0); +} + Index: ext/socket/rubysocket.h =================================================================== --- ext/socket/rubysocket.h (revision 39716) +++ ext/socket/rubysocket.h (working copy) @@ -330,6 +330,7 @@ void rsock_init_unixserver(void); void rsock_init_socket_constants(void); void rsock_init_ancdata(void); void rsock_init_addrinfo(void); +void rsock_init_sockif(void); void rsock_init_sockopt(void); void rsock_init_socket_init(void); Index: test/socket/test_interface.rb =================================================================== --- test/socket/test_interface.rb (revision 0) +++ test/socket/test_interface.rb (working copy) @@ -0,0 +1,69 @@ +require 'test/unit' +require 'socket' + +class TestSocketInterface < Test::Unit::TestCase + + def test_self_list + list = Socket::Interface.list + + refute_empty list + + assert list.all? { |iface| Socket::Interface === iface } + end + + def test_initialize_index + assert_raises Errno::ENXIO do + Socket::Interface.new(0) + end + + iface = Socket::Interface.new(1) + + assert_instance_of Socket::Interface, iface + assert_equal 1, iface.index + end + + def test_initialize_name + assert_raises Errno::ENXIO do + Socket::Interface.new('garbage name') + end + + name = Socket::Interface.new(1).name + iface = Socket::Interface.new(name) + + assert_instance_of Socket::Interface, iface + assert_equal name, iface.name + end + + def test_equals2 + iface1a = Socket::Interface.new(1) + iface1b = Socket::Interface.new(1) + iface2 = Socket::Interface.new(2) + + assert_equal iface1a, iface1b + refute_equal iface1a, iface2 + end + + def test_index + iface = Socket::Interface.new(1) + + assert_equal 1, iface.index + end + + def test_inspect + iface = Socket::Interface.new(1) + + assert_equal "#", + iface.inspect + end + + def test_name + iface = Socket::Interface.new(1) + + name = iface.name + + assert_kind_of String, name + refute_empty name + end + +end +