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 IPPROTO_IPV6
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  *   sockopt.to_s => string
00131  *
00132  * returns the socket option data as a string.
00133  *
00134  *   p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).data
00135  *   #=> "\x01\x00\x00\x00"
00136  */
00137 static VALUE
00138 sockopt_data(VALUE self)
00139 {
00140     VALUE v = rb_attr_get(self, rb_intern("data"));
00141     StringValue(v);
00142     return v;
00143 }
00144 
00145 /*
00146  * call-seq:
00147  *   Socket::Option.byte(family, level, optname, integer) => sockopt
00148  *
00149  * Creates a new Socket::Option object which contains a byte as data.
00150  *
00151  * The size and endian is dependent on the platform.
00152  *
00153  *   p Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1)
00154  *   #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00155  */
00156 static VALUE
00157 sockopt_s_byte(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint)
00158 {
00159     int family = rsock_family_arg(vfamily);
00160     int level = rsock_level_arg(family, vlevel);
00161     int optname = rsock_optname_arg(family, level, voptname);
00162     unsigned char i = (unsigned char)NUM2CHR(vint);
00163     return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i)));
00164 }
00165 
00166 /*
00167  * call-seq:
00168  *   sockopt.byte => integer
00169  *
00170  * Returns the data in _sockopt_ as an byte.
00171  *
00172  * The size and endian is dependent on the platform.
00173  *
00174  *   sockopt = Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1)
00175  *   p sockopt.byte => 1
00176  */
00177 static VALUE
00178 sockopt_byte(VALUE self)
00179 {
00180     unsigned char i;
00181     VALUE data = sockopt_data(self);
00182     StringValue(data);
00183     if (RSTRING_LEN(data) != sizeof(i))
00184         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(int)=%d but %ld",
00185                  (int)sizeof(i), (long)RSTRING_LEN(data));
00186     return CHR2FIX(*RSTRING_PTR(data));
00187 }
00188 
00189 /*
00190  * call-seq:
00191  *   Socket::Option.int(family, level, optname, integer) => sockopt
00192  *
00193  * Creates a new Socket::Option object which contains an int as data.
00194  *
00195  * The size and endian is dependent on the platform.
00196  *
00197  *   p Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00198  *   #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00199  */
00200 static VALUE
00201 sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint)
00202 {
00203     int family = rsock_family_arg(vfamily);
00204     int level = rsock_level_arg(family, vlevel);
00205     int optname = rsock_optname_arg(family, level, voptname);
00206     int i = NUM2INT(vint);
00207     return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i)));
00208 }
00209 
00210 /*
00211  * call-seq:
00212  *   sockopt.int => integer
00213  *
00214  * Returns the data in _sockopt_ as an int.
00215  *
00216  * The size and endian is dependent on the platform.
00217  *
00218  *   sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00219  *   p sockopt.int => 1
00220  */
00221 static VALUE
00222 sockopt_int(VALUE self)
00223 {
00224     int i;
00225     VALUE data = sockopt_data(self);
00226     StringValue(data);
00227     if (RSTRING_LEN(data) != sizeof(int))
00228         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(int)=%d but %ld",
00229                  (int)sizeof(int), (long)RSTRING_LEN(data));
00230     memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00231     return INT2NUM(i);
00232 }
00233 
00234 /*
00235  * call-seq:
00236  *   Socket::Option.bool(family, level, optname, bool) => sockopt
00237  *
00238  * Creates a new Socket::Option object which contains boolean as data.
00239  * Actually 0 or 1 as int is used.
00240  *
00241  *   p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
00242  *   #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
00243  *
00244  *   p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false)
00245  *   #=> #<Socket::Option: AF_INET SOCKET KEEPALIVE 0>
00246  *
00247  */
00248 static VALUE
00249 sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vbool)
00250 {
00251     int family = rsock_family_arg(vfamily);
00252     int level = rsock_level_arg(family, vlevel);
00253     int optname = rsock_optname_arg(family, level, voptname);
00254     int i = RTEST(vbool) ? 1 : 0;
00255     return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i)));
00256 }
00257 
00258 /*
00259  * call-seq:
00260  *   sockopt.bool => true or false
00261  *
00262  * Returns the data in _sockopt_ as an boolean value.
00263  *
00264  *   sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
00265  *   p sockopt.bool => true
00266  */
00267 static VALUE
00268 sockopt_bool(VALUE self)
00269 {
00270     int i;
00271     VALUE data = sockopt_data(self);
00272     StringValue(data);
00273     if (RSTRING_LEN(data) != sizeof(int))
00274         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(int)=%d but %ld",
00275                  (int)sizeof(int), (long)RSTRING_LEN(data));
00276     memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00277     return i == 0 ? Qfalse : Qtrue;
00278 }
00279 
00280 /*
00281  * call-seq:
00282  *   Socket::Option.linger(onoff, secs) => sockopt
00283  *
00284  * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER.
00285  *
00286  * _onoff_ should be an integer or a boolean.
00287  *
00288  * _secs_ should be the number of seconds.
00289  *
00290  *   p Socket::Option.linger(true, 10)
00291  *   #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec>
00292  *
00293  */
00294 static VALUE
00295 sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs)
00296 {
00297     VALUE tmp;
00298     struct linger l;
00299     memset(&l, 0, sizeof(l));
00300     if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int")))
00301         l.l_onoff = NUM2INT(tmp);
00302     else
00303         l.l_onoff = RTEST(vonoff) ? 1 : 0;
00304     l.l_linger = NUM2INT(vsecs);
00305     return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, rb_str_new((char*)&l, sizeof(l)));
00306 }
00307 
00308 /*
00309  * call-seq:
00310  *   sockopt.linger => [bool, seconds]
00311  *
00312  * Returns the linger data in _sockopt_ as a pair of boolean and integer.
00313  *
00314  *   sockopt = Socket::Option.linger(true, 10)
00315  *   p sockopt.linger => [true, 10]
00316  */
00317 static VALUE
00318 sockopt_linger(VALUE self)
00319 {
00320     int level = sockopt_level(self);
00321     int optname = sockopt_optname(self);
00322     VALUE data = sockopt_data(self);
00323     struct linger l;
00324     VALUE vonoff, vsecs;
00325 
00326     if (level != SOL_SOCKET || optname != SO_LINGER)
00327         rb_raise(rb_eTypeError, "linger socket option expected");
00328     if (RSTRING_LEN(data) != sizeof(l))
00329         rb_raise(rb_eTypeError, "size differ.  expected as sizeof(struct linger)=%d but %ld",
00330                  (int)sizeof(struct linger), (long)RSTRING_LEN(data));
00331     memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger));
00332     switch (l.l_onoff) {
00333       case 0: vonoff = Qfalse; break;
00334       case 1: vonoff = Qtrue; break;
00335       default: vonoff = INT2NUM(l.l_onoff); break;
00336     }
00337     vsecs = INT2NUM(l.l_linger);
00338     return rb_assoc_new(vonoff, vsecs);
00339 }
00340 
00341 /*
00342  * call-seq:
00343  *   Socket::Option.ipv4_multicast_loop(integer) => sockopt
00344  *
00345  * Creates a new Socket::Option object for IP_MULTICAST_LOOP.
00346  *
00347  * The size is dependent on the platform.
00348  *
00349  *   sockopt = Socket::Option.int(:INET, :IPPROTO_IP, :IP_MULTICAST_LOOP, 1)
00350  *   p sockopt.int => 1
00351  *
00352  *   p Socket::Option.ipv4_multicast_loop(10)
00353  *   #=> #<Socket::Option: INET IP MULTICAST_LOOP 10>
00354  *
00355  */
00356 static VALUE
00357 sockopt_s_ipv4_multicast_loop(VALUE klass, VALUE value)
00358 {
00359 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP)
00360 # if defined(__NetBSD__) || defined(__OpenBSD__)
00361     unsigned char i = NUM2CHR(rb_to_int(value));
00362 # else
00363     int i = NUM2INT(rb_to_int(value));
00364 # endif
00365     return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_LOOP,
00366             rb_str_new((char*)&i, sizeof(i)));
00367 #else
00368 # error IPPROTO_IP or IP_MULTICAST_LOOP is not implemented
00369 #endif
00370 }
00371 
00372 /*
00373  * call-seq:
00374  *   sockopt.ipv4_multicast_loop => integer
00375  *
00376  * Returns the ipv4_multicast_loop data in _sockopt_ as a integer.
00377  *
00378  *   sockopt = Socket::Option.ipv4_multicast_loop(10)
00379  *   p sockopt.ipv4_multicast_loop => 10
00380  */
00381 static VALUE
00382 sockopt_ipv4_multicast_loop(VALUE self)
00383 {
00384     int family = NUM2INT(sockopt_family_m(self));
00385     int level = sockopt_level(self);
00386     int optname = sockopt_optname(self);
00387 
00388 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP)
00389     if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_LOOP) {
00390 # if defined(__NetBSD__) || defined(__OpenBSD__)
00391         return sockopt_byte(self);
00392 # else
00393         return sockopt_int(self);
00394 # endif
00395     }
00396 #endif
00397     rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected");
00398     UNREACHABLE;
00399 }
00400 
00401 #if defined(__NetBSD__) || defined(__OpenBSD__)
00402 # define inspect_ipv4_multicast_loop(a,b,c,d) inspect_byte(a,b,c,d)
00403 #else
00404 # define inspect_ipv4_multicast_loop(a,b,c,d) inspect_int(a,b,c,d)
00405 #endif
00406 
00407 /*
00408  * call-seq:
00409  *   Socket::Option.ipv4_multicast_ttl(integer) => sockopt
00410  *
00411  * Creates a new Socket::Option object for IP_MULTICAST_TTL.
00412  *
00413  * The size is dependent on the platform.
00414  *
00415  *   p Socket::Option.ipv4_multicast_ttl(10)
00416  *   #=> #<Socket::Option: INET IP MULTICAST_TTL 10>
00417  *
00418  */
00419 static VALUE
00420 sockopt_s_ipv4_multicast_ttl(VALUE klass, VALUE value)
00421 {
00422 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL)
00423 # if defined(__NetBSD__) || defined(__OpenBSD__)
00424     unsigned char i = NUM2CHR(rb_to_int(value));
00425 # else
00426     int i = NUM2INT(rb_to_int(value));
00427 # endif
00428     return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_TTL,
00429             rb_str_new((char*)&i, sizeof(i)));
00430 #else
00431 # error IPPROTO_IP or IP_MULTICAST_TTL is not implemented
00432 #endif
00433 }
00434 
00435 /*
00436  * call-seq:
00437  *   sockopt.ipv4_multicast_ttl => integer
00438  *
00439  * Returns the ipv4_multicast_ttl data in _sockopt_ as a integer.
00440  *
00441  *   sockopt = Socket::Option.ipv4_multicast_ttl(10)
00442  *   p sockopt.ipv4_multicast_ttl => 10
00443  */
00444 static VALUE
00445 sockopt_ipv4_multicast_ttl(VALUE self)
00446 {
00447     int family = NUM2INT(sockopt_family_m(self));
00448     int level = sockopt_level(self);
00449     int optname = sockopt_optname(self);
00450 
00451 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL)
00452     if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_TTL) {
00453 # if defined(__NetBSD__) || defined(__OpenBSD__)
00454         return sockopt_byte(self);
00455 # else
00456         return sockopt_int(self);
00457 # endif
00458     }
00459 #endif
00460     rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected");
00461     UNREACHABLE;
00462 }
00463 
00464 #if defined(__NetBSD__) || defined(__OpenBSD__)
00465 # define inspect_ipv4_multicast_ttl(a,b,c,d) inspect_byte(a,b,c,d)
00466 #else
00467 # define inspect_ipv4_multicast_ttl(a,b,c,d) inspect_int(a,b,c,d)
00468 #endif
00469 
00470 static int
00471 inspect_int(int level, int optname, VALUE data, VALUE ret)
00472 {
00473     if (RSTRING_LEN(data) == sizeof(int)) {
00474         int i;
00475         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00476         rb_str_catf(ret, " %d", i);
00477         return 1;
00478     }
00479     else {
00480         return 0;
00481     }
00482 }
00483 
00484 #if defined(__NetBSD__) || defined(__OpenBSD__)
00485 static int
00486 inspect_byte(int level, int optname, VALUE data, VALUE ret)
00487 {
00488     if (RSTRING_LEN(data) == sizeof(unsigned char)) {
00489         rb_str_catf(ret, " %d", (unsigned char)*RSTRING_PTR(data));
00490         return 1;
00491     }
00492     else {
00493         return 0;
00494     }
00495 }
00496 #endif
00497 
00498 static int
00499 inspect_errno(int level, int optname, VALUE data, VALUE ret)
00500 {
00501     if (RSTRING_LEN(data) == sizeof(int)) {
00502         int i;
00503         char *err;
00504         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00505         err = strerror(i);
00506         rb_str_catf(ret, " %s (%d)", err, i);
00507         return 1;
00508     }
00509     else {
00510         return 0;
00511     }
00512 }
00513 
00514 #if defined(IPV6_MULTICAST_LOOP)
00515 static int
00516 inspect_uint(int level, int optname, VALUE data, VALUE ret)
00517 {
00518     if (RSTRING_LEN(data) == sizeof(int)) {
00519         unsigned int i;
00520         memcpy((char*)&i, RSTRING_PTR(data), sizeof(unsigned int));
00521         rb_str_catf(ret, " %u", i);
00522         return 1;
00523     }
00524     else {
00525         return 0;
00526     }
00527 }
00528 #endif
00529 
00530 #if defined(SOL_SOCKET) && defined(SO_LINGER) /* POSIX */
00531 static int
00532 inspect_linger(int level, int optname, VALUE data, VALUE ret)
00533 {
00534     if (RSTRING_LEN(data) == sizeof(struct linger)) {
00535         struct linger s;
00536         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00537         switch (s.l_onoff) {
00538           case 0: rb_str_cat2(ret, " off"); break;
00539           case 1: rb_str_cat2(ret, " on"); break;
00540           default: rb_str_catf(ret, " on(%d)", s.l_onoff); break;
00541         }
00542         rb_str_catf(ret, " %dsec", s.l_linger);
00543         return 1;
00544     }
00545     else {
00546         return 0;
00547     }
00548 }
00549 #endif
00550 
00551 #if defined(SOL_SOCKET) && defined(SO_TYPE) /* POSIX */
00552 static int
00553 inspect_socktype(int level, int optname, VALUE data, VALUE ret)
00554 {
00555     if (RSTRING_LEN(data) == sizeof(int)) {
00556         int i;
00557         ID id;
00558         memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
00559         id = rsock_intern_socktype(i);
00560         if (id)
00561             rb_str_catf(ret, " %s", rb_id2name(id));
00562         else
00563             rb_str_catf(ret, " %d", i);
00564         return 1;
00565     }
00566     else {
00567         return 0;
00568     }
00569 }
00570 #endif
00571 
00572 static int
00573 inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret)
00574 {
00575     if (RSTRING_LEN(data) == sizeof(struct timeval)) {
00576         struct timeval s;
00577         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00578         rb_str_catf(ret, " %ld.%06ldsec", (long)s.tv_sec, (long)s.tv_usec);
00579         return 1;
00580     }
00581     else {
00582         return 0;
00583     }
00584 }
00585 
00586 /*
00587  * socket option for IPv4 multicast is bit confusing.
00588  *
00589  * IP Multicast is implemented by Steve Deering at first:
00590  *   IP Multicast Extensions for 4.3BSD UNIX and related systems
00591  *   (MULTICAST 1.2 Release)
00592  *   http://www.kohala.com/start/mcast.api.txt
00593  *
00594  * There are 3 socket options which takes a struct.
00595  *
00596  *   IP_MULTICAST_IF: struct in_addr
00597  *   IP_ADD_MEMBERSHIP: struct ip_mreq
00598  *   IP_DROP_MEMBERSHIP: struct ip_mreq
00599  *
00600  * But they uses an IP address to specify an interface.
00601  * This means the API cannot specify an unnumbered interface.
00602  *
00603  * Linux 2.4 introduces struct ip_mreqn to fix this problem.
00604  * struct ip_mreqn has imr_ifindex field to specify interface index.
00605  *
00606  *   IP_MULTICAST_IF: struct ip_mreqn
00607  *   IP_ADD_MEMBERSHIP: struct ip_mreqn
00608  *   IP_DROP_MEMBERSHIP: struct ip_mreqn
00609  *
00610  * FreeBSD 7 obtained struct ip_mreqn for IP_MULTICAST_IF.
00611  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/netinet/in.h.diff?r1=1.99;r2=1.100
00612  *
00613  * Another hackish workaround is "RFC 1724 hack".
00614  * RFC 1724 section 3.3 suggests unnumbered interfaces
00615  * specified by pseudo address 0.0.0.0/8.
00616  * NetBSD 4 and FreeBSD 5 documented it.
00617  * http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.16&r2=1.17
00618  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.37;r2=1.38
00619  * FreeBSD 7.0 removed it.
00620  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.49;r2=1.50
00621  *
00622  * RFC 1724 hack is not supported by Socket::Option#inspect because
00623  * it is not distinguishable by the size.
00624  */
00625 
00626 #ifndef HAVE_INET_NTOP
00627 static const char *
00628 inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
00629 {
00630 #ifdef HAVE_INET_NTOA
00631     struct in_addr in;
00632     memcpy(&in.s_addr, addr, sizeof(in.s_addr));
00633     snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
00634 #else
00635     unsigned long x = ntohl(*(unsigned long*)addr);
00636     snprintf(numaddr, numaddr_len, "%d.%d.%d.%d",
00637              (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
00638              (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
00639 #endif
00640     return numaddr;
00641 }
00642 #elif defined __MINGW64__
00643 # define inet_ntop(f,a,n,l)      rb_w32_inet_ntop(f,a,n,l)
00644 #endif
00645 
00646 /* Although the buffer size needed depends on the prefixes, "%u" may generate "4294967295".  */
00647 static int
00648 rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int ifindex, char *buf, size_t len)
00649 {
00650 #if defined(HAVE_IF_INDEXTONAME)
00651     char ifbuf[IFNAMSIZ];
00652     if (if_indextoname(ifindex, ifbuf) == NULL)
00653         return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
00654     else
00655         return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
00656 #else
00657 #   ifndef IFNAMSIZ
00658 #       define IFNAMSIZ (sizeof(unsigned int)*3+1)
00659 #   endif
00660     return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
00661 #endif
00662 }
00663 
00664 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
00665 static int
00666 inspect_ipv4_mreq(int level, int optname, VALUE data, VALUE ret)
00667 {
00668     if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) {
00669         struct ip_mreq s;
00670         char addrbuf[INET_ADDRSTRLEN];
00671         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00672         if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00673             rb_str_cat2(ret, " invalid-address");
00674         else
00675             rb_str_catf(ret, " %s", addrbuf);
00676         if (inet_ntop(AF_INET, &s.imr_interface, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00677             rb_str_catf(ret, " invalid-address");
00678         else
00679             rb_str_catf(ret, " %s", addrbuf);
00680         return 1;
00681     }
00682     else {
00683         return 0;
00684     }
00685 }
00686 #endif
00687 
00688 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* GNU/Linux, FreeBSD 7 */
00689 static int
00690 inspect_ipv4_mreqn(int level, int optname, VALUE data, VALUE ret)
00691 {
00692     if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
00693         struct ip_mreqn s;
00694         char addrbuf[INET_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
00695         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00696         if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00697             rb_str_cat2(ret, " invalid-address");
00698         else
00699             rb_str_catf(ret, " %s", addrbuf);
00700         if (inet_ntop(AF_INET, &s.imr_address, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00701             rb_str_catf(ret, " invalid-address");
00702         else
00703             rb_str_catf(ret, " %s", addrbuf);
00704         rb_if_indextoname(" ", " ifindex:", s.imr_ifindex, ifbuf, sizeof(ifbuf));
00705         rb_str_cat2(ret, ifbuf);
00706         return 1;
00707     }
00708     else {
00709         return 0;
00710     }
00711 }
00712 #endif
00713 
00714 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
00715 static int
00716 inspect_ipv4_add_drop_membership(int level, int optname, VALUE data, VALUE ret)
00717 {
00718     if (RSTRING_LEN(data) == sizeof(struct ip_mreq))
00719         return inspect_ipv4_mreq(level, optname, data, ret);
00720 # if defined(HAVE_TYPE_STRUCT_IP_MREQN)
00721     else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn))
00722         return inspect_ipv4_mreqn(level, optname, data, ret);
00723 # endif
00724     else
00725         return 0;
00726 }
00727 #endif
00728 
00729 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
00730 static int
00731 inspect_ipv4_multicast_if(int level, int optname, VALUE data, VALUE ret)
00732 {
00733     if (RSTRING_LEN(data) == sizeof(struct in_addr)) {
00734         struct in_addr s;
00735         char addrbuf[INET_ADDRSTRLEN];
00736         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00737         if (inet_ntop(AF_INET, &s, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00738             rb_str_cat2(ret, " invalid-address");
00739         else
00740             rb_str_catf(ret, " %s", addrbuf);
00741         return 1;
00742     }
00743     else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
00744         return inspect_ipv4_mreqn(level, optname, data, ret);
00745     }
00746     else {
00747         return 0;
00748     }
00749 }
00750 #endif
00751 
00752 #if defined(IPV6_MULTICAST_IF) /* POSIX, RFC 3493 */
00753 static int
00754 inspect_ipv6_multicast_if(int level, int optname, VALUE data, VALUE ret)
00755 {
00756     if (RSTRING_LEN(data) == sizeof(int)) {
00757         char ifbuf[32+IFNAMSIZ];
00758         unsigned int ifindex;
00759         memcpy((char*)&ifindex, RSTRING_PTR(data), sizeof(unsigned int));
00760         rb_if_indextoname(" ", " ", ifindex, ifbuf, sizeof(ifbuf));
00761         rb_str_cat2(ret, ifbuf);
00762         return 1;
00763     }
00764     else {
00765         return 0;
00766     }
00767 }
00768 #endif
00769 
00770 #if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */
00771 static int
00772 inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret)
00773 {
00774     if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) {
00775         struct ipv6_mreq s;
00776         char addrbuf[INET6_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
00777         memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
00778         if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
00779             rb_str_cat2(ret, " invalid-address");
00780         else
00781             rb_str_catf(ret, " %s", addrbuf);
00782         rb_if_indextoname(" ", " interface:", s.ipv6mr_interface, ifbuf, sizeof(ifbuf));
00783         rb_str_cat2(ret, ifbuf);
00784         return 1;
00785     }
00786     else {
00787         return 0;
00788     }
00789 }
00790 #endif
00791 
00792 #if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
00793 #if defined(__OpenBSD__)
00794 #define RUBY_SOCK_PEERCRED struct sockpeercred
00795 #else
00796 #define RUBY_SOCK_PEERCRED struct ucred
00797 #endif
00798 static int
00799 inspect_peercred(int level, int optname, VALUE data, VALUE ret)
00800 {
00801     if (RSTRING_LEN(data) == sizeof(RUBY_SOCK_PEERCRED)) {
00802         RUBY_SOCK_PEERCRED cred;
00803         memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED));
00804         rb_str_catf(ret, " pid=%u euid=%u egid=%u",
00805                     (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid);
00806         rb_str_cat2(ret, " (ucred)");
00807         return 1;
00808     }
00809     else {
00810         return 0;
00811     }
00812 }
00813 #endif
00814 
00815 #if defined(LOCAL_PEERCRED) /* FreeBSD, MacOS X */
00816 static int
00817 inspect_local_peercred(int level, int optname, VALUE data, VALUE ret)
00818 {
00819     if (RSTRING_LEN(data) == sizeof(struct xucred)) {
00820         struct xucred cred;
00821         memcpy(&cred, RSTRING_PTR(data), sizeof(struct xucred));
00822         if (cred.cr_version != XUCRED_VERSION)
00823             return 0;
00824         rb_str_catf(ret, " version=%u", cred.cr_version);
00825         rb_str_catf(ret, " euid=%u", cred.cr_uid);
00826         if (cred.cr_ngroups) {
00827             int i;
00828             const char *sep = " groups=";
00829             for (i = 0; i < cred.cr_ngroups; i++) {
00830                 rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]);
00831                 sep = ",";
00832             }
00833         }
00834         rb_str_cat2(ret, " (xucred)");
00835         return 1;
00836     }
00837     else {
00838         return 0;
00839     }
00840 }
00841 #endif
00842 
00843 
00844 /*
00845  * call-seq:
00846  *   sockopt.inspect => string
00847  *
00848  * Returns a string which shows sockopt in human-readable form.
00849  *
00850  *   p Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")).inspect
00851  *   #=> "#<Socket::Option: INET SOCKET KEEPALIVE 1>"
00852  *
00853  */
00854 static VALUE
00855 sockopt_inspect(VALUE self)
00856 {
00857     int family = NUM2INT(sockopt_family_m(self));
00858     int level = NUM2INT(sockopt_level_m(self));
00859     int optname = NUM2INT(sockopt_optname_m(self));
00860     VALUE data = sockopt_data(self);
00861     VALUE v, ret;
00862     ID family_id, level_id, optname_id;
00863     int inspected;
00864 
00865     StringValue(data);
00866 
00867     ret = rb_sprintf("#<%s:", rb_obj_classname(self));
00868 
00869     family_id = rsock_intern_family_noprefix(family);
00870     if (family_id)
00871         rb_str_catf(ret, " %s", rb_id2name(family_id));
00872     else
00873         rb_str_catf(ret, " family:%d", family);
00874 
00875     if (level == SOL_SOCKET) {
00876         rb_str_cat2(ret, " SOCKET");
00877 
00878         optname_id = rsock_intern_so_optname(optname);
00879         if (optname_id)
00880             rb_str_catf(ret, " %s", rb_id2name(optname_id));
00881         else
00882             rb_str_catf(ret, " optname:%d", optname);
00883     }
00884 #ifdef HAVE_SYS_UN_H
00885     else if (family == AF_UNIX) {
00886         rb_str_catf(ret, " level:%d", level);
00887 
00888         optname_id = rsock_intern_local_optname(optname);
00889         if (optname_id)
00890             rb_str_catf(ret, " %s", rb_id2name(optname_id));
00891         else
00892             rb_str_catf(ret, " optname:%d", optname);
00893     }
00894 #endif
00895     else if (IS_IP_FAMILY(family)) {
00896         level_id = rsock_intern_iplevel(level);
00897         if (level_id)
00898             rb_str_catf(ret, " %s", rb_id2name(level_id));
00899         else
00900             rb_str_catf(ret, " level:%d", level);
00901 
00902         v = optname_to_sym(level, optname);
00903         if (SYMBOL_P(v))
00904             rb_str_catf(ret, " %s", rb_id2name(SYM2ID(v)));
00905         else
00906             rb_str_catf(ret, " optname:%d", optname);
00907     }
00908     else {
00909         rb_str_catf(ret, " level:%d", level);
00910         rb_str_catf(ret, " optname:%d", optname);
00911     }
00912 
00913     inspected = 0;
00914 
00915     if (level == SOL_SOCKET)
00916         family = AF_UNSPEC;
00917     switch (family) {
00918       case AF_UNSPEC:
00919         switch (level) {
00920           case SOL_SOCKET:
00921             switch (optname) {
00922 #            if defined(SO_DEBUG) /* POSIX */
00923               case SO_DEBUG: inspected = inspect_int(level, optname, data, ret); break;
00924 #            endif
00925 #            if defined(SO_ERROR) /* POSIX */
00926               case SO_ERROR: inspected = inspect_errno(level, optname, data, ret); break;
00927 #            endif
00928 #            if defined(SO_TYPE) /* POSIX */
00929               case SO_TYPE: inspected = inspect_socktype(level, optname, data, ret); break;
00930 #            endif
00931 #            if defined(SO_ACCEPTCONN) /* POSIX */
00932               case SO_ACCEPTCONN: inspected = inspect_int(level, optname, data, ret); break;
00933 #            endif
00934 #            if defined(SO_BROADCAST) /* POSIX */
00935               case SO_BROADCAST: inspected = inspect_int(level, optname, data, ret); break;
00936 #            endif
00937 #            if defined(SO_REUSEADDR) /* POSIX */
00938               case SO_REUSEADDR: inspected = inspect_int(level, optname, data, ret); break;
00939 #            endif
00940 #            if defined(SO_KEEPALIVE) /* POSIX */
00941               case SO_KEEPALIVE: inspected = inspect_int(level, optname, data, ret); break;
00942 #            endif
00943 #            if defined(SO_OOBINLINE) /* POSIX */
00944               case SO_OOBINLINE: inspected = inspect_int(level, optname, data, ret); break;
00945 #            endif
00946 #            if defined(SO_SNDBUF) /* POSIX */
00947               case SO_SNDBUF: inspected = inspect_int(level, optname, data, ret); break;
00948 #            endif
00949 #            if defined(SO_RCVBUF) /* POSIX */
00950               case SO_RCVBUF: inspected = inspect_int(level, optname, data, ret); break;
00951 #            endif
00952 #            if defined(SO_DONTROUTE) /* POSIX */
00953               case SO_DONTROUTE: inspected = inspect_int(level, optname, data, ret); break;
00954 #            endif
00955 #            if defined(SO_RCVLOWAT) /* POSIX */
00956               case SO_RCVLOWAT: inspected = inspect_int(level, optname, data, ret); break;
00957 #            endif
00958 #            if defined(SO_SNDLOWAT) /* POSIX */
00959               case SO_SNDLOWAT: inspected = inspect_int(level, optname, data, ret); break;
00960 #            endif
00961 #            if defined(SO_LINGER) /* POSIX */
00962               case SO_LINGER: inspected = inspect_linger(level, optname, data, ret); break;
00963 #            endif
00964 #            if defined(SO_RCVTIMEO) /* POSIX */
00965               case SO_RCVTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
00966 #            endif
00967 #            if defined(SO_SNDTIMEO) /* POSIX */
00968               case SO_SNDTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
00969 #            endif
00970 #            if defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
00971               case SO_PEERCRED: inspected = inspect_peercred(level, optname, data, ret); break;
00972 #            endif
00973             }
00974             break;
00975         }
00976         break;
00977 
00978       case AF_INET:
00979 #ifdef INET6
00980       case AF_INET6:
00981 #endif
00982         switch (level) {
00983 #        if defined(IPPROTO_IP)
00984           case IPPROTO_IP:
00985             switch (optname) {
00986 #            if defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
00987               case IP_MULTICAST_IF: inspected = inspect_ipv4_multicast_if(level, optname, data, ret); break;
00988 #            endif
00989 #            if defined(IP_ADD_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
00990               case IP_ADD_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
00991 #            endif
00992 #            if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
00993               case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
00994 #            endif
00995 #            if defined(IP_MULTICAST_LOOP) /* 4.4BSD, GNU/Linux */
00996               case IP_MULTICAST_LOOP: inspected = inspect_ipv4_multicast_loop(level, optname, data, ret); break;
00997 #            endif
00998 #            if defined(IP_MULTICAST_TTL) /* 4.4BSD, GNU/Linux */
00999               case IP_MULTICAST_TTL: inspected = inspect_ipv4_multicast_ttl(level, optname, data, ret); break;
01000 #            endif
01001             }
01002             break;
01003 #        endif
01004 
01005 #        if defined(IPPROTO_IPV6)
01006           case IPPROTO_IPV6:
01007             switch (optname) {
01008 #            if defined(IPV6_MULTICAST_HOPS) /* POSIX */
01009               case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
01010 #            endif
01011 #            if defined(IPV6_MULTICAST_IF) /* POSIX */
01012               case IPV6_MULTICAST_IF: inspected = inspect_ipv6_multicast_if(level, optname, data, ret); break;
01013 #            endif
01014 #            if defined(IPV6_MULTICAST_LOOP) /* POSIX */
01015               case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break;
01016 #            endif
01017 #            if defined(IPV6_JOIN_GROUP) /* POSIX */
01018               case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
01019 #            endif
01020 #            if defined(IPV6_LEAVE_GROUP) /* POSIX */
01021               case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
01022 #            endif
01023 #            if defined(IPV6_UNICAST_HOPS) /* POSIX */
01024               case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
01025 #            endif
01026 #            if defined(IPV6_V6ONLY) /* POSIX */
01027               case IPV6_V6ONLY: inspected = inspect_int(level, optname, data, ret); break;
01028 #            endif
01029             }
01030             break;
01031 #        endif
01032 
01033 #        if defined(IPPROTO_TCP)
01034           case IPPROTO_TCP:
01035             switch (optname) {
01036 #            if defined(TCP_NODELAY) /* POSIX */
01037               case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break;
01038 #            endif
01039             }
01040             break;
01041 #        endif
01042         }
01043         break;
01044 
01045 #ifdef HAVE_SYS_UN_H
01046       case AF_UNIX:
01047         switch (level) {
01048           case 0:
01049             switch (optname) {
01050 #            if defined(LOCAL_PEERCRED)
01051               case LOCAL_PEERCRED: inspected = inspect_local_peercred(level, optname, data, ret); break;
01052 #            endif
01053             }
01054             break;
01055         }
01056         break;
01057 #endif
01058     }
01059 
01060     if (!inspected) {
01061         rb_str_cat2(ret, " ");
01062         rb_str_append(ret, rb_str_dump(data));
01063     }
01064 
01065     rb_str_cat2(ret, ">");
01066 
01067     return ret;
01068 }
01069 
01070 /*
01071  * call-seq:
01072  *   sockopt.unpack(template) => array
01073  *
01074  * Calls String#unpack on sockopt.data.
01075  *
01076  *   sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
01077  *   p sockopt.unpack("i")      #=> [1]
01078  *   p sockopt.data.unpack("i") #=> [1]
01079  */
01080 static VALUE
01081 sockopt_unpack(VALUE self, VALUE template)
01082 {
01083     return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template);
01084 }
01085 
01086 void
01087 rsock_init_sockopt(void)
01088 {
01089     /*
01090      * Document-class: Socket::Option
01091      *
01092      * Socket::Option represents a socket option used by
01093      * BasicSocket#getsockopt and BasicSocket#setsockopt.  A socket option
01094      * contains the socket #family, protocol #level, option name #optname and
01095      * option value #data.
01096      */
01097     rb_cSockOpt = rb_define_class_under(rb_cSocket, "Option", rb_cObject);
01098     rb_define_method(rb_cSockOpt, "initialize", sockopt_initialize, 4);
01099     rb_define_method(rb_cSockOpt, "family", sockopt_family_m, 0);
01100     rb_define_method(rb_cSockOpt, "level", sockopt_level_m, 0);
01101     rb_define_method(rb_cSockOpt, "optname", sockopt_optname_m, 0);
01102     rb_define_method(rb_cSockOpt, "data", sockopt_data, 0);
01103     rb_define_method(rb_cSockOpt, "inspect", sockopt_inspect, 0);
01104 
01105     rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4);
01106     rb_define_method(rb_cSockOpt, "int", sockopt_int, 0);
01107 
01108     rb_define_singleton_method(rb_cSockOpt, "byte", sockopt_s_byte, 4);
01109     rb_define_method(rb_cSockOpt, "byte", sockopt_byte, 0);
01110 
01111     rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4);
01112     rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0);
01113 
01114     rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2);
01115     rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0);
01116 
01117     rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_s_ipv4_multicast_ttl, 1);
01118     rb_define_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_ipv4_multicast_ttl, 0);
01119 
01120     rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_s_ipv4_multicast_loop, 1);
01121     rb_define_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_ipv4_multicast_loop, 0);
01122 
01123     rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1);
01124 
01125     rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */
01126 }
01127 
01128 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7