diff options
Diffstat (limited to 'ext/socket/option.c')
| -rw-r--r-- | ext/socket/option.c | 674 |
1 files changed, 614 insertions, 60 deletions
diff --git a/ext/socket/option.c b/ext/socket/option.c index 1f334bbca0..0d818d0c70 100644 --- a/ext/socket/option.c +++ b/ext/socket/option.c @@ -2,6 +2,51 @@ VALUE rb_cSockOpt; +#define pack_var(v) rb_str_new((const char *)&(v), sizeof(v)) + +#define CAT(x,y) x##y +#define XCAT(x,y) CAT(x,y) + +#if defined(__linux__) || \ + defined(__GNU__) /* GNU/Hurd */ || \ + defined(__FreeBSD__) || \ + defined(__DragonFly__) || \ + defined(__APPLE__) || \ + defined(_WIN32) || \ + defined(__CYGWIN__) +# define TYPE_IP_MULTICAST_LOOP int +# define TYPE_IP_MULTICAST_TTL int +#else +/* The original IP multicast implementation by Steve Deering + * NetBSD + * OpenBSD + * SunOS + */ +# define TYPE_IP_MULTICAST_LOOP byte +# define TYPE_IP_MULTICAST_TTL byte +# define USE_INSPECT_BYTE 1 +#endif + +#define check_size(len, size) \ + ((len) == (size) ? \ + (void)0 : \ + rb_raise(rb_eTypeError, "size differ. expected as "#size"=%d but %ld", \ + (int)size, (long)(len))) + +static VALUE +sockopt_pack_byte(VALUE value) +{ + char i = NUM2CHR(rb_to_int(value)); + return pack_var(i); +} + +static VALUE +sockopt_pack_int(VALUE value) +{ + int i = NUM2INT(rb_to_int(value)); + return pack_var(i); +} + static VALUE constant_to_sym(int constant, ID (*intern_const)(int)) { @@ -21,7 +66,7 @@ optname_to_sym(int level, int optname) return constant_to_sym(optname, rsock_intern_so_optname); case IPPROTO_IP: return constant_to_sym(optname, rsock_intern_ip_optname); -#ifdef INET6 +#ifdef IPPROTO_IPV6 case IPPROTO_IPV6: return constant_to_sym(optname, rsock_intern_ipv6_optname); #endif @@ -61,10 +106,9 @@ sockopt_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE voptname, VALU VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data) { - NEWOBJ(obj, struct RObject); - OBJSETUP(obj, rb_cSockOpt, T_OBJECT); + VALUE obj = rb_obj_alloc(rb_cSockOpt); StringValue(data); - sockopt_initialize((VALUE)obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data); + sockopt_initialize(obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data); return (VALUE)obj; } @@ -128,6 +172,7 @@ sockopt_optname_m(VALUE self) /* * call-seq: * sockopt.data => string + * sockopt.to_s => string * * returns the socket option data as a string. * @@ -144,6 +189,42 @@ sockopt_data(VALUE self) /* * call-seq: + * Socket::Option.byte(family, level, optname, integer) => sockopt + * + * Creates a new Socket::Option object which contains a byte as data. + * + * p Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1) + * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> + */ +static VALUE +sockopt_s_byte(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int optname = rsock_optname_arg(family, level, voptname); + return rsock_sockopt_new(family, level, optname, sockopt_pack_byte(vint)); +} + +/* + * call-seq: + * sockopt.byte => integer + * + * Returns the data in _sockopt_ as an byte. + * + * sockopt = Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1) + * p sockopt.byte => 1 + */ +static VALUE +sockopt_byte(VALUE self) +{ + VALUE data = sockopt_data(self); + StringValue(data); + check_size(RSTRING_LEN(data), sizeof(char)); + return CHR2FIX(*RSTRING_PTR(data)); +} + +/* + * call-seq: * Socket::Option.int(family, level, optname, integer) => sockopt * * Creates a new Socket::Option object which contains an int as data. @@ -159,8 +240,7 @@ sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vi int family = rsock_family_arg(vfamily); int level = rsock_level_arg(family, vlevel); int optname = rsock_optname_arg(family, level, voptname); - int i = NUM2INT(vint); - return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i))); + return rsock_sockopt_new(family, level, optname, sockopt_pack_int(vint)); } /* @@ -180,9 +260,7 @@ sockopt_int(VALUE self) int i; VALUE data = sockopt_data(self); StringValue(data); - if (RSTRING_LEN(data) != sizeof(int)) - rb_raise(rb_eTypeError, "size differ. expected as sizeof(int)=%d but %ld", - (int)sizeof(int), (long)RSTRING_LEN(data)); + check_size(RSTRING_LEN(data), sizeof(int)); memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); return INT2NUM(i); } @@ -194,6 +272,8 @@ sockopt_int(VALUE self) * Creates a new Socket::Option object which contains boolean as data. * Actually 0 or 1 as int is used. * + * require 'socket' + * * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> * @@ -208,7 +288,7 @@ sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE v int level = rsock_level_arg(family, vlevel); int optname = rsock_optname_arg(family, level, voptname); int i = RTEST(vbool) ? 1 : 0; - return rsock_sockopt_new(family, level, optname, rb_str_new((char*)&i, sizeof(i))); + return rsock_sockopt_new(family, level, optname, pack_var(i)); } /* @@ -224,12 +304,15 @@ static VALUE sockopt_bool(VALUE self) { int i; + long len; VALUE data = sockopt_data(self); StringValue(data); - if (RSTRING_LEN(data) != sizeof(int)) - rb_raise(rb_eTypeError, "size differ. expected as sizeof(int)=%d but %ld", - (int)sizeof(int), (long)RSTRING_LEN(data)); - memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + len = RSTRING_LEN(data); + if (len == 1) { + return *RSTRING_PTR(data) == 0 ? Qfalse : Qtrue; + } + check_size(len, sizeof(int)); + memcpy((char*)&i, RSTRING_PTR(data), len); return i == 0 ? Qfalse : Qtrue; } @@ -258,7 +341,7 @@ sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs) else l.l_onoff = RTEST(vonoff) ? 1 : 0; l.l_linger = NUM2INT(vsecs); - return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, rb_str_new((char*)&l, sizeof(l))); + return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, pack_var(l)); } /* @@ -281,9 +364,7 @@ sockopt_linger(VALUE self) if (level != SOL_SOCKET || optname != SO_LINGER) rb_raise(rb_eTypeError, "linger socket option expected"); - if (RSTRING_LEN(data) != sizeof(l)) - rb_raise(rb_eTypeError, "size differ. expected as sizeof(struct linger)=%d but %ld", - (int)sizeof(struct linger), (long)RSTRING_LEN(data)); + check_size(RSTRING_LEN(data), sizeof(struct linger)); memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger)); switch (l.l_onoff) { case 0: vonoff = Qfalse; break; @@ -294,6 +375,112 @@ sockopt_linger(VALUE self) return rb_assoc_new(vonoff, vsecs); } +/* + * call-seq: + * Socket::Option.ipv4_multicast_loop(integer) => sockopt + * + * Creates a new Socket::Option object for IP_MULTICAST_LOOP. + * + * The size is dependent on the platform. + * + * sockopt = Socket::Option.int(:INET, :IPPROTO_IP, :IP_MULTICAST_LOOP, 1) + * p sockopt.int => 1 + * + * p Socket::Option.ipv4_multicast_loop(10) + * #=> #<Socket::Option: INET IP MULTICAST_LOOP 10> + * + */ +static VALUE +sockopt_s_ipv4_multicast_loop(VALUE klass, VALUE value) +{ + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP) + VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_LOOP)(value); + return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_LOOP, o); +#else +# error IPPROTO_IP or IP_MULTICAST_LOOP is not implemented +#endif +} + +/* + * call-seq: + * sockopt.ipv4_multicast_loop => integer + * + * Returns the ipv4_multicast_loop data in _sockopt_ as an integer. + * + * sockopt = Socket::Option.ipv4_multicast_loop(10) + * p sockopt.ipv4_multicast_loop => 10 + */ +static VALUE +sockopt_ipv4_multicast_loop(VALUE self) +{ + int family = NUM2INT(sockopt_family_m(self)); + int level = sockopt_level(self); + int optname = sockopt_optname(self); + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP) + if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_LOOP) { + return XCAT(sockopt_,TYPE_IP_MULTICAST_LOOP)(self); + } +#endif + rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected"); + UNREACHABLE_RETURN(Qnil); +} + +#define inspect_ipv4_multicast_loop(a,b,c,d) \ + XCAT(inspect_,TYPE_IP_MULTICAST_LOOP)(a,b,c,d) + +/* + * call-seq: + * Socket::Option.ipv4_multicast_ttl(integer) => sockopt + * + * Creates a new Socket::Option object for IP_MULTICAST_TTL. + * + * The size is dependent on the platform. + * + * p Socket::Option.ipv4_multicast_ttl(10) + * #=> #<Socket::Option: INET IP MULTICAST_TTL 10> + * + */ +static VALUE +sockopt_s_ipv4_multicast_ttl(VALUE klass, VALUE value) +{ +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_TTL)(value); + return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_TTL, o); +#else +# error IPPROTO_IP or IP_MULTICAST_TTL is not implemented +#endif +} + +/* + * call-seq: + * sockopt.ipv4_multicast_ttl => integer + * + * Returns the ipv4_multicast_ttl data in _sockopt_ as an integer. + * + * sockopt = Socket::Option.ipv4_multicast_ttl(10) + * p sockopt.ipv4_multicast_ttl => 10 + */ +static VALUE +sockopt_ipv4_multicast_ttl(VALUE self) +{ + int family = NUM2INT(sockopt_family_m(self)); + int level = sockopt_level(self); + int optname = sockopt_optname(self); + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_TTL) { + return XCAT(sockopt_,TYPE_IP_MULTICAST_TTL)(self); + } +#endif + rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected"); + UNREACHABLE_RETURN(Qnil); +} + +#define inspect_ipv4_multicast_ttl(a,b,c,d) \ + XCAT(inspect_,TYPE_IP_MULTICAST_TTL)(a,b,c,d) + static int inspect_int(int level, int optname, VALUE data, VALUE ret) { @@ -308,6 +495,20 @@ inspect_int(int level, int optname, VALUE data, VALUE ret) } } +#ifdef USE_INSPECT_BYTE +static int +inspect_byte(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(unsigned char)) { + rb_str_catf(ret, " %d", (unsigned char)*RSTRING_PTR(data)); + return 1; + } + else { + return 0; + } +} +#endif + static int inspect_errno(int level, int optname, VALUE data, VALUE ret) { @@ -404,6 +605,15 @@ inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret) * (MULTICAST 1.2 Release) * http://www.kohala.com/start/mcast.api.txt * + * There are 2 socket options which takes a u_char (unsigned char). + * + * IP_MULTICAST_TTL + * IP_MULTICAST_LOOP + * + * However Linux and FreeBSD setsockname accepts int argument + * as well as u_char. + * Their getsockname returns int. + * * There are 3 socket options which takes a struct. * * IP_MULTICAST_IF: struct in_addr @@ -436,8 +646,8 @@ inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret) * it is not distinguishable by the size. */ -#ifndef HAVE_INET_NTOP -static char * +#if !defined HAVE_INET_NTOP && ! defined _WIN32 +const char * inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len) { #ifdef HAVE_INET_NTOA @@ -447,8 +657,8 @@ inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len) #else unsigned long x = ntohl(*(unsigned long*)addr); snprintf(numaddr, numaddr_len, "%d.%d.%d.%d", - (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, - (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); #endif return numaddr; } @@ -460,10 +670,10 @@ rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int { #if defined(HAVE_IF_INDEXTONAME) char ifbuf[IFNAMSIZ]; - if (if_indextoname(ifindex, ifbuf) == NULL) - return snprintf(buf, len, "%s%u", fail_prefix, ifindex); - else + if (if_indextoname(ifindex, ifbuf)) return snprintf(buf, len, "%s%s", succ_prefix, ifbuf); + else + return snprintf(buf, len, "%s%u", fail_prefix, ifindex); #else # ifndef IFNAMSIZ # define IFNAMSIZ (sizeof(unsigned int)*3+1) @@ -600,6 +810,333 @@ inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret) } #endif +#if defined(IPPROTO_TCP) && defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO) + +#ifdef __FreeBSD__ +# ifndef HAVE_CONST_TCP_ESTABLISHED +# define TCP_ESTABLISHED TCPS_ESTABLISHED +# endif +# ifndef HAVE_CONST_TCP_SYN_SENT +# define TCP_SYN_SENT TCPS_SYN_SENT +# endif +# ifndef HAVE_CONST_TCP_SYN_RECV +# define TCP_SYN_RECV TCPS_SYN_RECEIVED +# endif +# ifndef HAVE_CONST_TCP_FIN_WAIT1 +# define TCP_FIN_WAIT1 TCPS_FIN_WAIT_1 +# endif +# ifndef HAVE_CONST_TCP_FIN_WAIT2 +# define TCP_FIN_WAIT2 TCPS_FIN_WAIT_2 +# endif +# ifndef HAVE_CONST_TCP_TIME_WAIT +# define TCP_TIME_WAIT TCPS_TIME_WAIT +# endif +# ifndef HAVE_CONST_TCP_CLOSE +# define TCP_CLOSE TCPS_CLOSED +# endif +# ifndef HAVE_CONST_TCP_CLOSE_WAIT +# define TCP_CLOSE_WAIT TCPS_CLOSE_WAIT +# endif +# ifndef HAVE_CONST_TCP_LAST_ACK +# define TCP_LAST_ACK TCPS_LAST_ACK +# endif +# ifndef HAVE_CONST_TCP_LISTEN +# define TCP_LISTEN TCPS_LISTEN +# endif +# ifndef HAVE_CONST_TCP_CLOSING +# define TCP_CLOSING TCPS_CLOSING +# endif +#endif + +#if defined(HAVE_CONST_TCP_ESTABLISHED) && !defined(TCP_ESTABLISHED) +# define TCP_ESTABLISHED TCP_ESTABLISHED +#endif +#if defined(HAVE_CONST_TCP_SYN_SENT) && !defined(TCP_SYN_SENT) +# define TCP_SYN_SENT TCP_SYN_SENT +#endif +#if defined(HAVE_CONST_TCP_SYN_RECV) && !defined(TCP_SYN_RECV) +# define TCP_SYN_RECV TCP_SYN_RECV +#endif +#if defined(HAVE_CONST_TCP_FIN_WAIT1) && !defined(TCP_FIN_WAIT1) +# define TCP_FIN_WAIT1 TCP_FIN_WAIT1 +#endif +#if defined(HAVE_CONST_TCP_FIN_WAIT2) && !defined(TCP_FIN_WAIT2) +# define TCP_FIN_WAIT2 TCP_FIN_WAIT2 +#endif +#if defined(HAVE_CONST_TCP_TIME_WAIT) && !defined(TCP_TIME_WAIT) +# define TCP_TIME_WAIT TCP_TIME_WAIT +#endif +#if defined(HAVE_CONST_TCP_CLOSE) && !defined(TCP_CLOSE) +# define TCP_CLOSE TCP_CLOSE +#endif +#if defined(HAVE_CONST_TCP_CLOSE_WAIT) && !defined(TCP_CLOSE_WAIT) +# define TCP_CLOSE_WAIT TCP_CLOSE_WAIT +#endif +#if defined(HAVE_CONST_TCP_LAST_ACK) && !defined(TCP_LAST_ACK) +# define TCP_LAST_ACK TCP_LAST_ACK +#endif +#if defined(HAVE_CONST_TCP_LISTEN) && !defined(TCP_LISTEN) +# define TCP_LISTEN TCP_LISTEN +#endif +#if defined(HAVE_CONST_TCP_CLOSING) && !defined(TCP_CLOSING) +# define TCP_CLOSING TCP_CLOSING +#endif + +static void +inspect_tcpi_options(VALUE ret, uint8_t options) +{ + int sep = '='; + + rb_str_cat2(ret, " options"); +#define INSPECT_TCPI_OPTION(optval, name) \ + if (options & (optval)) { \ + options &= ~(uint8_t)(optval); \ + rb_str_catf(ret, "%c%s", sep, name); \ + sep = ','; \ + } +#ifdef TCPI_OPT_TIMESTAMPS /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_TIMESTAMPS, "TIMESTAMPS"); +#endif +#ifdef TCPI_OPT_SACK /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_SACK, "SACK"); +#endif +#ifdef TCPI_OPT_WSCALE /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_WSCALE, "WSCALE"); +#endif +#ifdef TCPI_OPT_ECN /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_ECN, "ECN"); +#endif +#ifdef TCPI_OPT_ECN_SEEN /* GNU/Linux */ + INSPECT_TCPI_OPTION(TCPI_OPT_ECN_SEEN, "ECN_SEEN"); +#endif +#ifdef TCPI_OPT_SYN_DATA /* GNU/Linux */ + INSPECT_TCPI_OPTION(TCPI_OPT_SYN_DATA, "SYN_DATA"); +#endif +#ifdef TCPI_OPT_TOE /* FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_TOE, "TOE"); +#endif +#undef INSPECT_TCPI_OPTION + + if (options || sep == '=') { + rb_str_catf(ret, "%c%u", sep, options); + } +} + +static void +inspect_tcpi_usec(VALUE ret, const char *prefix, uint32_t t) +{ + rb_str_catf(ret, "%s%u.%06us", prefix, t / 1000000, t % 1000000); +} + +#if !defined __FreeBSD__ && ( \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV || \ + 0) +static void +inspect_tcpi_msec(VALUE ret, const char *prefix, uint32_t t) +{ + rb_str_catf(ret, "%s%u.%03us", prefix, t / 1000, t % 1000); +} +#endif + +#ifdef __FreeBSD__ +# define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t) +# define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_usec(ret, " last_data_recv=", t) +# define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t) +# define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t) +#else +# define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t) +# define inspect_tcpi_ato(ret, t) inspect_tcpi_usec(ret, " ato=", t) +# define inspect_tcpi_last_data_sent(ret, t) inspect_tcpi_msec(ret, " last_data_sent=", t) +# define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_msec(ret, " last_data_recv=", t) +# define inspect_tcpi_last_ack_sent(ret, t) inspect_tcpi_msec(ret, " last_ack_sent=", t) +# define inspect_tcpi_last_ack_recv(ret, t) inspect_tcpi_msec(ret, " last_ack_recv=", t) +# define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t) +# define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t) +# define inspect_tcpi_rcv_rtt(ret, t) inspect_tcpi_usec(ret, " rcv_rtt=", t) +#endif + +static int +inspect_tcp_info(int level, int optname, VALUE data, VALUE ret) +{ + size_t actual_size = RSTRING_LEN(data); + if (sizeof(struct tcp_info) <= actual_size) { + struct tcp_info s; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_STATE + switch (s.tcpi_state) { +# ifdef TCP_ESTABLISHED + case TCP_ESTABLISHED: rb_str_cat_cstr(ret, " state=ESTABLISHED"); break; +# endif +# ifdef TCP_SYN_SENT + case TCP_SYN_SENT: rb_str_cat_cstr(ret, " state=SYN_SENT"); break; +# endif +# ifdef TCP_SYN_RECV + case TCP_SYN_RECV: rb_str_cat_cstr(ret, " state=SYN_RECV"); break; +# endif +# ifdef TCP_FIN_WAIT1 + case TCP_FIN_WAIT1: rb_str_cat_cstr(ret, " state=FIN_WAIT1"); break; +# endif +# ifdef TCP_FIN_WAIT2 + case TCP_FIN_WAIT2: rb_str_cat_cstr(ret, " state=FIN_WAIT2"); break; +# endif +# ifdef TCP_TIME_WAIT + case TCP_TIME_WAIT: rb_str_cat_cstr(ret, " state=TIME_WAIT"); break; +# endif +# ifdef TCP_CLOSE + case TCP_CLOSE: rb_str_cat_cstr(ret, " state=CLOSED"); break; /* RFC 793 uses "CLOSED", not "CLOSE" */ +# endif +# ifdef TCP_CLOSE_WAIT + case TCP_CLOSE_WAIT: rb_str_cat_cstr(ret, " state=CLOSE_WAIT"); break; +# endif +# ifdef TCP_LAST_ACK + case TCP_LAST_ACK: rb_str_cat_cstr(ret, " state=LAST_ACK"); break; +# endif +# ifdef TCP_LISTEN + case TCP_LISTEN: rb_str_cat_cstr(ret, " state=LISTEN"); break; +# endif +# ifdef TCP_CLOSING + case TCP_CLOSING: rb_str_cat_cstr(ret, " state=CLOSING"); break; +# endif + default: rb_str_catf(ret, " state=%u", s.tcpi_state); break; + } +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_CA_STATE + switch (s.tcpi_ca_state) { + case TCP_CA_Open: rb_str_cat_cstr(ret, " ca_state=Open"); break; + case TCP_CA_Disorder: rb_str_cat_cstr(ret, " ca_state=Disorder"); break; + case TCP_CA_CWR: rb_str_cat_cstr(ret, " ca_state=CWR"); break; + case TCP_CA_Recovery: rb_str_cat_cstr(ret, " ca_state=Recovery"); break; + case TCP_CA_Loss: rb_str_cat_cstr(ret, " ca_state=Loss"); break; + default: rb_str_catf(ret, " ca_state=%u", s.tcpi_ca_state); break; + } +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANSMITS + rb_str_catf(ret, " retransmits=%u", s.tcpi_retransmits); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_PROBES + rb_str_catf(ret, " probes=%u", s.tcpi_probes); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_BACKOFF + rb_str_catf(ret, " backoff=%u", s.tcpi_backoff); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_OPTIONS + inspect_tcpi_options(ret, s.tcpi_options); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WSCALE + rb_str_catf(ret, " snd_wscale=%u", s.tcpi_snd_wscale); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_WSCALE + rb_str_catf(ret, " rcv_wscale=%u", s.tcpi_rcv_wscale); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTO + inspect_tcpi_rto(ret, s.tcpi_rto); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_ATO + inspect_tcpi_ato(ret, s.tcpi_ato); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_MSS + rb_str_catf(ret, " snd_mss=%u", s.tcpi_snd_mss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_MSS + rb_str_catf(ret, " rcv_mss=%u", s.tcpi_rcv_mss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_UNACKED + rb_str_catf(ret, " unacked=%u", s.tcpi_unacked); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SACKED + rb_str_catf(ret, " sacked=%u", s.tcpi_sacked); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LOST + rb_str_catf(ret, " lost=%u", s.tcpi_lost); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANS + rb_str_catf(ret, " retrans=%u", s.tcpi_retrans); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_FACKETS + rb_str_catf(ret, " fackets=%u", s.tcpi_fackets); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT + inspect_tcpi_last_data_sent(ret, s.tcpi_last_data_sent); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT + inspect_tcpi_last_ack_sent(ret, s.tcpi_last_ack_sent); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV + inspect_tcpi_last_data_recv(ret, s.tcpi_last_data_recv); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV + inspect_tcpi_last_ack_recv(ret, s.tcpi_last_ack_recv); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_PMTU + rb_str_catf(ret, " pmtu=%u", s.tcpi_pmtu); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SSTHRESH + rb_str_catf(ret, " rcv_ssthresh=%u", s.tcpi_rcv_ssthresh); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTT + inspect_tcpi_rtt(ret, s.tcpi_rtt); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTTVAR + inspect_tcpi_rttvar(ret, s.tcpi_rttvar); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_SSTHRESH + rb_str_catf(ret, " snd_ssthresh=%u", s.tcpi_snd_ssthresh); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + rb_str_catf(ret, " snd_cwnd=%u", s.tcpi_snd_cwnd); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_ADVMSS + rb_str_catf(ret, " advmss=%u", s.tcpi_advmss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_REORDERING + rb_str_catf(ret, " reordering=%u", s.tcpi_reordering); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_RTT + inspect_tcpi_rcv_rtt(ret, s.tcpi_rcv_rtt); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SPACE + rb_str_catf(ret, " rcv_space=%u", s.tcpi_rcv_space); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS + rb_str_catf(ret, " total_retrans=%u", s.tcpi_total_retrans); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WND + rb_str_catf(ret, " snd_wnd=%u", s.tcpi_snd_wnd); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_BWND + rb_str_catf(ret, " snd_bwnd=%u", s.tcpi_snd_bwnd); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_NXT + rb_str_catf(ret, " snd_nxt=%u", s.tcpi_snd_nxt); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_NXT + rb_str_catf(ret, " rcv_nxt=%u", s.tcpi_rcv_nxt); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOE_TID + rb_str_catf(ret, " toe_tid=%u", s.tcpi_toe_tid); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_REXMITPACK + rb_str_catf(ret, " snd_rexmitpack=%u", s.tcpi_snd_rexmitpack); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_OOOPACK + rb_str_catf(ret, " rcv_ooopack=%u", s.tcpi_rcv_ooopack); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_ZEROWIN + rb_str_catf(ret, " snd_zerowin=%u", s.tcpi_snd_zerowin); /* FreeBSD */ +#endif + if (sizeof(struct tcp_info) < actual_size) + rb_str_catf(ret, " (%u bytes too long)", (unsigned)(actual_size - sizeof(struct tcp_info))); + return 1; + } + else { + return 0; + } +} +#endif + #if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */ #if defined(__OpenBSD__) #define RUBY_SOCK_PEERCRED struct sockpeercred @@ -613,7 +1150,7 @@ inspect_peercred(int level, int optname, VALUE data, VALUE ret) RUBY_SOCK_PEERCRED cred; memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED)); rb_str_catf(ret, " pid=%u euid=%u egid=%u", - (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid); + (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid); rb_str_cat2(ret, " (ucred)"); return 1; } @@ -634,14 +1171,14 @@ inspect_local_peercred(int level, int optname, VALUE data, VALUE ret) return 0; rb_str_catf(ret, " version=%u", cred.cr_version); rb_str_catf(ret, " euid=%u", cred.cr_uid); - if (cred.cr_ngroups) { - int i; - const char *sep = " groups="; - for (i = 0; i < cred.cr_ngroups; i++) { - rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]); - sep = ","; - } - } + if (cred.cr_ngroups) { + int i; + const char *sep = " groups="; + for (i = 0; i < cred.cr_ngroups; i++) { + rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]); + sep = ","; + } + } rb_str_cat2(ret, " (xucred)"); return 1; } @@ -679,42 +1216,42 @@ sockopt_inspect(VALUE self) family_id = rsock_intern_family_noprefix(family); if (family_id) - rb_str_catf(ret, " %s", rb_id2name(family_id)); + rb_str_catf(ret, " %s", rb_id2name(family_id)); else rb_str_catf(ret, " family:%d", family); if (level == SOL_SOCKET) { rb_str_cat2(ret, " SOCKET"); - optname_id = rsock_intern_so_optname(optname); - if (optname_id) - rb_str_catf(ret, " %s", rb_id2name(optname_id)); - else - rb_str_catf(ret, " optname:%d", optname); + optname_id = rsock_intern_so_optname(optname); + if (optname_id) + rb_str_catf(ret, " %s", rb_id2name(optname_id)); + else + rb_str_catf(ret, " optname:%d", optname); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN else if (family == AF_UNIX) { - rb_str_catf(ret, " level:%d", level); + rb_str_catf(ret, " level:%d", level); - optname_id = rsock_intern_local_optname(optname); - if (optname_id) - rb_str_catf(ret, " %s", rb_id2name(optname_id)); - else - rb_str_catf(ret, " optname:%d", optname); + optname_id = rsock_intern_local_optname(optname); + if (optname_id) + rb_str_catf(ret, " %s", rb_id2name(optname_id)); + else + rb_str_catf(ret, " optname:%d", optname); } #endif else if (IS_IP_FAMILY(family)) { - level_id = rsock_intern_iplevel(level); - if (level_id) - rb_str_catf(ret, " %s", rb_id2name(level_id)); - else - rb_str_catf(ret, " level:%d", level); - - v = optname_to_sym(level, optname); - if (SYMBOL_P(v)) - rb_str_catf(ret, " %s", rb_id2name(SYM2ID(v))); - else - rb_str_catf(ret, " optname:%d", optname); + level_id = rsock_intern_iplevel(level); + if (level_id) + rb_str_catf(ret, " %s", rb_id2name(level_id)); + else + rb_str_catf(ret, " level:%d", level); + + v = optname_to_sym(level, optname); + if (SYMBOL_P(v)) + rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(v)); + else + rb_str_catf(ret, " optname:%d", optname); } else { rb_str_catf(ret, " level:%d", level); @@ -803,6 +1340,12 @@ sockopt_inspect(VALUE self) # if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */ case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break; # endif +# if defined(IP_MULTICAST_LOOP) /* 4.4BSD, GNU/Linux */ + case IP_MULTICAST_LOOP: inspected = inspect_ipv4_multicast_loop(level, optname, data, ret); break; +# endif +# if defined(IP_MULTICAST_TTL) /* 4.4BSD, GNU/Linux */ + case IP_MULTICAST_TTL: inspected = inspect_ipv4_multicast_ttl(level, optname, data, ret); break; +# endif } break; # endif @@ -841,13 +1384,16 @@ sockopt_inspect(VALUE self) # if defined(TCP_NODELAY) /* POSIX */ case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break; # endif +# if defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO) /* Linux, FreeBSD */ + case TCP_INFO: inspected = inspect_tcp_info(level, optname, data, ret); break; +# endif } break; # endif } break; -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: switch (level) { case 0: @@ -910,14 +1456,22 @@ rsock_init_sockopt(void) rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4); rb_define_method(rb_cSockOpt, "int", sockopt_int, 0); + rb_define_singleton_method(rb_cSockOpt, "byte", sockopt_s_byte, 4); + rb_define_method(rb_cSockOpt, "byte", sockopt_byte, 0); + rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4); rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0); rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2); rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0); + rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_s_ipv4_multicast_ttl, 1); + rb_define_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_ipv4_multicast_ttl, 0); + + rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_s_ipv4_multicast_loop, 1); + rb_define_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_ipv4_multicast_loop, 0); + rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1); rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */ } - |
