1#include "rubysocket.h" 2 3VALUE rb_cSockOpt; 4 5static VALUE 6constant_to_sym(int constant, ID (*intern_const)(int)) 7{ 8 ID name = intern_const(constant); 9 if (name) { 10 return ID2SYM(name); 11 } 12 13 return INT2NUM(constant); 14} 15 16static VALUE 17optname_to_sym(int level, int optname) 18{ 19 switch (level) { 20 case SOL_SOCKET: 21 return constant_to_sym(optname, rsock_intern_so_optname); 22 case IPPROTO_IP: 23 return constant_to_sym(optname, rsock_intern_ip_optname); 24#ifdef INET6 25 case IPPROTO_IPV6: 26 return constant_to_sym(optname, rsock_intern_ipv6_optname); 27#endif 28 case IPPROTO_TCP: 29 return constant_to_sym(optname, rsock_intern_tcp_optname); 30 case IPPROTO_UDP: 31 return constant_to_sym(optname, rsock_intern_udp_optname); 32 default: 33 return INT2NUM(optname); 34 } 35} 36 37/* 38 * call-seq: 39 * Socket::Option.new(family, level, optname, data) => sockopt 40 * 41 * Returns a new Socket::Option object. 42 * 43 * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")) 44 * p sockopt #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> 45 * 46 */ 47static VALUE 48sockopt_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE data) 49{ 50 int family = rsock_family_arg(vfamily); 51 int level = rsock_level_arg(family, vlevel); 52 int optname = rsock_optname_arg(family, level, voptname); 53 StringValue(data); 54 rb_ivar_set(self, rb_intern("family"), INT2NUM(family)); 55 rb_ivar_set(self, rb_intern("level"), INT2NUM(level)); 56 rb_ivar_set(self, rb_intern("optname"), INT2NUM(optname)); 57 rb_ivar_set(self, rb_intern("data"), data); 58 return self; 59} 60 61VALUE 62rsock_sockopt_new(int family, int level, int optname, VALUE data) 63{ 64 NEWOBJ_OF(obj, struct RObject, rb_cSockOpt, T_OBJECT); 65 StringValue(data); 66 sockopt_initialize((VALUE)obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data); 67 return (VALUE)obj; 68} 69 70/* 71 * call-seq: 72 * sockopt.family => integer 73 * 74 * returns the socket family as an integer. 75 * 76 * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).family 77 * #=> 10 78 */ 79static VALUE 80sockopt_family_m(VALUE self) 81{ 82 return rb_attr_get(self, rb_intern("family")); 83} 84 85static int 86sockopt_level(VALUE self) 87{ 88 return NUM2INT(rb_attr_get(self, rb_intern("level"))); 89} 90 91/* 92 * call-seq: 93 * sockopt.level => integer 94 * 95 * returns the socket level as an integer. 96 * 97 * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).level 98 * #=> 41 99 */ 100static VALUE 101sockopt_level_m(VALUE self) 102{ 103 return INT2NUM(sockopt_level(self)); 104} 105 106static int 107sockopt_optname(VALUE self) 108{ 109 return NUM2INT(rb_attr_get(self, rb_intern("optname"))); 110} 111 112/* 113 * call-seq: 114 * sockopt.optname => integer 115 * 116 * returns the socket option name as an integer. 117 * 118 * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).optname 119 * #=> 2 120 */ 121static VALUE 122sockopt_optname_m(VALUE self) 123{ 124 return INT2NUM(sockopt_optname(self)); 125} 126 127/* 128 * call-seq: 129 * sockopt.data => string 130 * 131 * returns the socket option data as a string. 132 * 133 * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).data 134 * #=> "\x01\x00\x00\x00" 135 */ 136static VALUE 137sockopt_data(VALUE self) 138{ 139 VALUE v = rb_attr_get(self, rb_intern("data")); 140 StringValue(v); 141 return v; 142} 143 144/* 145 * call-seq: 146 * Socket::Option.int(family, level, optname, integer) => sockopt 147 * 148 * Creates a new Socket::Option object which contains an int as data. 149 * 150 * The size and endian is dependent on the platform. 151 * 152 * p Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) 153 * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> 154 */ 155static VALUE 156sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint) 157{ 158 int family = rsock_family_arg(vfamily); 159 int level = rsock_level_arg(family, vlevel); 160 int optname = rsock_optname_arg(family, level, voptname); 161 int i = NUM2INT(vint); 162 return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i))); 163} 164 165/* 166 * call-seq: 167 * sockopt.int => integer 168 * 169 * Returns the data in _sockopt_ as an int. 170 * 171 * The size and endian is dependent on the platform. 172 * 173 * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) 174 * p sockopt.int => 1 175 */ 176static VALUE 177sockopt_int(VALUE self) 178{ 179 int i; 180 VALUE data = sockopt_data(self); 181 StringValue(data); 182 if (RSTRING_LEN(data) != sizeof(int)) 183 rb_raise(rb_eTypeError, "size differ. expected as sizeof(int)=%d but %ld", 184 (int)sizeof(int), (long)RSTRING_LEN(data)); 185 memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); 186 return INT2NUM(i); 187} 188 189/* 190 * call-seq: 191 * Socket::Option.bool(family, level, optname, bool) => sockopt 192 * 193 * Creates a new Socket::Option object which contains boolean as data. 194 * Actually 0 or 1 as int is used. 195 * 196 * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) 197 * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> 198 * 199 * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false) 200 * #=> #<Socket::Option: AF_INET SOCKET KEEPALIVE 0> 201 * 202 */ 203static VALUE 204sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vbool) 205{ 206 int family = rsock_family_arg(vfamily); 207 int level = rsock_level_arg(family, vlevel); 208 int optname = rsock_optname_arg(family, level, voptname); 209 int i = RTEST(vbool) ? 1 : 0; 210 return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i))); 211} 212 213/* 214 * call-seq: 215 * sockopt.bool => true or false 216 * 217 * Returns the data in _sockopt_ as an boolean value. 218 * 219 * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) 220 * p sockopt.bool => true 221 */ 222static VALUE 223sockopt_bool(VALUE self) 224{ 225 int i; 226 VALUE data = sockopt_data(self); 227 StringValue(data); 228 if (RSTRING_LEN(data) != sizeof(int)) 229 rb_raise(rb_eTypeError, "size differ. expected as sizeof(int)=%d but %ld", 230 (int)sizeof(int), (long)RSTRING_LEN(data)); 231 memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); 232 return i == 0 ? Qfalse : Qtrue; 233} 234 235/* 236 * call-seq: 237 * Socket::Option.linger(onoff, secs) => sockopt 238 * 239 * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER. 240 * 241 * _onoff_ should be an integer or a boolean. 242 * 243 * _secs_ should be the number of seconds. 244 * 245 * p Socket::Option.linger(true, 10) 246 * #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec> 247 * 248 */ 249static VALUE 250sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs) 251{ 252 VALUE tmp; 253 struct linger l; 254 memset(&l, 0, sizeof(l)); 255 if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int"))) 256 l.l_onoff = NUM2INT(tmp); 257 else 258 l.l_onoff = RTEST(vonoff) ? 1 : 0; 259 l.l_linger = NUM2INT(vsecs); 260 return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, rb_str_new((char*)&l, sizeof(l))); 261} 262 263/* 264 * call-seq: 265 * sockopt.linger => [bool, seconds] 266 * 267 * Returns the linger data in _sockopt_ as a pair of boolean and integer. 268 * 269 * sockopt = Socket::Option.linger(true, 10) 270 * p sockopt.linger => [true, 10] 271 */ 272static VALUE 273sockopt_linger(VALUE self) 274{ 275 int level = sockopt_level(self); 276 int optname = sockopt_optname(self); 277 VALUE data = sockopt_data(self); 278 struct linger l; 279 VALUE vonoff, vsecs; 280 281 if (level != SOL_SOCKET || optname != SO_LINGER) 282 rb_raise(rb_eTypeError, "linger socket option expected"); 283 if (RSTRING_LEN(data) != sizeof(l)) 284 rb_raise(rb_eTypeError, "size differ. expected as sizeof(struct linger)=%d but %ld", 285 (int)sizeof(struct linger), (long)RSTRING_LEN(data)); 286 memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger)); 287 switch (l.l_onoff) { 288 case 0: vonoff = Qfalse; break; 289 case 1: vonoff = Qtrue; break; 290 default: vonoff = INT2NUM(l.l_onoff); break; 291 } 292 vsecs = INT2NUM(l.l_linger); 293 return rb_assoc_new(vonoff, vsecs); 294} 295 296static int 297inspect_int(int level, int optname, VALUE data, VALUE ret) 298{ 299 if (RSTRING_LEN(data) == sizeof(int)) { 300 int i; 301 memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); 302 rb_str_catf(ret, " %d", i); 303 return 1; 304 } 305 else { 306 return 0; 307 } 308} 309 310static int 311inspect_errno(int level, int optname, VALUE data, VALUE ret) 312{ 313 if (RSTRING_LEN(data) == sizeof(int)) { 314 int i; 315 char *err; 316 memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); 317 err = strerror(i); 318 rb_str_catf(ret, " %s (%d)", err, i); 319 return 1; 320 } 321 else { 322 return 0; 323 } 324} 325 326#if defined(IPV6_MULTICAST_LOOP) 327static int 328inspect_uint(int level, int optname, VALUE data, VALUE ret) 329{ 330 if (RSTRING_LEN(data) == sizeof(int)) { 331 unsigned int i; 332 memcpy((char*)&i, RSTRING_PTR(data), sizeof(unsigned int)); 333 rb_str_catf(ret, " %u", i); 334 return 1; 335 } 336 else { 337 return 0; 338 } 339} 340#endif 341 342#if defined(SOL_SOCKET) && defined(SO_LINGER) /* POSIX */ 343static int 344inspect_linger(int level, int optname, VALUE data, VALUE ret) 345{ 346 if (RSTRING_LEN(data) == sizeof(struct linger)) { 347 struct linger s; 348 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 349 switch (s.l_onoff) { 350 case 0: rb_str_cat2(ret, " off"); break; 351 case 1: rb_str_cat2(ret, " on"); break; 352 default: rb_str_catf(ret, " on(%d)", s.l_onoff); break; 353 } 354 rb_str_catf(ret, " %dsec", s.l_linger); 355 return 1; 356 } 357 else { 358 return 0; 359 } 360} 361#endif 362 363#if defined(SOL_SOCKET) && defined(SO_TYPE) /* POSIX */ 364static int 365inspect_socktype(int level, int optname, VALUE data, VALUE ret) 366{ 367 if (RSTRING_LEN(data) == sizeof(int)) { 368 int i; 369 ID id; 370 memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); 371 id = rsock_intern_socktype(i); 372 if (id) 373 rb_str_catf(ret, " %s", rb_id2name(id)); 374 else 375 rb_str_catf(ret, " %d", i); 376 return 1; 377 } 378 else { 379 return 0; 380 } 381} 382#endif 383 384static int 385inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret) 386{ 387 if (RSTRING_LEN(data) == sizeof(struct timeval)) { 388 struct timeval s; 389 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 390 rb_str_catf(ret, " %ld.%06ldsec", (long)s.tv_sec, (long)s.tv_usec); 391 return 1; 392 } 393 else { 394 return 0; 395 } 396} 397 398/* 399 * socket option for IPv4 multicast is bit confusing. 400 * 401 * IP Multicast is implemented by Steve Deering at first: 402 * IP Multicast Extensions for 4.3BSD UNIX and related systems 403 * (MULTICAST 1.2 Release) 404 * http://www.kohala.com/start/mcast.api.txt 405 * 406 * There are 3 socket options which takes a struct. 407 * 408 * IP_MULTICAST_IF: struct in_addr 409 * IP_ADD_MEMBERSHIP: struct ip_mreq 410 * IP_DROP_MEMBERSHIP: struct ip_mreq 411 * 412 * But they uses an IP address to specify an interface. 413 * This means the API cannot specify an unnumbered interface. 414 * 415 * Linux 2.4 introduces struct ip_mreqn to fix this problem. 416 * struct ip_mreqn has imr_ifindex field to specify interface index. 417 * 418 * IP_MULTICAST_IF: struct ip_mreqn 419 * IP_ADD_MEMBERSHIP: struct ip_mreqn 420 * IP_DROP_MEMBERSHIP: struct ip_mreqn 421 * 422 * FreeBSD 7 obtained struct ip_mreqn for IP_MULTICAST_IF. 423 * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/netinet/in.h.diff?r1=1.99;r2=1.100 424 * 425 * Another hackish workaround is "RFC 1724 hack". 426 * RFC 1724 section 3.3 suggests unnumbered interfaces 427 * specified by pseudo address 0.0.0.0/8. 428 * NetBSD 4 and FreeBSD 5 documented it. 429 * http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.16&r2=1.17 430 * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.37;r2=1.38 431 * FreeBSD 7.0 removed it. 432 * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.49;r2=1.50 433 * 434 * RFC 1724 hack is not supported by Socket::Option#inspect because 435 * it is not distinguishable by the size. 436 */ 437 438#ifndef HAVE_INET_NTOP 439static const char * 440inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len) 441{ 442#ifdef HAVE_INET_NTOA 443 struct in_addr in; 444 memcpy(&in.s_addr, addr, sizeof(in.s_addr)); 445 snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in)); 446#else 447 unsigned long x = ntohl(*(unsigned long*)addr); 448 snprintf(numaddr, numaddr_len, "%d.%d.%d.%d", 449 (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, 450 (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); 451#endif 452 return numaddr; 453} 454#elif defined __MINGW64__ 455# define inet_ntop(f,a,n,l) rb_w32_inet_ntop(f,a,n,l) 456#endif 457 458/* Although the buffer size needed depends on the prefixes, "%u" may generate "4294967295". */ 459static int 460rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int ifindex, char *buf, size_t len) 461{ 462#if defined(HAVE_IF_INDEXTONAME) 463 char ifbuf[IFNAMSIZ]; 464 if (if_indextoname(ifindex, ifbuf) == NULL) 465 return snprintf(buf, len, "%s%u", fail_prefix, ifindex); 466 else 467 return snprintf(buf, len, "%s%s", succ_prefix, ifbuf); 468#else 469# ifndef IFNAMSIZ 470# define IFNAMSIZ (sizeof(unsigned int)*3+1) 471# endif 472 return snprintf(buf, len, "%s%u", fail_prefix, ifindex); 473#endif 474} 475 476#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */ 477static int 478inspect_ipv4_mreq(int level, int optname, VALUE data, VALUE ret) 479{ 480 if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) { 481 struct ip_mreq s; 482 char addrbuf[INET_ADDRSTRLEN]; 483 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 484 if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 485 rb_str_cat2(ret, " invalid-address"); 486 else 487 rb_str_catf(ret, " %s", addrbuf); 488 if (inet_ntop(AF_INET, &s.imr_interface, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 489 rb_str_catf(ret, " invalid-address"); 490 else 491 rb_str_catf(ret, " %s", addrbuf); 492 return 1; 493 } 494 else { 495 return 0; 496 } 497} 498#endif 499 500#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* GNU/Linux, FreeBSD 7 */ 501static int 502inspect_ipv4_mreqn(int level, int optname, VALUE data, VALUE ret) 503{ 504 if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) { 505 struct ip_mreqn s; 506 char addrbuf[INET_ADDRSTRLEN], ifbuf[32+IFNAMSIZ]; 507 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 508 if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 509 rb_str_cat2(ret, " invalid-address"); 510 else 511 rb_str_catf(ret, " %s", addrbuf); 512 if (inet_ntop(AF_INET, &s.imr_address, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 513 rb_str_catf(ret, " invalid-address"); 514 else 515 rb_str_catf(ret, " %s", addrbuf); 516 rb_if_indextoname(" ", " ifindex:", s.imr_ifindex, ifbuf, sizeof(ifbuf)); 517 rb_str_cat2(ret, ifbuf); 518 return 1; 519 } 520 else { 521 return 0; 522 } 523} 524#endif 525 526#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */ 527static int 528inspect_ipv4_add_drop_membership(int level, int optname, VALUE data, VALUE ret) 529{ 530 if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) 531 return inspect_ipv4_mreq(level, optname, data, ret); 532# if defined(HAVE_TYPE_STRUCT_IP_MREQN) 533 else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) 534 return inspect_ipv4_mreqn(level, optname, data, ret); 535# endif 536 else 537 return 0; 538} 539#endif 540 541#if defined(IPPROTO_IP) && defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */ 542static int 543inspect_ipv4_multicast_if(int level, int optname, VALUE data, VALUE ret) 544{ 545 if (RSTRING_LEN(data) == sizeof(struct in_addr)) { 546 struct in_addr s; 547 char addrbuf[INET_ADDRSTRLEN]; 548 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 549 if (inet_ntop(AF_INET, &s, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 550 rb_str_cat2(ret, " invalid-address"); 551 else 552 rb_str_catf(ret, " %s", addrbuf); 553 return 1; 554 } 555 else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) { 556 return inspect_ipv4_mreqn(level, optname, data, ret); 557 } 558 else { 559 return 0; 560 } 561} 562#endif 563 564#if defined(IPV6_MULTICAST_IF) /* POSIX, RFC 3493 */ 565static int 566inspect_ipv6_multicast_if(int level, int optname, VALUE data, VALUE ret) 567{ 568 if (RSTRING_LEN(data) == sizeof(int)) { 569 char ifbuf[32+IFNAMSIZ]; 570 unsigned int ifindex; 571 memcpy((char*)&ifindex, RSTRING_PTR(data), sizeof(unsigned int)); 572 rb_if_indextoname(" ", " ", ifindex, ifbuf, sizeof(ifbuf)); 573 rb_str_cat2(ret, ifbuf); 574 return 1; 575 } 576 else { 577 return 0; 578 } 579} 580#endif 581 582#if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */ 583static int 584inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret) 585{ 586 if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) { 587 struct ipv6_mreq s; 588 char addrbuf[INET6_ADDRSTRLEN], ifbuf[32+IFNAMSIZ]; 589 memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); 590 if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) 591 rb_str_cat2(ret, " invalid-address"); 592 else 593 rb_str_catf(ret, " %s", addrbuf); 594 rb_if_indextoname(" ", " interface:", s.ipv6mr_interface, ifbuf, sizeof(ifbuf)); 595 rb_str_cat2(ret, ifbuf); 596 return 1; 597 } 598 else { 599 return 0; 600 } 601} 602#endif 603 604#if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */ 605#if defined(__OpenBSD__) 606#define RUBY_SOCK_PEERCRED struct sockpeercred 607#else 608#define RUBY_SOCK_PEERCRED struct ucred 609#endif 610static int 611inspect_peercred(int level, int optname, VALUE data, VALUE ret) 612{ 613 if (RSTRING_LEN(data) == sizeof(RUBY_SOCK_PEERCRED)) { 614 RUBY_SOCK_PEERCRED cred; 615 memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED)); 616 rb_str_catf(ret, " pid=%u euid=%u egid=%u", 617 (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid); 618 rb_str_cat2(ret, " (ucred)"); 619 return 1; 620 } 621 else { 622 return 0; 623 } 624} 625#endif 626 627#if defined(LOCAL_PEERCRED) /* FreeBSD, MacOS X */ 628static int 629inspect_local_peercred(int level, int optname, VALUE data, VALUE ret) 630{ 631 if (RSTRING_LEN(data) == sizeof(struct xucred)) { 632 struct xucred cred; 633 memcpy(&cred, RSTRING_PTR(data), sizeof(struct xucred)); 634 if (cred.cr_version != XUCRED_VERSION) 635 return 0; 636 rb_str_catf(ret, " version=%u", cred.cr_version); 637 rb_str_catf(ret, " euid=%u", cred.cr_uid); 638 if (cred.cr_ngroups) { 639 int i; 640 const char *sep = " groups="; 641 for (i = 0; i < cred.cr_ngroups; i++) { 642 rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]); 643 sep = ","; 644 } 645 } 646 rb_str_cat2(ret, " (xucred)"); 647 return 1; 648 } 649 else { 650 return 0; 651 } 652} 653#endif 654 655 656/* 657 * call-seq: 658 * sockopt.inspect => string 659 * 660 * Returns a string which shows sockopt in human-readable form. 661 * 662 * p Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")).inspect 663 * #=> "#<Socket::Option: INET SOCKET KEEPALIVE 1>" 664 * 665 */ 666static VALUE 667sockopt_inspect(VALUE self) 668{ 669 int family = NUM2INT(sockopt_family_m(self)); 670 int level = NUM2INT(sockopt_level_m(self)); 671 int optname = NUM2INT(sockopt_optname_m(self)); 672 VALUE data = sockopt_data(self); 673 VALUE v, ret; 674 ID family_id, level_id, optname_id; 675 int inspected; 676 677 StringValue(data); 678 679 ret = rb_sprintf("#<%s:", rb_obj_classname(self)); 680 681 family_id = rsock_intern_family_noprefix(family); 682 if (family_id) 683 rb_str_catf(ret, " %s", rb_id2name(family_id)); 684 else 685 rb_str_catf(ret, " family:%d", family); 686 687 if (level == SOL_SOCKET) { 688 rb_str_cat2(ret, " SOCKET"); 689 690 optname_id = rsock_intern_so_optname(optname); 691 if (optname_id) 692 rb_str_catf(ret, " %s", rb_id2name(optname_id)); 693 else 694 rb_str_catf(ret, " optname:%d", optname); 695 } 696#ifdef HAVE_SYS_UN_H 697 else if (family == AF_UNIX) { 698 rb_str_catf(ret, " level:%d", level); 699 700 optname_id = rsock_intern_local_optname(optname); 701 if (optname_id) 702 rb_str_catf(ret, " %s", rb_id2name(optname_id)); 703 else 704 rb_str_catf(ret, " optname:%d", optname); 705 } 706#endif 707 else if (IS_IP_FAMILY(family)) { 708 level_id = rsock_intern_iplevel(level); 709 if (level_id) 710 rb_str_catf(ret, " %s", rb_id2name(level_id)); 711 else 712 rb_str_catf(ret, " level:%d", level); 713 714 v = optname_to_sym(level, optname); 715 if (SYMBOL_P(v)) 716 rb_str_catf(ret, " %s", rb_id2name(SYM2ID(v))); 717 else 718 rb_str_catf(ret, " optname:%d", optname); 719 } 720 else { 721 rb_str_catf(ret, " level:%d", level); 722 rb_str_catf(ret, " optname:%d", optname); 723 } 724 725 inspected = 0; 726 727 if (level == SOL_SOCKET) 728 family = AF_UNSPEC; 729 switch (family) { 730 case AF_UNSPEC: 731 switch (level) { 732 case SOL_SOCKET: 733 switch (optname) { 734# if defined(SO_DEBUG) /* POSIX */ 735 case SO_DEBUG: inspected = inspect_int(level, optname, data, ret); break; 736# endif 737# if defined(SO_ERROR) /* POSIX */ 738 case SO_ERROR: inspected = inspect_errno(level, optname, data, ret); break; 739# endif 740# if defined(SO_TYPE) /* POSIX */ 741 case SO_TYPE: inspected = inspect_socktype(level, optname, data, ret); break; 742# endif 743# if defined(SO_ACCEPTCONN) /* POSIX */ 744 case SO_ACCEPTCONN: inspected = inspect_int(level, optname, data, ret); break; 745# endif 746# if defined(SO_BROADCAST) /* POSIX */ 747 case SO_BROADCAST: inspected = inspect_int(level, optname, data, ret); break; 748# endif 749# if defined(SO_REUSEADDR) /* POSIX */ 750 case SO_REUSEADDR: inspected = inspect_int(level, optname, data, ret); break; 751# endif 752# if defined(SO_KEEPALIVE) /* POSIX */ 753 case SO_KEEPALIVE: inspected = inspect_int(level, optname, data, ret); break; 754# endif 755# if defined(SO_OOBINLINE) /* POSIX */ 756 case SO_OOBINLINE: inspected = inspect_int(level, optname, data, ret); break; 757# endif 758# if defined(SO_SNDBUF) /* POSIX */ 759 case SO_SNDBUF: inspected = inspect_int(level, optname, data, ret); break; 760# endif 761# if defined(SO_RCVBUF) /* POSIX */ 762 case SO_RCVBUF: inspected = inspect_int(level, optname, data, ret); break; 763# endif 764# if defined(SO_DONTROUTE) /* POSIX */ 765 case SO_DONTROUTE: inspected = inspect_int(level, optname, data, ret); break; 766# endif 767# if defined(SO_RCVLOWAT) /* POSIX */ 768 case SO_RCVLOWAT: inspected = inspect_int(level, optname, data, ret); break; 769# endif 770# if defined(SO_SNDLOWAT) /* POSIX */ 771 case SO_SNDLOWAT: inspected = inspect_int(level, optname, data, ret); break; 772# endif 773# if defined(SO_LINGER) /* POSIX */ 774 case SO_LINGER: inspected = inspect_linger(level, optname, data, ret); break; 775# endif 776# if defined(SO_RCVTIMEO) /* POSIX */ 777 case SO_RCVTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break; 778# endif 779# if defined(SO_SNDTIMEO) /* POSIX */ 780 case SO_SNDTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break; 781# endif 782# if defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */ 783 case SO_PEERCRED: inspected = inspect_peercred(level, optname, data, ret); break; 784# endif 785 } 786 break; 787 } 788 break; 789 790 case AF_INET: 791#ifdef INET6 792 case AF_INET6: 793#endif 794 switch (level) { 795# if defined(IPPROTO_IP) 796 case IPPROTO_IP: 797 switch (optname) { 798# if defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */ 799 case IP_MULTICAST_IF: inspected = inspect_ipv4_multicast_if(level, optname, data, ret); break; 800# endif 801# if defined(IP_ADD_MEMBERSHIP) /* 4.4BSD, GNU/Linux */ 802 case IP_ADD_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break; 803# endif 804# if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */ 805 case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break; 806# endif 807 } 808 break; 809# endif 810 811# if defined(IPPROTO_IPV6) 812 case IPPROTO_IPV6: 813 switch (optname) { 814# if defined(IPV6_MULTICAST_HOPS) /* POSIX */ 815 case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break; 816# endif 817# if defined(IPV6_MULTICAST_IF) /* POSIX */ 818 case IPV6_MULTICAST_IF: inspected = inspect_ipv6_multicast_if(level, optname, data, ret); break; 819# endif 820# if defined(IPV6_MULTICAST_LOOP) /* POSIX */ 821 case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break; 822# endif 823# if defined(IPV6_JOIN_GROUP) /* POSIX */ 824 case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break; 825# endif 826# if defined(IPV6_LEAVE_GROUP) /* POSIX */ 827 case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break; 828# endif 829# if defined(IPV6_UNICAST_HOPS) /* POSIX */ 830 case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break; 831# endif 832# if defined(IPV6_V6ONLY) /* POSIX */ 833 case IPV6_V6ONLY: inspected = inspect_int(level, optname, data, ret); break; 834# endif 835 } 836 break; 837# endif 838 839# if defined(IPPROTO_TCP) 840 case IPPROTO_TCP: 841 switch (optname) { 842# if defined(TCP_NODELAY) /* POSIX */ 843 case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break; 844# endif 845 } 846 break; 847# endif 848 } 849 break; 850 851#ifdef HAVE_SYS_UN_H 852 case AF_UNIX: 853 switch (level) { 854 case 0: 855 switch (optname) { 856# if defined(LOCAL_PEERCRED) 857 case LOCAL_PEERCRED: inspected = inspect_local_peercred(level, optname, data, ret); break; 858# endif 859 } 860 break; 861 } 862 break; 863#endif 864 } 865 866 if (!inspected) { 867 rb_str_cat2(ret, " "); 868 rb_str_append(ret, rb_str_dump(data)); 869 } 870 871 rb_str_cat2(ret, ">"); 872 873 return ret; 874} 875 876/* 877 * call-seq: 878 * sockopt.unpack(template) => array 879 * 880 * Calls String#unpack on sockopt.data. 881 * 882 * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")) 883 * p sockopt.unpack("i") #=> [1] 884 * p sockopt.data.unpack("i") #=> [1] 885 */ 886static VALUE 887sockopt_unpack(VALUE self, VALUE template) 888{ 889 return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template); 890} 891 892void 893rsock_init_sockopt(void) 894{ 895 /* 896 * Document-class: Socket::Option 897 * 898 * Socket::Option represents a socket option used by 899 * BasicSocket#getsockopt and BasicSocket#setsockopt. A socket option 900 * contains the socket #family, protocol #level, option name #optname and 901 * option value #data. 902 */ 903 rb_cSockOpt = rb_define_class_under(rb_cSocket, "Option", rb_cObject); 904 rb_define_method(rb_cSockOpt, "initialize", sockopt_initialize, 4); 905 rb_define_method(rb_cSockOpt, "family", sockopt_family_m, 0); 906 rb_define_method(rb_cSockOpt, "level", sockopt_level_m, 0); 907 rb_define_method(rb_cSockOpt, "optname", sockopt_optname_m, 0); 908 rb_define_method(rb_cSockOpt, "data", sockopt_data, 0); 909 rb_define_method(rb_cSockOpt, "inspect", sockopt_inspect, 0); 910 911 rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4); 912 rb_define_method(rb_cSockOpt, "int", sockopt_int, 0); 913 914 rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4); 915 rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0); 916 917 rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2); 918 rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0); 919 920 rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1); 921 922 rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */ 923} 924 925