Ruby
2.0.0p247(2013-06-27revision41674)
|
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