Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
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
36static VALUE
37sockopt_pack_byte(VALUE value)
38{
39 char i = NUM2CHR(rb_to_int(value));
40 return pack_var(i);
41}
42
43static VALUE
44sockopt_pack_int(VALUE value)
45{
46 int i = NUM2INT(rb_to_int(value));
47 return pack_var(i);
48}
49
50static VALUE
51constant_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
61static VALUE
62optname_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 */
92static VALUE
93sockopt_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
106VALUE
107rsock_sockopt_new(int family, int level, int optname, VALUE data)
108{
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 */
124static VALUE
125sockopt_family_m(VALUE self)
126{
127 return rb_attr_get(self, rb_intern("family"));
128}
129
130static int
131sockopt_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 */
145static VALUE
146sockopt_level_m(VALUE self)
147{
148 return INT2NUM(sockopt_level(self));
149}
150
151static int
152sockopt_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 */
166static VALUE
167sockopt_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 */
182static VALUE
183sockopt_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 */
199static VALUE
200sockopt_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 */
217static VALUE
218sockopt_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 */
237static VALUE
238sockopt_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 */
257static VALUE
258sockopt_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 */
284static VALUE
285sockopt_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 */
303static VALUE
304sockopt_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 */
333static VALUE
334sockopt_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 */
356static VALUE
357sockopt_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 */
393static VALUE
394sockopt_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 */
414static VALUE
415sockopt_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");
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 */
445static VALUE
446sockopt_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 */
465static VALUE
466sockopt_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");
479}
480
481#define inspect_ipv4_multicast_ttl(a,b,c,d) \
482 XCAT(inspect_,TYPE_IP_MULTICAST_TTL)(a,b,c,d)
483
484static int
485inspect_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
499static int
500inspect_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
512static int
513inspect_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)
529static int
530inspect_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 */
545static int
546inspect_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 */
566static int
567inspect_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));
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
586static int
587inspect_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
650const char *
651inet_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". */
668static int
669rb_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 */
686static int
687inspect_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 */
710static int
711inspect_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 */
736static int
737inspect_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 */
751static int
752inspect_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 */
774static int
775inspect_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 */
792static int
793inspect_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
885static void
886inspect_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
925static void
926inspect_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)
937static void
938inspect_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
961static int
962inspect_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
1146static int
1147inspect_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 */
1164static int
1165inspect_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 */
1202static VALUE
1203sockopt_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 */
1431static VALUE
1432sockopt_unpack(VALUE self, VALUE template)
1433{
1434 return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template);
1435}
1436
1437void
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}
int rsock_family_arg(VALUE domain)
Definition: constants.c:42
int rsock_optname_arg(int family, int level, VALUE optname)
Definition: constants.c:68
int rsock_level_arg(int family, VALUE level)
Definition: constants.c:56
ID rsock_intern_ipv6_optname(int val)
Definition: constdefs.c:6820
ID rsock_intern_tcp_optname(int val)
Definition: constdefs.c:6829
ID rsock_intern_udp_optname(int val)
Definition: constdefs.c:6838
ID rsock_intern_socktype(int val)
Definition: constdefs.c:6775
ID rsock_intern_iplevel(int val)
Definition: constdefs.c:6793
ID rsock_intern_local_optname(int val)
Definition: constdefs.c:6856
ID rsock_intern_ip_optname(int val)
Definition: constdefs.c:6811
ID rsock_intern_so_optname(int val)
Definition: constdefs.c:6802
ID rsock_intern_family_noprefix(int val)
Definition: constdefs.c:6757
int socklen_t
Definition: getaddrinfo.c:83
VALUE rb_define_class_under(VALUE, const char *, VALUE)
Defines a class under the namespace of outer.
Definition: class.c:711
VALUE rb_cObject
Object class.
Definition: ruby.h:2012
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
VALUE rb_eTypeError
Definition: error.c:924
VALUE rb_check_to_integer(VALUE, const char *)
Tries to convert val into Integer.
Definition: object.c:2999
VALUE rb_to_int(VALUE)
Converts val into Integer.
Definition: object.c:3021
const char * name
Definition: nkf.c:208
VALUE rb_cSockOpt
Definition: option.c:3
const char * inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
Definition: option.c:651
#define inspect_ipv4_multicast_loop(a, b, c, d)
Definition: option.c:430
#define pack_var(v)
Definition: option.c:5
void rsock_init_sockopt(void)
Definition: option.c:1438
#define XCAT(x, y)
Definition: option.c:8
#define check_size(len, size)
Definition: option.c:30
#define inspect_ipv4_multicast_ttl(a, b, c, d)
Definition: option.c:481
#define IFNAMSIZ
#define TYPE_IP_MULTICAST_LOOP
Definition: option.c:25
VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data)
Definition: option.c:107
#define TYPE_IP_MULTICAST_TTL
Definition: option.c:26
__uint32_t uint32_t
#define NULL
use StringValue() instead")))
#define RSTRING_LEN(str)
#define NEWOBJ_OF(obj, type, klass, flags)
#define RTEST(v)
#define CHR2FIX(x)
VALUE rb_assoc_new(VALUE, VALUE)
Definition: array.c:896
__uint8_t uint8_t
#define rb_str_cat2
const VALUE VALUE obj
#define RSTRING_PTR(str)
char * strerror(int)
Definition: strerror.c:11
int snprintf(char *__restrict__, size_t, const char *__restrict__,...) __attribute__((__format__(__printf__
#define NIL_P(v)
#define NUM2CHR(x)
#define ID2SYM(x)
const char * rb_id2name(ID)
Definition: symbol.c:801
unsigned long VALUE
VALUE rb_sym2str(VALUE)
Definition: symbol.c:784
uint32_t i
__inline__ const void *__restrict__ size_t len
const char * rb_obj_classname(VALUE)
Definition: variable.c:289
#define INT2NUM(x)
#define NUM2INT(x)
void rb_define_singleton_method(VALUE, const char *, VALUE(*)(), int)
#define PRIsVALUE
void * memset(void *, int, size_t)
#define rb_funcall(recv, mid, argc,...)
int VALUE v
#define rb_str_cat_cstr(str, ptr)
#define rb_intern(str)
#define UNREACHABLE_RETURN(val)
VALUE rb_str_catf(VALUE, const char *,...) __attribute__((format(printf
#define Qtrue
struct rb_call_cache buf
VALUE rb_str_dump(VALUE)
Definition: string.c:6042
VALUE rb_str_append(VALUE, VALUE)
Definition: string.c:2965
VALUE rb_attr_get(VALUE, ID)
Definition: variable.c:1084
#define Qnil
#define Qfalse
void * memcpy(void *__restrict__, const void *__restrict__, size_t)
#define T_OBJECT
#define SYMBOL_P(x)
VALUE rb_ivar_set(VALUE, ID, VALUE)
Definition: variable.c:1300
VALUE rb_sprintf(const char *,...) __attribute__((format(printf
unsigned long ID
VALUE ID id
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define IS_IP_FAMILY(af)
Definition: rubysocket.h:162
#define INET_ADDRSTRLEN
Definition: constdefs.h:1825
#define IPPROTO_TCP
Definition: constdefs.h:610
#define IPPROTO_UDP
Definition: constdefs.h:627
#define IPPROTO_IP
Definition: constdefs.h:586
#define INET6_ADDRSTRLEN
Definition: constdefs.h:1832
VALUE rb_cSocket
Definition: init.c:26
#define AF_UNSPEC
Definition: sockport.h:101
Definition: ruby.h:922