Ruby  2.5.0dev(2017-10-22revision60238)
option.c
Go to the documentation of this file.
1 #include "rubysocket.h"
2 
4 
5 #define pack_var(v) rb_str_new((const char *)&(v), sizeof(v))
6 
7 #define CAT(x,y) x##y
8 #define XCAT(x,y) CAT(x,y)
9 
10 #if defined(__linux__) || \
11  defined(__GNU__) /* GNU/Hurd */ || \
12  defined(__FreeBSD__) || \
13  defined(__DragonFly__) || \
14  defined(__APPLE__) || \
15  defined(_WIN32) || \
16  defined(__CYGWIN__)
17 # define TYPE_IP_MULTICAST_LOOP int
18 # define TYPE_IP_MULTICAST_TTL int
19 #else
20 /* The original IP multicast implementation by Steve Deering
21  * NetBSD
22  * OpenBSD
23  * SunOS
24  */
25 # define TYPE_IP_MULTICAST_LOOP byte
26 # define TYPE_IP_MULTICAST_TTL byte
27 # define USE_INSPECT_BYTE 1
28 #endif
29 
30 #define check_size(len, size) \
31  ((len) == (size) ? \
32  (void)0 : \
33  rb_raise(rb_eTypeError, "size differ. expected as "#size"=%d but %ld", \
34  (int)size, (long)(len)))
35 
36 static VALUE
37 sockopt_pack_byte(VALUE value)
38 {
39  char i = NUM2CHR(rb_to_int(value));
40  return pack_var(i);
41 }
42 
43 static VALUE
44 sockopt_pack_int(VALUE value)
45 {
46  int i = NUM2INT(rb_to_int(value));
47  return pack_var(i);
48 }
49 
50 static VALUE
51 constant_to_sym(int constant, ID (*intern_const)(int))
52 {
53  ID name = intern_const(constant);
54  if (name) {
55  return ID2SYM(name);
56  }
57 
58  return INT2NUM(constant);
59 }
60 
61 static VALUE
62 optname_to_sym(int level, int optname)
63 {
64  switch (level) {
65  case SOL_SOCKET:
66  return constant_to_sym(optname, rsock_intern_so_optname);
67  case IPPROTO_IP:
68  return constant_to_sym(optname, rsock_intern_ip_optname);
69 #ifdef IPPROTO_IPV6
70  case IPPROTO_IPV6:
71  return constant_to_sym(optname, rsock_intern_ipv6_optname);
72 #endif
73  case IPPROTO_TCP:
74  return constant_to_sym(optname, rsock_intern_tcp_optname);
75  case IPPROTO_UDP:
76  return constant_to_sym(optname, rsock_intern_udp_optname);
77  default:
78  return INT2NUM(optname);
79  }
80 }
81 
82 /*
83  * call-seq:
84  * Socket::Option.new(family, level, optname, data) => sockopt
85  *
86  * Returns a new Socket::Option object.
87  *
88  * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
89  * p sockopt #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
90  *
91  */
92 static VALUE
93 sockopt_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE data)
94 {
95  int family = rsock_family_arg(vfamily);
96  int level = rsock_level_arg(family, vlevel);
97  int optname = rsock_optname_arg(family, level, voptname);
98  StringValue(data);
99  rb_ivar_set(self, rb_intern("family"), INT2NUM(family));
100  rb_ivar_set(self, rb_intern("level"), INT2NUM(level));
101  rb_ivar_set(self, rb_intern("optname"), INT2NUM(optname));
102  rb_ivar_set(self, rb_intern("data"), data);
103  return self;
104 }
105 
106 VALUE
107 rsock_sockopt_new(int family, int level, int optname, VALUE data)
108 {
109  NEWOBJ_OF(obj, struct RObject, rb_cSockOpt, T_OBJECT);
110  StringValue(data);
111  sockopt_initialize((VALUE)obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data);
112  return (VALUE)obj;
113 }
114 
115 /*
116  * call-seq:
117  * sockopt.family => integer
118  *
119  * returns the socket family as an integer.
120  *
121  * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).family
122  * #=> 10
123  */
124 static VALUE
125 sockopt_family_m(VALUE self)
126 {
127  return rb_attr_get(self, rb_intern("family"));
128 }
129 
130 static int
131 sockopt_level(VALUE self)
132 {
133  return NUM2INT(rb_attr_get(self, rb_intern("level")));
134 }
135 
136 /*
137  * call-seq:
138  * sockopt.level => integer
139  *
140  * returns the socket level as an integer.
141  *
142  * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).level
143  * #=> 41
144  */
145 static VALUE
146 sockopt_level_m(VALUE self)
147 {
148  return INT2NUM(sockopt_level(self));
149 }
150 
151 static int
152 sockopt_optname(VALUE self)
153 {
154  return NUM2INT(rb_attr_get(self, rb_intern("optname")));
155 }
156 
157 /*
158  * call-seq:
159  * sockopt.optname => integer
160  *
161  * returns the socket option name as an integer.
162  *
163  * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).optname
164  * #=> 2
165  */
166 static VALUE
167 sockopt_optname_m(VALUE self)
168 {
169  return INT2NUM(sockopt_optname(self));
170 }
171 
172 /*
173  * call-seq:
174  * sockopt.data => string
175  * sockopt.to_s => string
176  *
177  * returns the socket option data as a string.
178  *
179  * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).data
180  * #=> "\x01\x00\x00\x00"
181  */
182 static VALUE
183 sockopt_data(VALUE self)
184 {
185  VALUE v = rb_attr_get(self, rb_intern("data"));
186  StringValue(v);
187  return v;
188 }
189 
190 /*
191  * call-seq:
192  * Socket::Option.byte(family, level, optname, integer) => sockopt
193  *
194  * Creates a new Socket::Option object which contains a byte as data.
195  *
196  * p Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1)
197  * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
198  */
199 static VALUE
200 sockopt_s_byte(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint)
201 {
202  int family = rsock_family_arg(vfamily);
203  int level = rsock_level_arg(family, vlevel);
204  int optname = rsock_optname_arg(family, level, voptname);
205  return rsock_sockopt_new(family, level, optname, sockopt_pack_byte(vint));
206 }
207 
208 /*
209  * call-seq:
210  * sockopt.byte => integer
211  *
212  * Returns the data in _sockopt_ as an byte.
213  *
214  * sockopt = Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1)
215  * p sockopt.byte => 1
216  */
217 static VALUE
218 sockopt_byte(VALUE self)
219 {
220  VALUE data = sockopt_data(self);
221  StringValue(data);
222  check_size(RSTRING_LEN(data), sizeof(char));
223  return CHR2FIX(*RSTRING_PTR(data));
224 }
225 
226 /*
227  * call-seq:
228  * Socket::Option.int(family, level, optname, integer) => sockopt
229  *
230  * Creates a new Socket::Option object which contains an int as data.
231  *
232  * The size and endian is dependent on the platform.
233  *
234  * p Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
235  * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
236  */
237 static VALUE
238 sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint)
239 {
240  int family = rsock_family_arg(vfamily);
241  int level = rsock_level_arg(family, vlevel);
242  int optname = rsock_optname_arg(family, level, voptname);
243  return rsock_sockopt_new(family, level, optname, sockopt_pack_int(vint));
244 }
245 
246 /*
247  * call-seq:
248  * sockopt.int => integer
249  *
250  * Returns the data in _sockopt_ as an int.
251  *
252  * The size and endian is dependent on the platform.
253  *
254  * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
255  * p sockopt.int => 1
256  */
257 static VALUE
258 sockopt_int(VALUE self)
259 {
260  int i;
261  VALUE data = sockopt_data(self);
262  StringValue(data);
263  check_size(RSTRING_LEN(data), sizeof(int));
264  memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
265  return INT2NUM(i);
266 }
267 
268 /*
269  * call-seq:
270  * Socket::Option.bool(family, level, optname, bool) => sockopt
271  *
272  * Creates a new Socket::Option object which contains boolean as data.
273  * Actually 0 or 1 as int is used.
274  *
275  * require 'socket'
276  *
277  * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
278  * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1>
279  *
280  * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false)
281  * #=> #<Socket::Option: AF_INET SOCKET KEEPALIVE 0>
282  *
283  */
284 static VALUE
285 sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vbool)
286 {
287  int family = rsock_family_arg(vfamily);
288  int level = rsock_level_arg(family, vlevel);
289  int optname = rsock_optname_arg(family, level, voptname);
290  int i = RTEST(vbool) ? 1 : 0;
291  return rsock_sockopt_new(family, level, optname, pack_var(i));
292 }
293 
294 /*
295  * call-seq:
296  * sockopt.bool => true or false
297  *
298  * Returns the data in _sockopt_ as an boolean value.
299  *
300  * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
301  * p sockopt.bool => true
302  */
303 static VALUE
304 sockopt_bool(VALUE self)
305 {
306  int i;
307  long len;
308  VALUE data = sockopt_data(self);
309  StringValue(data);
310  len = RSTRING_LEN(data);
311  if (len == 1) {
312  return *RSTRING_PTR(data) == 0 ? Qfalse : Qtrue;
313  }
314  check_size(len, sizeof(int));
315  memcpy((char*)&i, RSTRING_PTR(data), len);
316  return i == 0 ? Qfalse : Qtrue;
317 }
318 
319 /*
320  * call-seq:
321  * Socket::Option.linger(onoff, secs) => sockopt
322  *
323  * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER.
324  *
325  * _onoff_ should be an integer or a boolean.
326  *
327  * _secs_ should be the number of seconds.
328  *
329  * p Socket::Option.linger(true, 10)
330  * #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec>
331  *
332  */
333 static VALUE
334 sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs)
335 {
336  VALUE tmp;
337  struct linger l;
338  memset(&l, 0, sizeof(l));
339  if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int")))
340  l.l_onoff = NUM2INT(tmp);
341  else
342  l.l_onoff = RTEST(vonoff) ? 1 : 0;
343  l.l_linger = NUM2INT(vsecs);
344  return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, pack_var(l));
345 }
346 
347 /*
348  * call-seq:
349  * sockopt.linger => [bool, seconds]
350  *
351  * Returns the linger data in _sockopt_ as a pair of boolean and integer.
352  *
353  * sockopt = Socket::Option.linger(true, 10)
354  * p sockopt.linger => [true, 10]
355  */
356 static VALUE
357 sockopt_linger(VALUE self)
358 {
359  int level = sockopt_level(self);
360  int optname = sockopt_optname(self);
361  VALUE data = sockopt_data(self);
362  struct linger l;
363  VALUE vonoff, vsecs;
364 
365  if (level != SOL_SOCKET || optname != SO_LINGER)
366  rb_raise(rb_eTypeError, "linger socket option expected");
367  check_size(RSTRING_LEN(data), sizeof(struct linger));
368  memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger));
369  switch (l.l_onoff) {
370  case 0: vonoff = Qfalse; break;
371  case 1: vonoff = Qtrue; break;
372  default: vonoff = INT2NUM(l.l_onoff); break;
373  }
374  vsecs = INT2NUM(l.l_linger);
375  return rb_assoc_new(vonoff, vsecs);
376 }
377 
378 /*
379  * call-seq:
380  * Socket::Option.ipv4_multicast_loop(integer) => sockopt
381  *
382  * Creates a new Socket::Option object for IP_MULTICAST_LOOP.
383  *
384  * The size is dependent on the platform.
385  *
386  * sockopt = Socket::Option.int(:INET, :IPPROTO_IP, :IP_MULTICAST_LOOP, 1)
387  * p sockopt.int => 1
388  *
389  * p Socket::Option.ipv4_multicast_loop(10)
390  * #=> #<Socket::Option: INET IP MULTICAST_LOOP 10>
391  *
392  */
393 static VALUE
394 sockopt_s_ipv4_multicast_loop(VALUE klass, VALUE value)
395 {
396 
397 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP)
398  VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_LOOP)(value);
399  return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_LOOP, o);
400 #else
401 # error IPPROTO_IP or IP_MULTICAST_LOOP is not implemented
402 #endif
403 }
404 
405 /*
406  * call-seq:
407  * sockopt.ipv4_multicast_loop => integer
408  *
409  * Returns the ipv4_multicast_loop data in _sockopt_ as an integer.
410  *
411  * sockopt = Socket::Option.ipv4_multicast_loop(10)
412  * p sockopt.ipv4_multicast_loop => 10
413  */
414 static VALUE
415 sockopt_ipv4_multicast_loop(VALUE self)
416 {
417  int family = NUM2INT(sockopt_family_m(self));
418  int level = sockopt_level(self);
419  int optname = sockopt_optname(self);
420 
421 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP)
422  if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_LOOP) {
423  return XCAT(sockopt_,TYPE_IP_MULTICAST_LOOP)(self);
424  }
425 #endif
426  rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected");
427  UNREACHABLE;
428 }
429 
430 #define inspect_ipv4_multicast_loop(a,b,c,d) \
431  XCAT(inspect_,TYPE_IP_MULTICAST_LOOP)(a,b,c,d)
432 
433 /*
434  * call-seq:
435  * Socket::Option.ipv4_multicast_ttl(integer) => sockopt
436  *
437  * Creates a new Socket::Option object for IP_MULTICAST_TTL.
438  *
439  * The size is dependent on the platform.
440  *
441  * p Socket::Option.ipv4_multicast_ttl(10)
442  * #=> #<Socket::Option: INET IP MULTICAST_TTL 10>
443  *
444  */
445 static VALUE
446 sockopt_s_ipv4_multicast_ttl(VALUE klass, VALUE value)
447 {
448 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL)
449  VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_TTL)(value);
450  return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_TTL, o);
451 #else
452 # error IPPROTO_IP or IP_MULTICAST_TTL is not implemented
453 #endif
454 }
455 
456 /*
457  * call-seq:
458  * sockopt.ipv4_multicast_ttl => integer
459  *
460  * Returns the ipv4_multicast_ttl data in _sockopt_ as an integer.
461  *
462  * sockopt = Socket::Option.ipv4_multicast_ttl(10)
463  * p sockopt.ipv4_multicast_ttl => 10
464  */
465 static VALUE
466 sockopt_ipv4_multicast_ttl(VALUE self)
467 {
468  int family = NUM2INT(sockopt_family_m(self));
469  int level = sockopt_level(self);
470  int optname = sockopt_optname(self);
471 
472 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL)
473  if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_TTL) {
474  return XCAT(sockopt_,TYPE_IP_MULTICAST_TTL)(self);
475  }
476 #endif
477  rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected");
478  UNREACHABLE;
479 }
480 
481 #define inspect_ipv4_multicast_ttl(a,b,c,d) \
482  XCAT(inspect_,TYPE_IP_MULTICAST_TTL)(a,b,c,d)
483 
484 static int
485 inspect_int(int level, int optname, VALUE data, VALUE ret)
486 {
487  if (RSTRING_LEN(data) == sizeof(int)) {
488  int i;
489  memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
490  rb_str_catf(ret, " %d", i);
491  return 1;
492  }
493  else {
494  return 0;
495  }
496 }
497 
498 #ifdef USE_INSPECT_BYTE
499 static int
500 inspect_byte(int level, int optname, VALUE data, VALUE ret)
501 {
502  if (RSTRING_LEN(data) == sizeof(unsigned char)) {
503  rb_str_catf(ret, " %d", (unsigned char)*RSTRING_PTR(data));
504  return 1;
505  }
506  else {
507  return 0;
508  }
509 }
510 #endif
511 
512 static int
513 inspect_errno(int level, int optname, VALUE data, VALUE ret)
514 {
515  if (RSTRING_LEN(data) == sizeof(int)) {
516  int i;
517  char *err;
518  memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
519  err = strerror(i);
520  rb_str_catf(ret, " %s (%d)", err, i);
521  return 1;
522  }
523  else {
524  return 0;
525  }
526 }
527 
528 #if defined(IPV6_MULTICAST_LOOP)
529 static int
530 inspect_uint(int level, int optname, VALUE data, VALUE ret)
531 {
532  if (RSTRING_LEN(data) == sizeof(int)) {
533  unsigned int i;
534  memcpy((char*)&i, RSTRING_PTR(data), sizeof(unsigned int));
535  rb_str_catf(ret, " %u", i);
536  return 1;
537  }
538  else {
539  return 0;
540  }
541 }
542 #endif
543 
544 #if defined(SOL_SOCKET) && defined(SO_LINGER) /* POSIX */
545 static int
546 inspect_linger(int level, int optname, VALUE data, VALUE ret)
547 {
548  if (RSTRING_LEN(data) == sizeof(struct linger)) {
549  struct linger s;
550  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
551  switch (s.l_onoff) {
552  case 0: rb_str_cat2(ret, " off"); break;
553  case 1: rb_str_cat2(ret, " on"); break;
554  default: rb_str_catf(ret, " on(%d)", s.l_onoff); break;
555  }
556  rb_str_catf(ret, " %dsec", s.l_linger);
557  return 1;
558  }
559  else {
560  return 0;
561  }
562 }
563 #endif
564 
565 #if defined(SOL_SOCKET) && defined(SO_TYPE) /* POSIX */
566 static int
567 inspect_socktype(int level, int optname, VALUE data, VALUE ret)
568 {
569  if (RSTRING_LEN(data) == sizeof(int)) {
570  int i;
571  ID id;
572  memcpy((char*)&i, RSTRING_PTR(data), sizeof(int));
573  id = rsock_intern_socktype(i);
574  if (id)
575  rb_str_catf(ret, " %s", rb_id2name(id));
576  else
577  rb_str_catf(ret, " %d", i);
578  return 1;
579  }
580  else {
581  return 0;
582  }
583 }
584 #endif
585 
586 static int
587 inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret)
588 {
589  if (RSTRING_LEN(data) == sizeof(struct timeval)) {
590  struct timeval s;
591  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
592  rb_str_catf(ret, " %ld.%06ldsec", (long)s.tv_sec, (long)s.tv_usec);
593  return 1;
594  }
595  else {
596  return 0;
597  }
598 }
599 
600 /*
601  * socket option for IPv4 multicast is bit confusing.
602  *
603  * IP Multicast is implemented by Steve Deering at first:
604  * IP Multicast Extensions for 4.3BSD UNIX and related systems
605  * (MULTICAST 1.2 Release)
606  * http://www.kohala.com/start/mcast.api.txt
607  *
608  * There are 2 socket options which takes a u_char (unsigned char).
609  *
610  * IP_MULTICAST_TTL
611  * IP_MULTICAST_LOOP
612  *
613  * However Linux and FreeBSD setsockname accepts int argument
614  * as well as u_char.
615  * Their getsockname returns int.
616  *
617  * There are 3 socket options which takes a struct.
618  *
619  * IP_MULTICAST_IF: struct in_addr
620  * IP_ADD_MEMBERSHIP: struct ip_mreq
621  * IP_DROP_MEMBERSHIP: struct ip_mreq
622  *
623  * But they uses an IP address to specify an interface.
624  * This means the API cannot specify an unnumbered interface.
625  *
626  * Linux 2.4 introduces struct ip_mreqn to fix this problem.
627  * struct ip_mreqn has imr_ifindex field to specify interface index.
628  *
629  * IP_MULTICAST_IF: struct ip_mreqn
630  * IP_ADD_MEMBERSHIP: struct ip_mreqn
631  * IP_DROP_MEMBERSHIP: struct ip_mreqn
632  *
633  * FreeBSD 7 obtained struct ip_mreqn for IP_MULTICAST_IF.
634  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/netinet/in.h.diff?r1=1.99;r2=1.100
635  *
636  * Another hackish workaround is "RFC 1724 hack".
637  * RFC 1724 section 3.3 suggests unnumbered interfaces
638  * specified by pseudo address 0.0.0.0/8.
639  * NetBSD 4 and FreeBSD 5 documented it.
640  * http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.16&r2=1.17
641  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.37;r2=1.38
642  * FreeBSD 7.0 removed it.
643  * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.49;r2=1.50
644  *
645  * RFC 1724 hack is not supported by Socket::Option#inspect because
646  * it is not distinguishable by the size.
647  */
648 
649 #if !defined HAVE_INET_NTOP && ! defined _WIN32
650 const char *
651 inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
652 {
653 #ifdef HAVE_INET_NTOA
654  struct in_addr in;
655  memcpy(&in.s_addr, addr, sizeof(in.s_addr));
656  snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in));
657 #else
658  unsigned long x = ntohl(*(unsigned long*)addr);
659  snprintf(numaddr, numaddr_len, "%d.%d.%d.%d",
660  (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
661  (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
662 #endif
663  return numaddr;
664 }
665 #endif
666 
667 /* Although the buffer size needed depends on the prefixes, "%u" may generate "4294967295". */
668 static int
669 rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int ifindex, char *buf, size_t len)
670 {
671 #if defined(HAVE_IF_INDEXTONAME)
672  char ifbuf[IFNAMSIZ];
673  if (if_indextoname(ifindex, ifbuf) == NULL)
674  return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
675  else
676  return snprintf(buf, len, "%s%s", succ_prefix, ifbuf);
677 #else
678 # ifndef IFNAMSIZ
679 # define IFNAMSIZ (sizeof(unsigned int)*3+1)
680 # endif
681  return snprintf(buf, len, "%s%u", fail_prefix, ifindex);
682 #endif
683 }
684 
685 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
686 static int
687 inspect_ipv4_mreq(int level, int optname, VALUE data, VALUE ret)
688 {
689  if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) {
690  struct ip_mreq s;
691  char addrbuf[INET_ADDRSTRLEN];
692  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
693  if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
694  rb_str_cat2(ret, " invalid-address");
695  else
696  rb_str_catf(ret, " %s", addrbuf);
697  if (inet_ntop(AF_INET, &s.imr_interface, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
698  rb_str_catf(ret, " invalid-address");
699  else
700  rb_str_catf(ret, " %s", addrbuf);
701  return 1;
702  }
703  else {
704  return 0;
705  }
706 }
707 #endif
708 
709 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* GNU/Linux, FreeBSD 7 */
710 static int
711 inspect_ipv4_mreqn(int level, int optname, VALUE data, VALUE ret)
712 {
713  if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
714  struct ip_mreqn s;
715  char addrbuf[INET_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
716  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
717  if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
718  rb_str_cat2(ret, " invalid-address");
719  else
720  rb_str_catf(ret, " %s", addrbuf);
721  if (inet_ntop(AF_INET, &s.imr_address, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
722  rb_str_catf(ret, " invalid-address");
723  else
724  rb_str_catf(ret, " %s", addrbuf);
725  rb_if_indextoname(" ", " ifindex:", s.imr_ifindex, ifbuf, sizeof(ifbuf));
726  rb_str_cat2(ret, ifbuf);
727  return 1;
728  }
729  else {
730  return 0;
731  }
732 }
733 #endif
734 
735 #if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */
736 static int
737 inspect_ipv4_add_drop_membership(int level, int optname, VALUE data, VALUE ret)
738 {
739  if (RSTRING_LEN(data) == sizeof(struct ip_mreq))
740  return inspect_ipv4_mreq(level, optname, data, ret);
741 # if defined(HAVE_TYPE_STRUCT_IP_MREQN)
742  else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn))
743  return inspect_ipv4_mreqn(level, optname, data, ret);
744 # endif
745  else
746  return 0;
747 }
748 #endif
749 
750 #if defined(IPPROTO_IP) && defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
751 static int
752 inspect_ipv4_multicast_if(int level, int optname, VALUE data, VALUE ret)
753 {
754  if (RSTRING_LEN(data) == sizeof(struct in_addr)) {
755  struct in_addr s;
756  char addrbuf[INET_ADDRSTRLEN];
757  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
758  if (inet_ntop(AF_INET, &s, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
759  rb_str_cat2(ret, " invalid-address");
760  else
761  rb_str_catf(ret, " %s", addrbuf);
762  return 1;
763  }
764  else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) {
765  return inspect_ipv4_mreqn(level, optname, data, ret);
766  }
767  else {
768  return 0;
769  }
770 }
771 #endif
772 
773 #if defined(IPV6_MULTICAST_IF) /* POSIX, RFC 3493 */
774 static int
775 inspect_ipv6_multicast_if(int level, int optname, VALUE data, VALUE ret)
776 {
777  if (RSTRING_LEN(data) == sizeof(int)) {
778  char ifbuf[32+IFNAMSIZ];
779  unsigned int ifindex;
780  memcpy((char*)&ifindex, RSTRING_PTR(data), sizeof(unsigned int));
781  rb_if_indextoname(" ", " ", ifindex, ifbuf, sizeof(ifbuf));
782  rb_str_cat2(ret, ifbuf);
783  return 1;
784  }
785  else {
786  return 0;
787  }
788 }
789 #endif
790 
791 #if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */
792 static int
793 inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret)
794 {
795  if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) {
796  struct ipv6_mreq s;
797  char addrbuf[INET6_ADDRSTRLEN], ifbuf[32+IFNAMSIZ];
798  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
799  if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL)
800  rb_str_cat2(ret, " invalid-address");
801  else
802  rb_str_catf(ret, " %s", addrbuf);
803  rb_if_indextoname(" ", " interface:", s.ipv6mr_interface, ifbuf, sizeof(ifbuf));
804  rb_str_cat2(ret, ifbuf);
805  return 1;
806  }
807  else {
808  return 0;
809  }
810 }
811 #endif
812 
813 #if defined(IPPROTO_TCP) && defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO)
814 
815 #ifdef __FreeBSD__
816 # ifndef HAVE_CONST_TCP_ESTABLISHED
817 # define TCP_ESTABLISHED TCPS_ESTABLISHED
818 # endif
819 # ifndef HAVE_CONST_TCP_SYN_SENT
820 # define TCP_SYN_SENT TCPS_SYN_SENT
821 # endif
822 # ifndef HAVE_CONST_TCP_SYN_RECV
823 # define TCP_SYN_RECV TCPS_SYN_RECEIVED
824 # endif
825 # ifndef HAVE_CONST_TCP_FIN_WAIT1
826 # define TCP_FIN_WAIT1 TCPS_FIN_WAIT_1
827 # endif
828 # ifndef HAVE_CONST_TCP_FIN_WAIT2
829 # define TCP_FIN_WAIT2 TCPS_FIN_WAIT_2
830 # endif
831 # ifndef HAVE_CONST_TCP_TIME_WAIT
832 # define TCP_TIME_WAIT TCPS_TIME_WAIT
833 # endif
834 # ifndef HAVE_CONST_TCP_CLOSE
835 # define TCP_CLOSE TCPS_CLOSED
836 # endif
837 # ifndef HAVE_CONST_TCP_CLOSE_WAIT
838 # define TCP_CLOSE_WAIT TCPS_CLOSE_WAIT
839 # endif
840 # ifndef HAVE_CONST_TCP_LAST_ACK
841 # define TCP_LAST_ACK TCPS_LAST_ACK
842 # endif
843 # ifndef HAVE_CONST_TCP_LISTEN
844 # define TCP_LISTEN TCPS_LISTEN
845 # endif
846 # ifndef HAVE_CONST_TCP_CLOSING
847 # define TCP_CLOSING TCPS_CLOSING
848 # endif
849 #endif
850 
851 #if defined(HAVE_CONST_TCP_ESTABLISHED) && !defined(TCP_ESTABLISHED)
852 # define TCP_ESTABLISHED TCP_ESTABLISHED
853 #endif
854 #if defined(HAVE_CONST_TCP_SYN_SENT) && !defined(TCP_SYN_SENT)
855 # define TCP_SYN_SENT TCP_SYN_SENT
856 #endif
857 #if defined(HAVE_CONST_TCP_SYN_RECV) && !defined(TCP_SYN_RECV)
858 # define TCP_SYN_RECV TCP_SYN_RECV
859 #endif
860 #if defined(HAVE_CONST_TCP_FIN_WAIT1) && !defined(TCP_FIN_WAIT1)
861 # define TCP_FIN_WAIT1 TCP_FIN_WAIT1
862 #endif
863 #if defined(HAVE_CONST_TCP_FIN_WAIT2) && !defined(TCP_FIN_WAIT2)
864 # define TCP_FIN_WAIT2 TCP_FIN_WAIT2
865 #endif
866 #if defined(HAVE_CONST_TCP_TIME_WAIT) && !defined(TCP_TIME_WAIT)
867 # define TCP_TIME_WAIT TCP_TIME_WAIT
868 #endif
869 #if defined(HAVE_CONST_TCP_CLOSE) && !defined(TCP_CLOSE)
870 # define TCP_CLOSE TCP_CLOSE
871 #endif
872 #if defined(HAVE_CONST_TCP_CLOSE_WAIT) && !defined(TCP_CLOSE_WAIT)
873 # define TCP_CLOSE_WAIT TCP_CLOSE_WAIT
874 #endif
875 #if defined(HAVE_CONST_TCP_LAST_ACK) && !defined(TCP_LAST_ACK)
876 # define TCP_LAST_ACK TCP_LAST_ACK
877 #endif
878 #if defined(HAVE_CONST_TCP_LISTEN) && !defined(TCP_LISTEN)
879 # define TCP_LISTEN TCP_LISTEN
880 #endif
881 #if defined(HAVE_CONST_TCP_CLOSING) && !defined(TCP_CLOSING)
882 # define TCP_CLOSING TCP_CLOSING
883 #endif
884 
885 static void
886 inspect_tcpi_options(VALUE ret, uint8_t options)
887 {
888  int sep = '=';
889 
890  rb_str_cat2(ret, " options");
891 #define INSPECT_TCPI_OPTION(optval, name) \
892  if (options & (optval)) { \
893  options &= ~(uint8_t)(optval); \
894  rb_str_catf(ret, "%c%s", sep, name); \
895  sep = ','; \
896  }
897 #ifdef TCPI_OPT_TIMESTAMPS /* GNU/Linux, FreeBSD */
898  INSPECT_TCPI_OPTION(TCPI_OPT_TIMESTAMPS, "TIMESTAMPS");
899 #endif
900 #ifdef TCPI_OPT_SACK /* GNU/Linux, FreeBSD */
901  INSPECT_TCPI_OPTION(TCPI_OPT_SACK, "SACK");
902 #endif
903 #ifdef TCPI_OPT_WSCALE /* GNU/Linux, FreeBSD */
904  INSPECT_TCPI_OPTION(TCPI_OPT_WSCALE, "WSCALE");
905 #endif
906 #ifdef TCPI_OPT_ECN /* GNU/Linux, FreeBSD */
907  INSPECT_TCPI_OPTION(TCPI_OPT_ECN, "ECN");
908 #endif
909 #ifdef TCPI_OPT_ECN_SEEN /* GNU/Linux */
910  INSPECT_TCPI_OPTION(TCPI_OPT_ECN_SEEN, "ECN_SEEN");
911 #endif
912 #ifdef TCPI_OPT_SYN_DATA /* GNU/Linux */
913  INSPECT_TCPI_OPTION(TCPI_OPT_SYN_DATA, "SYN_DATA");
914 #endif
915 #ifdef TCPI_OPT_TOE /* FreeBSD */
916  INSPECT_TCPI_OPTION(TCPI_OPT_TOE, "TOE");
917 #endif
918 #undef INSPECT_TCPI_OPTION
919 
920  if (options || sep == '=') {
921  rb_str_catf(ret, "%c%u", sep, options);
922  }
923 }
924 
925 static void
926 inspect_tcpi_usec(VALUE ret, const char *prefix, uint32_t t)
927 {
928  rb_str_catf(ret, "%s%u.%06us", prefix, t / 1000000, t % 1000000);
929 }
930 
931 #if !defined __FreeBSD__ && ( \
932  defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT || \
933  defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV || \
934  defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT || \
935  defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV || \
936  0)
937 static void
938 inspect_tcpi_msec(VALUE ret, const char *prefix, uint32_t t)
939 {
940  rb_str_catf(ret, "%s%u.%03us", prefix, t / 1000, t % 1000);
941 }
942 #endif
943 
944 #ifdef __FreeBSD__
945 # define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t)
946 # define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_usec(ret, " last_data_recv=", t)
947 # define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t)
948 # define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t)
949 #else
950 # define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t)
951 # define inspect_tcpi_ato(ret, t) inspect_tcpi_usec(ret, " ato=", t)
952 # define inspect_tcpi_last_data_sent(ret, t) inspect_tcpi_msec(ret, " last_data_sent=", t)
953 # define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_msec(ret, " last_data_recv=", t)
954 # define inspect_tcpi_last_ack_sent(ret, t) inspect_tcpi_msec(ret, " last_ack_sent=", t)
955 # define inspect_tcpi_last_ack_recv(ret, t) inspect_tcpi_msec(ret, " last_ack_recv=", t)
956 # define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t)
957 # define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t)
958 # define inspect_tcpi_rcv_rtt(ret, t) inspect_tcpi_usec(ret, " rcv_rtt=", t)
959 #endif
960 
961 static int
962 inspect_tcp_info(int level, int optname, VALUE data, VALUE ret)
963 {
964  size_t actual_size = RSTRING_LEN(data);
965  if (sizeof(struct tcp_info) <= actual_size) {
966  struct tcp_info s;
967  memcpy((char*)&s, RSTRING_PTR(data), sizeof(s));
968 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_STATE
969  switch (s.tcpi_state) {
970 # ifdef TCP_ESTABLISHED
971  case TCP_ESTABLISHED: rb_str_cat_cstr(ret, " state=ESTABLISHED"); break;
972 # endif
973 # ifdef TCP_SYN_SENT
974  case TCP_SYN_SENT: rb_str_cat_cstr(ret, " state=SYN_SENT"); break;
975 # endif
976 # ifdef TCP_SYN_RECV
977  case TCP_SYN_RECV: rb_str_cat_cstr(ret, " state=SYN_RECV"); break;
978 # endif
979 # ifdef TCP_FIN_WAIT1
980  case TCP_FIN_WAIT1: rb_str_cat_cstr(ret, " state=FIN_WAIT1"); break;
981 # endif
982 # ifdef TCP_FIN_WAIT2
983  case TCP_FIN_WAIT2: rb_str_cat_cstr(ret, " state=FIN_WAIT2"); break;
984 # endif
985 # ifdef TCP_TIME_WAIT
986  case TCP_TIME_WAIT: rb_str_cat_cstr(ret, " state=TIME_WAIT"); break;
987 # endif
988 # ifdef TCP_CLOSE
989  case TCP_CLOSE: rb_str_cat_cstr(ret, " state=CLOSED"); break; /* RFC 793 uses "CLOSED", not "CLOSE" */
990 # endif
991 # ifdef TCP_CLOSE_WAIT
992  case TCP_CLOSE_WAIT: rb_str_cat_cstr(ret, " state=CLOSE_WAIT"); break;
993 # endif
994 # ifdef TCP_LAST_ACK
995  case TCP_LAST_ACK: rb_str_cat_cstr(ret, " state=LAST_ACK"); break;
996 # endif
997 # ifdef TCP_LISTEN
998  case TCP_LISTEN: rb_str_cat_cstr(ret, " state=LISTEN"); break;
999 # endif
1000 # ifdef TCP_CLOSING
1001  case TCP_CLOSING: rb_str_cat_cstr(ret, " state=CLOSING"); break;
1002 # endif
1003  default: rb_str_catf(ret, " state=%u", s.tcpi_state); break;
1004  }
1005 #endif
1006 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_CA_STATE
1007  switch (s.tcpi_ca_state) {
1008  case TCP_CA_Open: rb_str_cat_cstr(ret, " ca_state=Open"); break;
1009  case TCP_CA_Disorder: rb_str_cat_cstr(ret, " ca_state=Disorder"); break;
1010  case TCP_CA_CWR: rb_str_cat_cstr(ret, " ca_state=CWR"); break;
1011  case TCP_CA_Recovery: rb_str_cat_cstr(ret, " ca_state=Recovery"); break;
1012  case TCP_CA_Loss: rb_str_cat_cstr(ret, " ca_state=Loss"); break;
1013  default: rb_str_catf(ret, " ca_state=%u", s.tcpi_ca_state); break;
1014  }
1015 #endif
1016 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANSMITS
1017  rb_str_catf(ret, " retransmits=%u", s.tcpi_retransmits);
1018 #endif
1019 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_PROBES
1020  rb_str_catf(ret, " probes=%u", s.tcpi_probes);
1021 #endif
1022 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_BACKOFF
1023  rb_str_catf(ret, " backoff=%u", s.tcpi_backoff);
1024 #endif
1025 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_OPTIONS
1026  inspect_tcpi_options(ret, s.tcpi_options);
1027 #endif
1028 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WSCALE
1029  rb_str_catf(ret, " snd_wscale=%u", s.tcpi_snd_wscale);
1030 #endif
1031 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_WSCALE
1032  rb_str_catf(ret, " rcv_wscale=%u", s.tcpi_rcv_wscale);
1033 #endif
1034 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTO
1035  inspect_tcpi_rto(ret, s.tcpi_rto);
1036 #endif
1037 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_ATO
1038  inspect_tcpi_ato(ret, s.tcpi_ato);
1039 #endif
1040 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_MSS
1041  rb_str_catf(ret, " snd_mss=%u", s.tcpi_snd_mss);
1042 #endif
1043 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_MSS
1044  rb_str_catf(ret, " rcv_mss=%u", s.tcpi_rcv_mss);
1045 #endif
1046 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_UNACKED
1047  rb_str_catf(ret, " unacked=%u", s.tcpi_unacked);
1048 #endif
1049 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SACKED
1050  rb_str_catf(ret, " sacked=%u", s.tcpi_sacked);
1051 #endif
1052 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_LOST
1053  rb_str_catf(ret, " lost=%u", s.tcpi_lost);
1054 #endif
1055 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANS
1056  rb_str_catf(ret, " retrans=%u", s.tcpi_retrans);
1057 #endif
1058 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_FACKETS
1059  rb_str_catf(ret, " fackets=%u", s.tcpi_fackets);
1060 #endif
1061 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT
1062  inspect_tcpi_last_data_sent(ret, s.tcpi_last_data_sent);
1063 #endif
1064 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT
1065  inspect_tcpi_last_ack_sent(ret, s.tcpi_last_ack_sent);
1066 #endif
1067 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV
1068  inspect_tcpi_last_data_recv(ret, s.tcpi_last_data_recv);
1069 #endif
1070 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV
1071  inspect_tcpi_last_ack_recv(ret, s.tcpi_last_ack_recv);
1072 #endif
1073 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_PMTU
1074  rb_str_catf(ret, " pmtu=%u", s.tcpi_pmtu);
1075 #endif
1076 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SSTHRESH
1077  rb_str_catf(ret, " rcv_ssthresh=%u", s.tcpi_rcv_ssthresh);
1078 #endif
1079 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTT
1080  inspect_tcpi_rtt(ret, s.tcpi_rtt);
1081 #endif
1082 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTTVAR
1083  inspect_tcpi_rttvar(ret, s.tcpi_rttvar);
1084 #endif
1085 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_SSTHRESH
1086  rb_str_catf(ret, " snd_ssthresh=%u", s.tcpi_snd_ssthresh);
1087 #endif
1088 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND
1089  rb_str_catf(ret, " snd_cwnd=%u", s.tcpi_snd_cwnd);
1090 #endif
1091 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_ADVMSS
1092  rb_str_catf(ret, " advmss=%u", s.tcpi_advmss);
1093 #endif
1094 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_REORDERING
1095  rb_str_catf(ret, " reordering=%u", s.tcpi_reordering);
1096 #endif
1097 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_RTT
1098  inspect_tcpi_rcv_rtt(ret, s.tcpi_rcv_rtt);
1099 #endif
1100 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SPACE
1101  rb_str_catf(ret, " rcv_space=%u", s.tcpi_rcv_space);
1102 #endif
1103 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS
1104  rb_str_catf(ret, " total_retrans=%u", s.tcpi_total_retrans);
1105 #endif
1106 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WND
1107  rb_str_catf(ret, " snd_wnd=%u", s.tcpi_snd_wnd); /* FreeBSD */
1108 #endif
1109 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_BWND
1110  rb_str_catf(ret, " snd_bwnd=%u", s.tcpi_snd_bwnd); /* FreeBSD */
1111 #endif
1112 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_NXT
1113  rb_str_catf(ret, " snd_nxt=%u", s.tcpi_snd_nxt); /* FreeBSD */
1114 #endif
1115 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_NXT
1116  rb_str_catf(ret, " rcv_nxt=%u", s.tcpi_rcv_nxt); /* FreeBSD */
1117 #endif
1118 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOE_TID
1119  rb_str_catf(ret, " toe_tid=%u", s.tcpi_toe_tid); /* FreeBSD */
1120 #endif
1121 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_REXMITPACK
1122  rb_str_catf(ret, " snd_rexmitpack=%u", s.tcpi_snd_rexmitpack); /* FreeBSD */
1123 #endif
1124 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_OOOPACK
1125  rb_str_catf(ret, " rcv_ooopack=%u", s.tcpi_rcv_ooopack); /* FreeBSD */
1126 #endif
1127 #ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_ZEROWIN
1128  rb_str_catf(ret, " snd_zerowin=%u", s.tcpi_snd_zerowin); /* FreeBSD */
1129 #endif
1130  if (sizeof(struct tcp_info) < actual_size)
1131  rb_str_catf(ret, " (%u bytes too long)", (unsigned)(actual_size - sizeof(struct tcp_info)));
1132  return 1;
1133  }
1134  else {
1135  return 0;
1136  }
1137 }
1138 #endif
1139 
1140 #if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
1141 #if defined(__OpenBSD__)
1142 #define RUBY_SOCK_PEERCRED struct sockpeercred
1143 #else
1144 #define RUBY_SOCK_PEERCRED struct ucred
1145 #endif
1146 static int
1147 inspect_peercred(int level, int optname, VALUE data, VALUE ret)
1148 {
1149  if (RSTRING_LEN(data) == sizeof(RUBY_SOCK_PEERCRED)) {
1150  RUBY_SOCK_PEERCRED cred;
1151  memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED));
1152  rb_str_catf(ret, " pid=%u euid=%u egid=%u",
1153  (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid);
1154  rb_str_cat2(ret, " (ucred)");
1155  return 1;
1156  }
1157  else {
1158  return 0;
1159  }
1160 }
1161 #endif
1162 
1163 #if defined(LOCAL_PEERCRED) /* FreeBSD, MacOS X */
1164 static int
1165 inspect_local_peercred(int level, int optname, VALUE data, VALUE ret)
1166 {
1167  if (RSTRING_LEN(data) == sizeof(struct xucred)) {
1168  struct xucred cred;
1169  memcpy(&cred, RSTRING_PTR(data), sizeof(struct xucred));
1170  if (cred.cr_version != XUCRED_VERSION)
1171  return 0;
1172  rb_str_catf(ret, " version=%u", cred.cr_version);
1173  rb_str_catf(ret, " euid=%u", cred.cr_uid);
1174  if (cred.cr_ngroups) {
1175  int i;
1176  const char *sep = " groups=";
1177  for (i = 0; i < cred.cr_ngroups; i++) {
1178  rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]);
1179  sep = ",";
1180  }
1181  }
1182  rb_str_cat2(ret, " (xucred)");
1183  return 1;
1184  }
1185  else {
1186  return 0;
1187  }
1188 }
1189 #endif
1190 
1191 
1192 /*
1193  * call-seq:
1194  * sockopt.inspect => string
1195  *
1196  * Returns a string which shows sockopt in human-readable form.
1197  *
1198  * p Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")).inspect
1199  * #=> "#<Socket::Option: INET SOCKET KEEPALIVE 1>"
1200  *
1201  */
1202 static VALUE
1203 sockopt_inspect(VALUE self)
1204 {
1205  int family = NUM2INT(sockopt_family_m(self));
1206  int level = NUM2INT(sockopt_level_m(self));
1207  int optname = NUM2INT(sockopt_optname_m(self));
1208  VALUE data = sockopt_data(self);
1209  VALUE v, ret;
1210  ID family_id, level_id, optname_id;
1211  int inspected;
1212 
1213  StringValue(data);
1214 
1215  ret = rb_sprintf("#<%s:", rb_obj_classname(self));
1216 
1217  family_id = rsock_intern_family_noprefix(family);
1218  if (family_id)
1219  rb_str_catf(ret, " %s", rb_id2name(family_id));
1220  else
1221  rb_str_catf(ret, " family:%d", family);
1222 
1223  if (level == SOL_SOCKET) {
1224  rb_str_cat2(ret, " SOCKET");
1225 
1226  optname_id = rsock_intern_so_optname(optname);
1227  if (optname_id)
1228  rb_str_catf(ret, " %s", rb_id2name(optname_id));
1229  else
1230  rb_str_catf(ret, " optname:%d", optname);
1231  }
1232 #ifdef HAVE_SYS_UN_H
1233  else if (family == AF_UNIX) {
1234  rb_str_catf(ret, " level:%d", level);
1235 
1236  optname_id = rsock_intern_local_optname(optname);
1237  if (optname_id)
1238  rb_str_catf(ret, " %s", rb_id2name(optname_id));
1239  else
1240  rb_str_catf(ret, " optname:%d", optname);
1241  }
1242 #endif
1243  else if (IS_IP_FAMILY(family)) {
1244  level_id = rsock_intern_iplevel(level);
1245  if (level_id)
1246  rb_str_catf(ret, " %s", rb_id2name(level_id));
1247  else
1248  rb_str_catf(ret, " level:%d", level);
1249 
1250  v = optname_to_sym(level, optname);
1251  if (SYMBOL_P(v))
1252  rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(v));
1253  else
1254  rb_str_catf(ret, " optname:%d", optname);
1255  }
1256  else {
1257  rb_str_catf(ret, " level:%d", level);
1258  rb_str_catf(ret, " optname:%d", optname);
1259  }
1260 
1261  inspected = 0;
1262 
1263  if (level == SOL_SOCKET)
1264  family = AF_UNSPEC;
1265  switch (family) {
1266  case AF_UNSPEC:
1267  switch (level) {
1268  case SOL_SOCKET:
1269  switch (optname) {
1270 # if defined(SO_DEBUG) /* POSIX */
1271  case SO_DEBUG: inspected = inspect_int(level, optname, data, ret); break;
1272 # endif
1273 # if defined(SO_ERROR) /* POSIX */
1274  case SO_ERROR: inspected = inspect_errno(level, optname, data, ret); break;
1275 # endif
1276 # if defined(SO_TYPE) /* POSIX */
1277  case SO_TYPE: inspected = inspect_socktype(level, optname, data, ret); break;
1278 # endif
1279 # if defined(SO_ACCEPTCONN) /* POSIX */
1280  case SO_ACCEPTCONN: inspected = inspect_int(level, optname, data, ret); break;
1281 # endif
1282 # if defined(SO_BROADCAST) /* POSIX */
1283  case SO_BROADCAST: inspected = inspect_int(level, optname, data, ret); break;
1284 # endif
1285 # if defined(SO_REUSEADDR) /* POSIX */
1286  case SO_REUSEADDR: inspected = inspect_int(level, optname, data, ret); break;
1287 # endif
1288 # if defined(SO_KEEPALIVE) /* POSIX */
1289  case SO_KEEPALIVE: inspected = inspect_int(level, optname, data, ret); break;
1290 # endif
1291 # if defined(SO_OOBINLINE) /* POSIX */
1292  case SO_OOBINLINE: inspected = inspect_int(level, optname, data, ret); break;
1293 # endif
1294 # if defined(SO_SNDBUF) /* POSIX */
1295  case SO_SNDBUF: inspected = inspect_int(level, optname, data, ret); break;
1296 # endif
1297 # if defined(SO_RCVBUF) /* POSIX */
1298  case SO_RCVBUF: inspected = inspect_int(level, optname, data, ret); break;
1299 # endif
1300 # if defined(SO_DONTROUTE) /* POSIX */
1301  case SO_DONTROUTE: inspected = inspect_int(level, optname, data, ret); break;
1302 # endif
1303 # if defined(SO_RCVLOWAT) /* POSIX */
1304  case SO_RCVLOWAT: inspected = inspect_int(level, optname, data, ret); break;
1305 # endif
1306 # if defined(SO_SNDLOWAT) /* POSIX */
1307  case SO_SNDLOWAT: inspected = inspect_int(level, optname, data, ret); break;
1308 # endif
1309 # if defined(SO_LINGER) /* POSIX */
1310  case SO_LINGER: inspected = inspect_linger(level, optname, data, ret); break;
1311 # endif
1312 # if defined(SO_RCVTIMEO) /* POSIX */
1313  case SO_RCVTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
1314 # endif
1315 # if defined(SO_SNDTIMEO) /* POSIX */
1316  case SO_SNDTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break;
1317 # endif
1318 # if defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */
1319  case SO_PEERCRED: inspected = inspect_peercred(level, optname, data, ret); break;
1320 # endif
1321  }
1322  break;
1323  }
1324  break;
1325 
1326  case AF_INET:
1327 #ifdef INET6
1328  case AF_INET6:
1329 #endif
1330  switch (level) {
1331 # if defined(IPPROTO_IP)
1332  case IPPROTO_IP:
1333  switch (optname) {
1334 # if defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */
1335  case IP_MULTICAST_IF: inspected = inspect_ipv4_multicast_if(level, optname, data, ret); break;
1336 # endif
1337 # if defined(IP_ADD_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
1338  case IP_ADD_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
1339 # endif
1340 # if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */
1341  case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break;
1342 # endif
1343 # if defined(IP_MULTICAST_LOOP) /* 4.4BSD, GNU/Linux */
1344  case IP_MULTICAST_LOOP: inspected = inspect_ipv4_multicast_loop(level, optname, data, ret); break;
1345 # endif
1346 # if defined(IP_MULTICAST_TTL) /* 4.4BSD, GNU/Linux */
1347  case IP_MULTICAST_TTL: inspected = inspect_ipv4_multicast_ttl(level, optname, data, ret); break;
1348 # endif
1349  }
1350  break;
1351 # endif
1352 
1353 # if defined(IPPROTO_IPV6)
1354  case IPPROTO_IPV6:
1355  switch (optname) {
1356 # if defined(IPV6_MULTICAST_HOPS) /* POSIX */
1357  case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
1358 # endif
1359 # if defined(IPV6_MULTICAST_IF) /* POSIX */
1360  case IPV6_MULTICAST_IF: inspected = inspect_ipv6_multicast_if(level, optname, data, ret); break;
1361 # endif
1362 # if defined(IPV6_MULTICAST_LOOP) /* POSIX */
1363  case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break;
1364 # endif
1365 # if defined(IPV6_JOIN_GROUP) /* POSIX */
1366  case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
1367 # endif
1368 # if defined(IPV6_LEAVE_GROUP) /* POSIX */
1369  case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break;
1370 # endif
1371 # if defined(IPV6_UNICAST_HOPS) /* POSIX */
1372  case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break;
1373 # endif
1374 # if defined(IPV6_V6ONLY) /* POSIX */
1375  case IPV6_V6ONLY: inspected = inspect_int(level, optname, data, ret); break;
1376 # endif
1377  }
1378  break;
1379 # endif
1380 
1381 # if defined(IPPROTO_TCP)
1382  case IPPROTO_TCP:
1383  switch (optname) {
1384 # if defined(TCP_NODELAY) /* POSIX */
1385  case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break;
1386 # endif
1387 # if defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO) /* Linux, FreeBSD */
1388  case TCP_INFO: inspected = inspect_tcp_info(level, optname, data, ret); break;
1389 # endif
1390  }
1391  break;
1392 # endif
1393  }
1394  break;
1395 
1396 #ifdef HAVE_SYS_UN_H
1397  case AF_UNIX:
1398  switch (level) {
1399  case 0:
1400  switch (optname) {
1401 # if defined(LOCAL_PEERCRED)
1402  case LOCAL_PEERCRED: inspected = inspect_local_peercred(level, optname, data, ret); break;
1403 # endif
1404  }
1405  break;
1406  }
1407  break;
1408 #endif
1409  }
1410 
1411  if (!inspected) {
1412  rb_str_cat2(ret, " ");
1413  rb_str_append(ret, rb_str_dump(data));
1414  }
1415 
1416  rb_str_cat2(ret, ">");
1417 
1418  return ret;
1419 }
1420 
1421 /*
1422  * call-seq:
1423  * sockopt.unpack(template) => array
1424  *
1425  * Calls String#unpack on sockopt.data.
1426  *
1427  * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i"))
1428  * p sockopt.unpack("i") #=> [1]
1429  * p sockopt.data.unpack("i") #=> [1]
1430  */
1431 static VALUE
1432 sockopt_unpack(VALUE self, VALUE template)
1433 {
1434  return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template);
1435 }
1436 
1437 void
1439 {
1440  /*
1441  * Document-class: Socket::Option
1442  *
1443  * Socket::Option represents a socket option used by
1444  * BasicSocket#getsockopt and BasicSocket#setsockopt. A socket option
1445  * contains the socket #family, protocol #level, option name #optname and
1446  * option value #data.
1447  */
1449  rb_define_method(rb_cSockOpt, "initialize", sockopt_initialize, 4);
1450  rb_define_method(rb_cSockOpt, "family", sockopt_family_m, 0);
1451  rb_define_method(rb_cSockOpt, "level", sockopt_level_m, 0);
1452  rb_define_method(rb_cSockOpt, "optname", sockopt_optname_m, 0);
1453  rb_define_method(rb_cSockOpt, "data", sockopt_data, 0);
1454  rb_define_method(rb_cSockOpt, "inspect", sockopt_inspect, 0);
1455 
1456  rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4);
1457  rb_define_method(rb_cSockOpt, "int", sockopt_int, 0);
1458 
1459  rb_define_singleton_method(rb_cSockOpt, "byte", sockopt_s_byte, 4);
1460  rb_define_method(rb_cSockOpt, "byte", sockopt_byte, 0);
1461 
1462  rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4);
1463  rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0);
1464 
1465  rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2);
1466  rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0);
1467 
1468  rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_s_ipv4_multicast_ttl, 1);
1469  rb_define_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_ipv4_multicast_ttl, 0);
1470 
1471  rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_s_ipv4_multicast_loop, 1);
1472  rb_define_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_ipv4_multicast_loop, 0);
1473 
1474  rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1);
1475 
1476  rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */
1477 }
#define INET6_ADDRSTRLEN
Definition: constdefs.h:1832
#define T_OBJECT
Definition: ruby.h:491
#define IPPROTO_UDP
Definition: constdefs.h:627
ID rsock_intern_family_noprefix(int val)
Definition: constdefs.c:6745
#define INT2NUM(x)
Definition: ruby.h:1538
#define NUM2INT(x)
Definition: ruby.h:684
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
#define IPPROTO_IP
Definition: constdefs.h:586
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
#define Qtrue
Definition: ruby.h:437
const int id
Definition: nkf.c:209
#define UNREACHABLE
Definition: ruby.h:46
#define check_size(len, size)
Definition: option.c:30
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:774
ID rsock_intern_so_optname(int val)
Definition: constdefs.c:6790
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
const char * inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
Definition: option.c:651
const char * rb_obj_classname(VALUE)
Definition: variable.c:459
unsigned char uint8_t
Definition: sha2.h:100
#define NEWOBJ_OF(obj, type, klass, flags)
Definition: ruby.h:754
int rsock_family_arg(VALUE domain)
Definition: constants.c:43
#define pack_var(v)
Definition: option.c:5
VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data)
Definition: option.c:107
#define level
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1893
VALUE rb_str_cat2(VALUE, const char *)
#define snprintf
Definition: subst.h:6
VALUE rb_check_to_integer(VALUE, const char *)
Tries to convert val into Integer.
Definition: object.c:3062
#define NIL_P(v)
Definition: ruby.h:451
#define IPPROTO_TCP
Definition: constdefs.h:610
int rsock_level_arg(int family, VALUE level)
Definition: constants.c:57
#define inspect_ipv4_multicast_loop(a, b, c, d)
Definition: option.c:430
#define Qfalse
Definition: ruby.h:436
int err
Definition: win32.c:135
VALUE rb_cSocket
Definition: init.c:22
#define IS_IP_FAMILY(af)
Definition: rubysocket.h:156
#define RSTRING_LEN(str)
Definition: ruby.h:971
int socklen_t
Definition: getaddrinfo.c:83
ID rsock_intern_iplevel(int val)
Definition: constdefs.c:6781
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1452
#define NUM2CHR(x)
Definition: ruby.h:1575
VALUE rb_ivar_set(VALUE, ID, VALUE)
Definition: variable.c:1315
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4309
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:639
#define PRIsVALUE
Definition: ruby.h:135
#define XCAT(x, y)
Definition: option.c:8
unsigned long ID
Definition: ruby.h:86
ID rsock_intern_tcp_optname(int val)
Definition: constdefs.c:6817
unsigned long VALUE
Definition: ruby.h:85
VALUE rb_eTypeError
Definition: error.c:801
ID rsock_intern_local_optname(int val)
Definition: constdefs.c:6844
const char * rb_id2name(ID)
Definition: symbol.c:751
ID rsock_intern_socktype(int val)
Definition: constdefs.c:6763
VALUE rb_str_dump(VALUE)
Definition: string.c:5920
#define TYPE_IP_MULTICAST_LOOP
Definition: option.c:25
unsigned int uint32_t
Definition: sha2.h:101
register unsigned int len
Definition: zonetab.h:51
#define INET_ADDRSTRLEN
Definition: constdefs.h:1825
#define RSTRING_PTR(str)
Definition: ruby.h:975
#define CHR2FIX(x)
Definition: ruby.h:1576
VALUE rb_str_catf(VALUE str, const char *format,...)
Definition: sprintf.c:1492
RUBY_EXTERN char * strerror(int)
Definition: strerror.c:11
#define RTEST(v)
Definition: ruby.h:450
#define AF_UNSPEC
Definition: sockport.h:101
ID rsock_intern_udp_optname(int val)
Definition: constdefs.c:6826
VALUE rb_str_cat_cstr(VALUE, const char *)
Definition: string.c:2756
int rsock_optname_arg(int family, int level, VALUE optname)
Definition: constants.c:69
VALUE rb_cSockOpt
Definition: option.c:3
const char * name
Definition: nkf.c:208
#define ID2SYM(x)
Definition: ruby.h:383
Definition: ruby.h:889
#define IFNAMSIZ
void rsock_init_sockopt(void)
Definition: option.c:1438
#define inspect_ipv4_multicast_ttl(a, b, c, d)
Definition: option.c:481
ID rsock_intern_ipv6_optname(int val)
Definition: constdefs.c:6808
#define rb_intern(str)
#define SYMBOL_P(x)
Definition: ruby.h:382
#define NULL
Definition: _sdbm.c:102
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
VALUE rb_str_append(VALUE, VALUE)
Definition: string.c:2900
#define TYPE_IP_MULTICAST_TTL
Definition: option.c:26
VALUE rb_to_int(VALUE)
Converts val into Integer.
Definition: object.c:3084
VALUE rb_attr_get(VALUE, ID)
Definition: variable.c:1224
#define StringValue(v)
Definition: ruby.h:569
ID rsock_intern_ip_optname(int val)
Definition: constdefs.c:6799
#define rb_sym2str(sym)
Definition: console.c:107