Ruby  2.0.0p247(2013-06-27revision41674)
ext/socket/option.c
Go to the documentation of this file.
00001 #include "rubysocket.h"
00002 
00003 VALUE rb_cSockOpt;
00004 
00005 static VALUE
00006 constant_to_sym(int constant, ID (*intern_const)(int))
00007 {
00008     ID name = intern_const(constant);
00009     if (name) {
00010         return ID2SYM(name);
00011     }
00012 
00013     return INT2NUM(constant);
00014 }
00015 
00016 static VALUE
00017 optname_to_sym(int level, int optname)
00018 {
00019     switch (level) {
00020       case SOL_SOCKET:
00021         return constant_to_sym(optname, rsock_intern_so_optname);
00022       case IPPROTO_IP:
00023         return constant_to_sym(optname, rsock_intern_ip_optname);
00024 #ifdef INET6
00025       case IPPROTO_IPV6:
00026         return constant_to_sym(optname, rsock_intern_ipv6_optname);
00027 #endif
00028       case IPPROTO_TCP:
00029         return constant_to_sym(optname, rsock_intern_tcp_optname);
00030       case IPPROTO_UDP:
00031         return constant_to_sym(optname, rsock_intern_udp_optname);
00032       default:
00033         return INT2NUM(optname);
00034     }
00035 }
00036 
00037 /*
00038  * call-seq:
00039  *   Socket::Option.new(family, level, optname, data) => sockopt
00040  *
00041  * Returns a new Socket::Option object.
00042  *
00043  *   sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
00044  *   p sockopt #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00045  *
00046  */
00047 static VALUE
00048 sockopt_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE data)
00049 {
00050     int family = rsock_family_arg(vfamily);
00051     int level = rsock_level_arg(family, vlevel);
00052     int optname = rsock_optname_arg(family, level, voptname);
00053     StringValue(data);
00054     rb_ivar_set(self, rb_intern("family"), INT2NUM(family));
00055     rb_ivar_set(self, rb_intern("level"), INT2NUM(level));
00056     rb_ivar_set(self, rb_intern("optname"), INT2NUM(optname));
00057     rb_ivar_set(self, rb_intern("data"), data);
00058     return self;
00059 }
00060 
00061 VALUE
00062 rsock_sockopt_new(int family, int level, int optname, VALUE data)
00063 {
00064     NEWOBJ_OF(obj, struct RObject, rb_cSockOpt, T_OBJECT);
00065     StringValue(data);
00066     sockopt_initialize((VALUE)obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data);
00067     return (VALUE)obj;
00068 }
00069 
00070 /*
00071  * call-seq:
00072  *   sockopt.family => integer
00073  *
00074  * returns the socket family as an integer.
00075  *
00076  *   p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).family
00077  *   #=> 10
00078  */
00079 static VALUE
00080 sockopt_family_m(VALUE self)
00081 {
00082     return rb_attr_get(self, rb_intern("family"));
00083 }
00084 
00085 static int
00086 sockopt_level(VALUE self)
00087 {
00088     return NUM2INT(rb_attr_get(self, rb_intern("level")));
00089 }
00090 
00091 /*
00092  * call-seq:
00093  *   sockopt.level => integer
00094  *
00095  * returns the socket level as an integer.
00096  *
00097  *   p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).level
00098  *   #=> 41
00099  */
00100 static VALUE
00101 sockopt_level_m(VALUE self)
00102 {
00103     return INT2NUM(sockopt_level(self));
00104 }
00105 
00106 static int
00107 sockopt_optname(VALUE self)
00108 {
00109     return NUM2INT(rb_attr_get(self, rb_intern("optname")));
00110 }
00111 
00112 /*
00113  * call-seq:
00114  *   sockopt.optname => integer
00115  *
00116  * returns the socket option name as an integer.
00117  *
00118  *   p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).optname
00119  *   #=> 2
00120  */
00121 static VALUE
00122 sockopt_optname_m(VALUE self)
00123 {
00124     return INT2NUM(sockopt_optname(self));
00125 }
00126 
00127 /*
00128  * call-seq:
00129  *   sockopt.data => string
00130  *
00131  * returns the socket option data as a string.
00132  *
00133  *   p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).data
00134  *   #=> "\x01\x00\x00\x00"
00135  */
00136 static VALUE
00137 sockopt_data(VALUE self)
00138 {
00139     VALUE v = rb_attr_get(self, rb_intern("data"));
00140     StringValue(v);
00141     return v;
00142 }
00143 
00144 /*
00145  * call-seq:
00146  *   Socket::Option.int(family, level, optname, integer) => sockopt
00147  *
00148  * Creates a new Socket::Option object which contains an int as data.
00149  *
00150  * The size and endian is dependent on the platform.
00151  *
00152  *   p Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00153  *   #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00154  */
00155 static VALUE
00156 sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint)
00157 {
00158     int family = rsock_family_arg(vfamily);
00159     int level = rsock_level_arg(family, vlevel);
00160     int optname = rsock_optname_arg(family, level, voptname);
00161     int i = NUM2INT(vint);
00162     return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i)));
00163 }
00164 
00165 /*
00166  * call-seq:
00167  *   sockopt.int => integer
00168  *
00169  * Returns the data in _sockopt_ as an int.
00170  *
00171  * The size and endian is dependent on the platform.
00172  *
00173  *   sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00174  *   p sockopt.int => 1
00175  */
00176 static VALUE
00177 sockopt_int(VALUE self)
00178 {
00179     int i;
00180     VALUE data = sockopt_data(self);
00181     StringValue(data);
00182     if (RSTRING_LEN(data) != sizeof(int))
00183         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(int)=%d but %ld",
00184                  (int)sizeof(int), (long)RSTRING_LEN(data));
00185     memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00186     return INT2NUM(i);
00187 }
00188 
00189 /*
00190  * call-seq:
00191  *   Socket::Option.bool(family, level, optname, bool) => sockopt
00192  *
00193  * Creates a new Socket::Option object which contains boolean as data.
00194  * Actually 0 or 1 as int is used.
00195  *
00196  *   p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
00197  *   #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00198  *
00199  *   p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false)
00200  *   #=> #<Socket::Option: AF_INET SOCKET KEEPALIVE 0>
00201  *
00202  */
00203 static VALUE
00204 sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vbool)
00205 {
00206     int family = rsock_family_arg(vfamily);
00207     int level = rsock_level_arg(family, vlevel);
00208     int optname = rsock_optname_arg(family, level, voptname);
00209     int i = RTEST(vbool) ? 1 : 0;
00210     return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i)));
00211 }
00212 
00213 /*
00214  * call-seq:
00215  *   sockopt.bool => true or false
00216  *
00217  * Returns the data in _sockopt_ as an boolean value.
00218  *
00219  *   sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00220  *   p sockopt.bool => true
00221  */
00222 static VALUE
00223 sockopt_bool(VALUE self)
00224 {
00225     int i;
00226     VALUE data = sockopt_data(self);
00227     StringValue(data);
00228     if (RSTRING_LEN(data) != sizeof(int))
00229         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(int)=%d but %ld",
00230                  (int)sizeof(int), (long)RSTRING_LEN(data));
00231     memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00232     return i == 0 ? Qfalse : Qtrue;
00233 }
00234 
00235 /*
00236  * call-seq:
00237  *   Socket::Option.linger(onoff, secs) => sockopt
00238  *
00239  * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER.
00240  *
00241  * _onoff_ should be an integer or a boolean.
00242  *
00243  * _secs_ should be the number of seconds.
00244  *
00245  *   p Socket::Option.linger(true, 10)
00246  *   #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec>
00247  *
00248  */
00249 static VALUE
00250 sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs)
00251 {
00252     VALUE tmp;
00253     struct linger l;
00254     memset(&l, 0, sizeof(l));
00255     if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int")))
00256         l.l_onoff = NUM2INT(tmp);
00257     else
00258         l.l_onoff = RTEST(vonoff) ? 1 : 0;
00259     l.l_linger = NUM2INT(vsecs);
00260     return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, rb_str_new((char*)&l, sizeof(l)));
00261 }
00262 
00263 /*
00264  * call-seq:
00265  *   sockopt.linger => [bool, seconds]
00266  *
00267  * Returns the linger data in _sockopt_ as a pair of boolean and integer.
00268  *
00269  *   sockopt = Socket::Option.linger(true, 10)
00270  *   p sockopt.linger => [true, 10]
00271  */
00272 static VALUE
00273 sockopt_linger(VALUE self)
00274 {
00275     int level = sockopt_level(self);
00276     int optname = sockopt_optname(self);
00277     VALUE data = sockopt_data(self);
00278     struct linger l;
00279     VALUE vonoff, vsecs;
00280 
00281     if (level != SOL_SOCKET || optname != SO_LINGER)
00282         rb_raise(rb_eTypeError, "linger socket option expected");
00283     if (RSTRING_LEN(data) != sizeof(l))
00284         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(struct linger)=%d but %ld",
00285                  (int)sizeof(struct linger), (long)RSTRING_LEN(data));
00286     memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger));
00287     switch (l.l_onoff) {
00288       case 0: vonoff = Qfalse; break;
00289       case 1: vonoff = Qtrue; break;
00290       default: vonoff = INT2NUM(l.l_onoff); break;
00291     }
00292     vsecs = INT2NUM(l.l_linger);
00293     return rb_assoc_new(vonoff, vsecs);
00294 }
00295 
00296 static int
00297 inspect_int(int level, int optname, VALUE data, VALUE ret)
00298 {
00299     if (RSTRING_LEN(data) == sizeof(int)) {
00300         int i;
00301         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00302         rb_str_catf(ret, " %d", i);
00303         return 1;
00304     }
00305     else {
00306         return 0;
00307     }
00308 }
00309 
00310 static int
00311 inspect_errno(int level, int optname, VALUE data, VALUE ret)
00312 {
00313     if (RSTRING_LEN(data) == sizeof(int)) {
00314         int i;
00315         char *err;
00316         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00317         err = strerror(i);
00318         rb_str_catf(ret, " %s (%d)", err, i);
00319         return 1;
00320     }
00321     else {
00322         return 0;
00323     }
00324 }
00325 
00326 #if defined(IPV6_MULTICAST_LOOP)
00327 static int
00328 inspect_uint(int level, int optname, VALUE data, VALUE ret)
00329 {
00330     if (RSTRING_LEN(data) == sizeof(int)) {
00331         unsigned int i;
00332         memcpy((char*)&i, RSTRING_PTR(data), sizeof(unsigned int));
00333         rb_str_catf(ret, " %u", i);
00334         return 1;
00335     }
00336     else {
00337         return 0;
00338     }
00339 }
00340 #endif
00341 
00342 #if defined(SOL_SOCKET) && defined(SO_LINGER) /* POSIX */
00343 static int
00344 inspect_linger(int level, int optname, VALUE data, VALUE ret)
00345 {
00346     if (RSTRING_LEN(data) == sizeof(struct linger)) {
00347         struct linger s;
00348         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00349         switch (s.l_onoff) {
00350           case 0: rb_str_cat2(ret, " off"); break;
00351           case 1: rb_str_cat2(ret, " on"); break;
00352           default: rb_str_catf(ret, " on(%d)", s.l_onoff); break;
00353         }
00354         rb_str_catf(ret, " %dsec", s.l_linger);
00355         return 1;
00356     }
00357     else {
00358         return 0;
00359     }
00360 }
00361 #endif
00362 
00363 #if defined(SOL_SOCKET) && defined(SO_TYPE) /* POSIX */
00364 static int
00365 inspect_socktype(int level, int optname, VALUE data, VALUE ret)
00366 {
00367     if (RSTRING_LEN(data) == sizeof(int)) {
00368         int i;
00369         ID id;
00370         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00371         id = rsock_intern_socktype(i);
00372         if (id)
00373             rb_str_catf(ret, " %s", rb_id2name(id));
00374         else
00375             rb_str_catf(ret, " %d", i);
00376         return 1;
00377     }
00378     else {
00379         return 0;
00380     }
00381 }
00382 #endif
00383 
00384 static int
00385 inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret)
00386 {
00387     if (RSTRING_LEN(data) == sizeof(struct timeval)) {
00388         struct timeval s;
00389         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00390         rb_str_catf(ret, " %ld.%06ldsec", (long)s.tv_sec, (long)s.tv_usec);
00391         return 1;
00392     }
00393     else {
00394         return 0;
00395     }
00396 }
00397 
00398 /*
00399  * socket option for IPv4 multicast is bit confusing.
00400  *
00401  * IP Multicast is implemented by Steve Deering at first:
00402  *   IP Multicast Extensions for 4.3BSD UNIX and related systems
00403  *   (MULTICAST 1.2 Release)
00404  *   http://www.kohala.com/start/mcast.api.txt
00405  *
00406  * There are 3 socket options which takes a struct.
00407  *
00408  *   IP_MULTICAST_IF: struct in_addr
00409  *   IP_ADD_MEMBERSHIP: struct ip_mreq
00410  *   IP_DROP_MEMBERSHIP: struct ip_mreq
00411  *
00412  * But they uses an IP address to specify an interface.
00413  * This means the API cannot specify an unnumbered interface.
00414  *
00415  * Linux 2.4 introduces struct ip_mreqn to fix this problem.
00416  * struct ip_mreqn has imr_ifindex field to specify interface index.
00417  *
00418  *   IP_MULTICAST_IF: struct ip_mreqn
00419  *   IP_ADD_MEMBERSHIP: struct ip_mreqn
00420  *   IP_DROP_MEMBERSHIP: struct ip_mreqn
00421  *
00422  * FreeBSD 7 obtained struct ip_mreqn for IP_MULTICAST_IF.
00423  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/netinet/in.h.diff?r1=1.99;r2=1.100
00424  *
00425  * Another hackish workaround is "RFC 1724 hack".
00426  * RFC 1724 section 3.3 suggests unnumbered interfaces
00427  * specified by pseudo address 0.0.0.0/8.
00428  * NetBSD 4 and FreeBSD 5 documented it.
00429  * http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.16&r2=1.17
00430  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.37;r2=1.38
00431  * FreeBSD 7.0 removed it.
00432  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.49;r2=1.50
00433  *
00434  * RFC 1724 hack is not supported by Socket::Option#inspect because
00435  * it is not distinguishable by the size.
00436  */
00437 
00438 #ifndef HAVE_INET_NTOP
00439 static const char *
00440 inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
00441 {
00442 #ifdef HAVE_INET_NTOA
00443     struct in_addr in;
00444     memcpy(&in.s_addr, addr, sizeof(in.s_addr));
00445     snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
00446 #else
00447     unsigned long x = ntohl(*(unsigned long*)addr);
00448     snprintf(numaddr, numaddr_len, "%d.%d.%d.%d",
00449              (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
00450              (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
00451 #endif
00452     return numaddr;
00453 }
00454 #elif defined __MINGW64__
00455 # define inet_ntop(f,a,n,l)      rb_w32_inet_ntop(f,a,n,l)
00456 #endif
00457 
00458 /* Although the buffer size needed depends on the prefixes, "%u" may generate "4294967295".  */
00459 static int
00460 rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int ifindex, char *buf, size_t len)
00461 {
00462 #if defined(HAVE_IF_INDEXTONAME)
00463     char ifbuf[IFNAMSIZ];
00464     if (if_indextoname(ifindex, ifbuf) == NULL)
00465         return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
00466     else
00467         return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
00468 #else
00469 #   ifndef IFNAMSIZ
00470 #       define IFNAMSIZ (sizeof(unsigned int)*3+1)
00471 #   endif
00472     return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
00473 #endif
00474 }
00475 
00476 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
00477 static int
00478 inspect_ipv4_mreq(int level, int optname, VALUE data, VALUE ret)
00479 {
00480     if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) {
00481         struct ip_mreq s;
00482         char addrbuf[INET_ADDRSTRLEN];
00483         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00484         if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00485             rb_str_cat2(ret, " invalid-address");
00486         else
00487             rb_str_catf(ret, " %s", addrbuf);
00488         if (inet_ntop(AF_INET, &s.imr_interface, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00489             rb_str_catf(ret, " invalid-address");
00490         else
00491             rb_str_catf(ret, " %s", addrbuf);
00492         return 1;
00493     }
00494     else {
00495         return 0;
00496     }
00497 }
00498 #endif
00499 
00500 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* GNU/Linux, FreeBSD 7 */
00501 static int
00502 inspect_ipv4_mreqn(int level, int optname, VALUE data, VALUE ret)
00503 {
00504     if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
00505         struct ip_mreqn s;
00506         char addrbuf[INET_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
00507         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00508         if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00509             rb_str_cat2(ret, " invalid-address");
00510         else
00511             rb_str_catf(ret, " %s", addrbuf);
00512         if (inet_ntop(AF_INET, &s.imr_address, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00513             rb_str_catf(ret, " invalid-address");
00514         else
00515             rb_str_catf(ret, " %s", addrbuf);
00516         rb_if_indextoname(" ", " ifindex:", s.imr_ifindex, ifbuf, sizeof(ifbuf));
00517         rb_str_cat2(ret, ifbuf);
00518         return 1;
00519     }
00520     else {
00521         return 0;
00522     }
00523 }
00524 #endif
00525 
00526 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
00527 static int
00528 inspect_ipv4_add_drop_membership(int level, int optname, VALUE data, VALUE ret)
00529 {
00530     if (RSTRING_LEN(data) == sizeof(struct ip_mreq))
00531         return inspect_ipv4_mreq(level, optname, data, ret);
00532 # if defined(HAVE_TYPE_STRUCT_IP_MREQN)
00533     else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn))
00534         return inspect_ipv4_mreqn(level, optname, data, ret);
00535 # endif
00536     else
00537         return 0;
00538 }
00539 #endif
00540 
00541 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
00542 static int
00543 inspect_ipv4_multicast_if(int level, int optname, VALUE data, VALUE ret)
00544 {
00545     if (RSTRING_LEN(data) == sizeof(struct in_addr)) {
00546         struct in_addr s;
00547         char addrbuf[INET_ADDRSTRLEN];
00548         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00549         if (inet_ntop(AF_INET, &s, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00550             rb_str_cat2(ret, " invalid-address");
00551         else
00552             rb_str_catf(ret, " %s", addrbuf);
00553         return 1;
00554     }
00555     else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
00556         return inspect_ipv4_mreqn(level, optname, data, ret);
00557     }
00558     else {
00559         return 0;
00560     }
00561 }
00562 #endif
00563 
00564 #if defined(IPV6_MULTICAST_IF) /* POSIX, RFC 3493 */
00565 static int
00566 inspect_ipv6_multicast_if(int level, int optname, VALUE data, VALUE ret)
00567 {
00568     if (RSTRING_LEN(data) == sizeof(int)) {
00569         char ifbuf[32+IFNAMSIZ];
00570         unsigned int ifindex;
00571         memcpy((char*)&ifindex, RSTRING_PTR(data), sizeof(unsigned int));
00572         rb_if_indextoname(" ", " ", ifindex, ifbuf, sizeof(ifbuf));
00573         rb_str_cat2(ret, ifbuf);
00574         return 1;
00575     }
00576     else {
00577         return 0;
00578     }
00579 }
00580 #endif
00581 
00582 #if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */
00583 static int
00584 inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret)
00585 {
00586     if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) {
00587         struct ipv6_mreq s;
00588         char addrbuf[INET6_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
00589         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00590         if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00591             rb_str_cat2(ret, " invalid-address");
00592         else
00593             rb_str_catf(ret, " %s", addrbuf);
00594         rb_if_indextoname(" ", " interface:", s.ipv6mr_interface, ifbuf, sizeof(ifbuf));
00595         rb_str_cat2(ret, ifbuf);
00596         return 1;
00597     }
00598     else {
00599         return 0;
00600     }
00601 }
00602 #endif
00603 
00604 #if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
00605 #if defined(__OpenBSD__)
00606 #define RUBY_SOCK_PEERCRED struct sockpeercred
00607 #else
00608 #define RUBY_SOCK_PEERCRED struct ucred
00609 #endif
00610 static int
00611 inspect_peercred(int level, int optname, VALUE data, VALUE ret)
00612 {
00613     if (RSTRING_LEN(data) == sizeof(RUBY_SOCK_PEERCRED)) {
00614         RUBY_SOCK_PEERCRED cred;
00615         memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED));
00616         rb_str_catf(ret, " pid=%u euid=%u egid=%u",
00617                     (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid);
00618         rb_str_cat2(ret, " (ucred)");
00619         return 1;
00620     }
00621     else {
00622         return 0;
00623     }
00624 }
00625 #endif
00626 
00627 #if defined(LOCAL_PEERCRED) /* FreeBSD, MacOS X */
00628 static int
00629 inspect_local_peercred(int level, int optname, VALUE data, VALUE ret)
00630 {
00631     if (RSTRING_LEN(data) == sizeof(struct xucred)) {
00632         struct xucred cred;
00633         memcpy(&cred, RSTRING_PTR(data), sizeof(struct xucred));
00634         if (cred.cr_version != XUCRED_VERSION)
00635             return 0;
00636         rb_str_catf(ret, " version=%u", cred.cr_version);
00637         rb_str_catf(ret, " euid=%u", cred.cr_uid);
00638         if (cred.cr_ngroups) {
00639             int i;
00640             const char *sep = " groups=";
00641             for (i = 0; i < cred.cr_ngroups; i++) {
00642                 rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]);
00643                 sep = ",";
00644             }
00645         }
00646         rb_str_cat2(ret, " (xucred)");
00647         return 1;
00648     }
00649     else {
00650         return 0;
00651     }
00652 }
00653 #endif
00654 
00655 
00656 /*
00657  * call-seq:
00658  *   sockopt.inspect => string
00659  *
00660  * Returns a string which shows sockopt in human-readable form.
00661  *
00662  *   p Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")).inspect
00663  *   #=> "#<Socket::Option: INET SOCKET KEEPALIVE 1>"
00664  *
00665  */
00666 static VALUE
00667 sockopt_inspect(VALUE self)
00668 {
00669     int family = NUM2INT(sockopt_family_m(self));
00670     int level = NUM2INT(sockopt_level_m(self));
00671     int optname = NUM2INT(sockopt_optname_m(self));
00672     VALUE data = sockopt_data(self);
00673     VALUE v, ret;
00674     ID family_id, level_id, optname_id;
00675     int inspected;
00676 
00677     StringValue(data);
00678 
00679     ret = rb_sprintf("#<%s:", rb_obj_classname(self));
00680 
00681     family_id = rsock_intern_family_noprefix(family);
00682     if (family_id)
00683         rb_str_catf(ret, " %s", rb_id2name(family_id));
00684     else
00685         rb_str_catf(ret, " family:%d", family);
00686 
00687     if (level == SOL_SOCKET) {
00688         rb_str_cat2(ret, " SOCKET");
00689 
00690         optname_id = rsock_intern_so_optname(optname);
00691         if (optname_id)
00692             rb_str_catf(ret, " %s", rb_id2name(optname_id));
00693         else
00694             rb_str_catf(ret, " optname:%d", optname);
00695     }
00696 #ifdef HAVE_SYS_UN_H
00697     else if (family == AF_UNIX) {
00698         rb_str_catf(ret, " level:%d", level);
00699 
00700         optname_id = rsock_intern_local_optname(optname);
00701         if (optname_id)
00702             rb_str_catf(ret, " %s", rb_id2name(optname_id));
00703         else
00704             rb_str_catf(ret, " optname:%d", optname);
00705     }
00706 #endif
00707     else if (IS_IP_FAMILY(family)) {
00708         level_id = rsock_intern_iplevel(level);
00709         if (level_id)
00710             rb_str_catf(ret, " %s", rb_id2name(level_id));
00711         else
00712             rb_str_catf(ret, " level:%d", level);
00713 
00714         v = optname_to_sym(level, optname);
00715         if (SYMBOL_P(v))
00716             rb_str_catf(ret, " %s", rb_id2name(SYM2ID(v)));
00717         else
00718             rb_str_catf(ret, " optname:%d", optname);
00719     }
00720     else {
00721         rb_str_catf(ret, " level:%d", level);
00722         rb_str_catf(ret, " optname:%d", optname);
00723     }
00724 
00725     inspected = 0;
00726 
00727     if (level == SOL_SOCKET)
00728         family = AF_UNSPEC;
00729     switch (family) {
00730       case AF_UNSPEC:
00731         switch (level) {
00732           case SOL_SOCKET:
00733             switch (optname) {
00734 #            if defined(SO_DEBUG) /* POSIX */
00735               case SO_DEBUG: inspected = inspect_int(level, optname, data, ret); break;
00736 #            endif
00737 #            if defined(SO_ERROR) /* POSIX */
00738               case SO_ERROR: inspected = inspect_errno(level, optname, data, ret); break;
00739 #            endif
00740 #            if defined(SO_TYPE) /* POSIX */
00741               case SO_TYPE: inspected = inspect_socktype(level, optname, data, ret); break;
00742 #            endif
00743 #            if defined(SO_ACCEPTCONN) /* POSIX */
00744               case SO_ACCEPTCONN: inspected = inspect_int(level, optname, data, ret); break;
00745 #            endif
00746 #            if defined(SO_BROADCAST) /* POSIX */
00747               case SO_BROADCAST: inspected = inspect_int(level, optname, data, ret); break;
00748 #            endif
00749 #            if defined(SO_REUSEADDR) /* POSIX */
00750               case SO_REUSEADDR: inspected = inspect_int(level, optname, data, ret); break;
00751 #            endif
00752 #            if defined(SO_KEEPALIVE) /* POSIX */
00753               case SO_KEEPALIVE: inspected = inspect_int(level, optname, data, ret); break;
00754 #            endif
00755 #            if defined(SO_OOBINLINE) /* POSIX */
00756               case SO_OOBINLINE: inspected = inspect_int(level, optname, data, ret); break;
00757 #            endif
00758 #            if defined(SO_SNDBUF) /* POSIX */
00759               case SO_SNDBUF: inspected = inspect_int(level, optname, data, ret); break;
00760 #            endif
00761 #            if defined(SO_RCVBUF) /* POSIX */
00762               case SO_RCVBUF: inspected = inspect_int(level, optname, data, ret); break;
00763 #            endif
00764 #            if defined(SO_DONTROUTE) /* POSIX */
00765               case SO_DONTROUTE: inspected = inspect_int(level, optname, data, ret); break;
00766 #            endif
00767 #            if defined(SO_RCVLOWAT) /* POSIX */
00768               case SO_RCVLOWAT: inspected = inspect_int(level, optname, data, ret); break;
00769 #            endif
00770 #            if defined(SO_SNDLOWAT) /* POSIX */
00771               case SO_SNDLOWAT: inspected = inspect_int(level, optname, data, ret); break;
00772 #            endif
00773 #            if defined(SO_LINGER) /* POSIX */
00774               case SO_LINGER: inspected = inspect_linger(level, optname, data, ret); break;
00775 #            endif
00776 #            if defined(SO_RCVTIMEO) /* POSIX */
00777               case SO_RCVTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
00778 #            endif
00779 #            if defined(SO_SNDTIMEO) /* POSIX */
00780               case SO_SNDTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
00781 #            endif
00782 #            if defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
00783               case SO_PEERCRED: inspected = inspect_peercred(level, optname, data, ret); break;
00784 #            endif
00785             }
00786             break;
00787         }
00788         break;
00789 
00790       case AF_INET:
00791 #ifdef INET6
00792       case AF_INET6:
00793 #endif
00794         switch (level) {
00795 #        if defined(IPPROTO_IP)
00796           case IPPROTO_IP:
00797             switch (optname) {
00798 #            if defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
00799               case IP_MULTICAST_IF: inspected = inspect_ipv4_multicast_if(level, optname, data, ret); break;
00800 #            endif
00801 #            if defined(IP_ADD_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
00802               case IP_ADD_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
00803 #            endif
00804 #            if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
00805               case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
00806 #            endif
00807             }
00808             break;
00809 #        endif
00810 
00811 #        if defined(IPPROTO_IPV6)
00812           case IPPROTO_IPV6:
00813             switch (optname) {
00814 #            if defined(IPV6_MULTICAST_HOPS) /* POSIX */
00815               case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
00816 #            endif
00817 #            if defined(IPV6_MULTICAST_IF) /* POSIX */
00818               case IPV6_MULTICAST_IF: inspected = inspect_ipv6_multicast_if(level, optname, data, ret); break;
00819 #            endif
00820 #            if defined(IPV6_MULTICAST_LOOP) /* POSIX */
00821               case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break;
00822 #            endif
00823 #            if defined(IPV6_JOIN_GROUP) /* POSIX */
00824               case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
00825 #            endif
00826 #            if defined(IPV6_LEAVE_GROUP) /* POSIX */
00827               case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
00828 #            endif
00829 #            if defined(IPV6_UNICAST_HOPS) /* POSIX */
00830               case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
00831 #            endif
00832 #            if defined(IPV6_V6ONLY) /* POSIX */
00833               case IPV6_V6ONLY: inspected = inspect_int(level, optname, data, ret); break;
00834 #            endif
00835             }
00836             break;
00837 #        endif
00838 
00839 #        if defined(IPPROTO_TCP)
00840           case IPPROTO_TCP:
00841             switch (optname) {
00842 #            if defined(TCP_NODELAY) /* POSIX */
00843               case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break;
00844 #            endif
00845             }
00846             break;
00847 #        endif
00848         }
00849         break;
00850 
00851 #ifdef HAVE_SYS_UN_H
00852       case AF_UNIX:
00853         switch (level) {
00854           case 0:
00855             switch (optname) {
00856 #            if defined(LOCAL_PEERCRED)
00857               case LOCAL_PEERCRED: inspected = inspect_local_peercred(level, optname, data, ret); break;
00858 #            endif
00859             }
00860             break;
00861         }
00862         break;
00863 #endif
00864     }
00865 
00866     if (!inspected) {
00867         rb_str_cat2(ret, " ");
00868         rb_str_append(ret, rb_str_dump(data));
00869     }
00870 
00871     rb_str_cat2(ret, ">");
00872 
00873     return ret;
00874 }
00875 
00876 /*
00877  * call-seq:
00878  *   sockopt.unpack(template) => array
00879  *
00880  * Calls String#unpack on sockopt.data.
00881  *
00882  *   sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
00883  *   p sockopt.unpack("i")      #=> [1]
00884  *   p sockopt.data.unpack("i") #=> [1]
00885  */
00886 static VALUE
00887 sockopt_unpack(VALUE self, VALUE template)
00888 {
00889     return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template);
00890 }
00891 
00892 void
00893 rsock_init_sockopt(void)
00894 {
00895     /*
00896      * Document-class: Socket::Option
00897      *
00898      * Socket::Option represents a socket option used by
00899      * BasicSocket#getsockopt and BasicSocket#setsockopt.  A socket option
00900      * contains the socket #family, protocol #level, option name #optname and
00901      * option value #data.
00902      */
00903     rb_cSockOpt = rb_define_class_under(rb_cSocket, "Option", rb_cObject);
00904     rb_define_method(rb_cSockOpt, "initialize", sockopt_initialize, 4);
00905     rb_define_method(rb_cSockOpt, "family", sockopt_family_m, 0);
00906     rb_define_method(rb_cSockOpt, "level", sockopt_level_m, 0);
00907     rb_define_method(rb_cSockOpt, "optname", sockopt_optname_m, 0);
00908     rb_define_method(rb_cSockOpt, "data", sockopt_data, 0);
00909     rb_define_method(rb_cSockOpt, "inspect", sockopt_inspect, 0);
00910 
00911     rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4);
00912     rb_define_method(rb_cSockOpt, "int", sockopt_int, 0);
00913 
00914     rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4);
00915     rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0);
00916 
00917     rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2);
00918     rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0);
00919 
00920     rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1);
00921 
00922     rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */
00923 }
00924 
00925