Class: Socket::AncillaryData
Relationships & Source Files | |
Inherits: | Object |
Defined in: | ext/socket/ancdata.c |
Overview
Class Method Summary
-
.int(family, cmsg_level, cmsg_type, integer) ⇒ AncillaryData
Creates a new
AncillaryData
object which contains a int as data. -
.ip_pktinfo(addr, ifindex) ⇒ ancdata
Returns new ancillary data for IP_PKTINFO.
-
.ipv6_pktinfo(addr, ifindex) ⇒ ancdata
Returns new ancillary data for IPV6_PKTINFO.
-
.new(family, cmsg_level, cmsg_type, cmsg_data) ⇒ AncillaryData
constructor
family should be an integer, a string or a symbol.
-
.unix_rights(io1, io2, ...) ⇒ AncillaryData
Creates a new
AncillaryData
object which contains file descriptors as data.
Instance Method Summary
-
#cmsg_is?(level, type) ⇒ Boolean
tests the level and type of ancillarydata.
-
#data ⇒ String
returns the cmsg data as a string.
-
#family ⇒ Integer
returns the socket family as an integer.
-
#inspect ⇒ String
returns a string which shows ancillarydata in human-readable form.
-
#int ⇒ Integer
Returns the data in ancillarydata as an int.
-
#ip_pktinfo ⇒ Array, ...
Extracts addr, ifindex and spec_dst from IP_PKTINFO ancillary data.
-
#ipv6_pktinfo ⇒ Array, ifindex
Extracts addr and ifindex from IPV6_PKTINFO ancillary data.
-
#ipv6_pktinfo_addr ⇒ addr
Extracts addr from IPV6_PKTINFO ancillary data.
-
#ipv6_pktinfo_ifindex ⇒ addr
Extracts ifindex from IPV6_PKTINFO ancillary data.
-
#level ⇒ Integer
returns the cmsg level as an integer.
-
#timestamp ⇒ Time
returns the timestamp as a time object.
-
#type ⇒ Integer
returns the cmsg type as an integer.
-
#unix_rights ⇒ array-of-IOs?
returns the array of
::IO
objects for SCM_RIGHTS control message in UNIX domain socket.
Constructor Details
.new(family, cmsg_level, cmsg_type, cmsg_data) ⇒ AncillaryData
family should be an integer, a string or a symbol.
cmsg_level should be an integer, a string or a symbol.
-
SOL_SOCKET, “SOL_SOCKET”, “SOCKET”,
:SOL_SOCKET
and:SOCKET
-
IPPROTO_IP, “IP” and
:IP
-
IPPROTO_IPV6, “IPV6” and
:IPV6
-
IPPROTO_TCP, “TCP” and
:TCP
-
etc.
cmsg_type should be an integer, a string or a symbol. If a string/symbol is specified, it is interpreted depend on cmsg_level.
-
SCM_RIGHTS, “SCM_RIGHTS”, “RIGHTS”,
:SCM_RIGHTS
,:RIGHTS
for SOL_SOCKET -
IP_RECVTTL, “RECVTTL” and
:RECVTTL
for IPPROTO_IP -
IPV6_PKTINFO, “PKTINFO” and
:PKTINFO
for IPPROTO_IPV6 -
etc.
cmsg_data should be a string.
p Socket::AncillaryData.new(:INET, :TCP, :NODELAY, "")
#=> #<Socket::AncillaryData: INET TCP NODELAY "">
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "")
#=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO "">
# File 'ext/socket/ancdata.c', line 75
static VALUE ancillary_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE vtype, VALUE data) { int family = rsock_family_arg(vfamily); int level = rsock_level_arg(family, vlevel); int type = rsock_cmsg_type_arg(family, level, vtype); StringValue(data); rb_ivar_set(self, rb_intern("family"), INT2NUM(family)); rb_ivar_set(self, rb_intern("level"), INT2NUM(level)); rb_ivar_set(self, rb_intern("type"), INT2NUM(type)); rb_ivar_set(self, rb_intern("data"), data); return self; }
Class Method Details
.int(family, cmsg_level, cmsg_type, integer) ⇒ AncillaryData
Creates a new AncillaryData
object which contains a int as data.
The size and endian is dependent on the host.
require 'socket'
p Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, STDERR.fileno)
#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>
# File 'ext/socket/ancdata.c', line 368
static VALUE ancillary_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE vtype, VALUE integer) { int family = rsock_family_arg(vfamily); int level = rsock_level_arg(family, vlevel); int type = rsock_cmsg_type_arg(family, level, vtype); int i = NUM2INT(integer); return ancdata_new(family, level, type, rb_str_new((char*)&i, sizeof(i))); }
.ip_pktinfo(addr, ifindex) ⇒ ancdata
.ip_pktinfo(addr, ifindex, spec_dst) ⇒ ancdata
ancdata
.ip_pktinfo(addr, ifindex, spec_dst) ⇒ ancdata
Returns new ancillary data for IP_PKTINFO.
If spec_dst is not given, addr is used.
IP_PKTINFO is not standard.
Supported platform: GNU/Linux
addr = Addrinfo.ip("127.0.0.1")
ifindex = 0
spec_dst = Addrinfo.ip("127.0.0.1")
p Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dst)
#=> #<Socket::AncillaryData: INET IP PKTINFO 127.0.0.1 ifindex:0 spec_dst:127.0.0.1>
# File 'ext/socket/ancdata.c', line 422
static VALUE ancillary_s_ip_pktinfo(int argc, VALUE *argv, VALUE self) { VALUE v_addr, v_ifindex, v_spec_dst; unsigned int ifindex; struct sockaddr_in sa; struct in_pktinfo pktinfo; rb_scan_args(argc, argv, "21", &v_addr, &v_ifindex, &v_spec_dst); SockAddrStringValue(v_addr); ifindex = NUM2UINT(v_ifindex); if (NIL_P(v_spec_dst)) v_spec_dst = v_addr; else SockAddrStringValue(v_spec_dst); memset(&pktinfo, 0, sizeof(pktinfo)); memset(&sa, 0, sizeof(sa)); if (RSTRING_LEN(v_addr) != sizeof(sa)) rb_raise(rb_eArgError, "addr size different to AF_INET sockaddr"); memcpy(&sa, RSTRING_PTR(v_addr), sizeof(sa)); if (sa.sin_family != AF_INET) rb_raise(rb_eArgError, "addr is not AF_INET sockaddr"); memcpy(&pktinfo.ipi_addr, &sa.sin_addr, sizeof(pktinfo.ipi_addr)); pktinfo.ipi_ifindex = ifindex; memset(&sa, 0, sizeof(sa)); if (RSTRING_LEN(v_spec_dst) != sizeof(sa)) rb_raise(rb_eArgError, "spec_dat size different to AF_INET sockaddr"); memcpy(&sa, RSTRING_PTR(v_spec_dst), sizeof(sa)); if (sa.sin_family != AF_INET) rb_raise(rb_eArgError, "spec_dst is not AF_INET sockaddr"); memcpy(&pktinfo.ipi_spec_dst, &sa.sin_addr, sizeof(pktinfo.ipi_spec_dst)); return ancdata_new(AF_INET, IPPROTO_IP, IP_PKTINFO, rb_str_new((char *)&pktinfo, sizeof(pktinfo))); }
.ipv6_pktinfo(addr, ifindex) ⇒ ancdata
Returns new ancillary data for IPV6_PKTINFO.
IPV6_PKTINFO is defined by RFC 3542.
addr = Addrinfo.ip("::1")
ifindex = 0
p Socket::AncillaryData.ipv6_pktinfo(addr, ifindex)
#=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO ::1 ifindex:0>
# File 'ext/socket/ancdata.c', line 535
static VALUE ancillary_s_ipv6_pktinfo(VALUE self, VALUE v_addr, VALUE v_ifindex) { unsigned int ifindex; struct sockaddr_in6 sa; struct in6_pktinfo pktinfo; SockAddrStringValue(v_addr); ifindex = NUM2UINT(v_ifindex); memset(&pktinfo, 0, sizeof(pktinfo)); memset(&sa, 0, sizeof(sa)); if (RSTRING_LEN(v_addr) != sizeof(sa)) rb_raise(rb_eArgError, "addr size different to AF_INET6 sockaddr"); memcpy(&sa, RSTRING_PTR(v_addr), sizeof(sa)); if (sa.sin6_family != AF_INET6) rb_raise(rb_eArgError, "addr is not AF_INET6 sockaddr"); memcpy(&pktinfo.ipi6_addr, &sa.sin6_addr, sizeof(pktinfo.ipi6_addr)); pktinfo.ipi6_ifindex = ifindex; return ancdata_new(AF_INET6, IPPROTO_IPV6, IPV6_PKTINFO, rb_str_new((char *)&pktinfo, sizeof(pktinfo))); }
.unix_rights(io1, io2, ...) ⇒ AncillaryData
Creates a new AncillaryData
object which contains file descriptors as data.
p Socket::AncillaryData.unix_rights(STDERR)
#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>
# File 'ext/socket/ancdata.c', line 191
static VALUE ancillary_s_unix_rights(int argc, VALUE *argv, VALUE klass) { VALUE result, str, ary; int i; ary = rb_ary_new(); for (i = 0 ; i < argc; i++) { VALUE obj = argv[i]; if (!RB_TYPE_P(obj, T_FILE)) { rb_raise(rb_eTypeError, "IO expected"); } rb_ary_push(ary, obj); } str = rb_str_buf_new(sizeof(int) * argc); for (i = 0 ; i < argc; i++) { VALUE obj = RARRAY_AREF(ary, i); rb_io_t *fptr; int fd; GetOpenFile(obj, fptr); fd = fptr->fd; rb_str_buf_cat(str, (char *)&fd, sizeof(int)); } result = ancdata_new(AF_UNIX, SOL_SOCKET, SCM_RIGHTS, str); rb_ivar_set(result, rb_intern("unix_rights"), ary); return result; }
Instance Method Details
#cmsg_is?(level, type) ⇒ Boolean
tests the level and type of ancillarydata.
ancdata = Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "")
ancdata.cmsg_is?(Socket::IPPROTO_IPV6, Socket::IPV6_PKTINFO) #=> true
ancdata.cmsg_is?(:IPV6, :PKTINFO) #=> true
ancdata.cmsg_is?(:IP, :PKTINFO) #=> false
ancdata.cmsg_is?(:SOCKET, :RIGHTS) #=> false
# File 'ext/socket/ancdata.c', line 1092
static VALUE ancillary_cmsg_is_p(VALUE self, VALUE vlevel, VALUE vtype) { int family = ancillary_family(self); int level = rsock_level_arg(family, vlevel); int type = rsock_cmsg_type_arg(family, level, vtype); if (ancillary_level(self) == level && ancillary_type(self) == type) return Qtrue; else return Qfalse; }
#data ⇒ String
# File 'ext/socket/ancdata.c', line 173
static VALUE ancillary_data(VALUE self) { VALUE v = rb_attr_get(self, rb_intern("data")); StringValue(v); return v; }
#family ⇒ Integer
# File 'ext/socket/ancdata.c', line 114
static VALUE ancillary_family_m(VALUE self) { return INT2NUM(ancillary_family(self)); }
#inspect ⇒ String
# File 'ext/socket/ancdata.c', line 954
static VALUE ancillary_inspect(VALUE self) { VALUE ret; int family, level, type; VALUE data; ID family_id, level_id, type_id; VALUE vtype; int inspected; family = ancillary_family(self); level = ancillary_level(self); type = ancillary_type(self); data = ancillary_data(self); ret = rb_sprintf("#<%s:", rb_obj_classname(self)); family_id = rsock_intern_family_noprefix(family); if (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"); type_id = rsock_intern_scm_optname(type); if (type_id) rb_str_catf(ret, " %s", rb_id2name(type_id)); else rb_str_catf(ret, " cmsg_type:%d", type); } 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, " cmsg_level:%d", level); vtype = ip_cmsg_type_to_sym(level, type); if (SYMBOL_P(vtype)) rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(vtype)); else rb_str_catf(ret, " cmsg_type:%d", type); } else { rb_str_catf(ret, " cmsg_level:%d", level); rb_str_catf(ret, " cmsg_type:%d", type); } inspected = 0; if (level == SOL_SOCKET) family = AF_UNSPEC; switch (family) { case AF_UNSPEC: switch (level) { # if defined(SOL_SOCKET) case SOL_SOCKET: switch (type) { # if defined(SCM_TIMESTAMP) /* GNU/Linux, FreeBSD, NetBSD, OpenBSD, MacOS X, Solaris */ case SCM_TIMESTAMP: inspected = inspect_timeval_as_abstime(level, type, data, ret); break; # endif # if defined(SCM_TIMESTAMPNS) /* GNU/Linux */ case SCM_TIMESTAMPNS: inspected = inspect_timespec_as_abstime(level, type, data, ret); break; # endif # if defined(SCM_BINTIME) /* FreeBSD */ case SCM_BINTIME: inspected = inspect_bintime_as_abstime(level, type, data, ret); break; # endif # if defined(SCM_RIGHTS) /* 4.4BSD */ case SCM_RIGHTS: inspected = anc_inspect_socket_rights(level, type, data, ret); break; # endif # if defined(SCM_CREDENTIALS) /* GNU/Linux */ case SCM_CREDENTIALS: inspected = anc_inspect_passcred_credentials(level, type, data, ret); break; # endif # if defined(INSPECT_SCM_CREDS) /* NetBSD */ case SCM_CREDS: inspected = anc_inspect_socket_creds(level, type, data, ret); break; # endif } break; # endif } break; case AF_INET: #ifdef INET6 case AF_INET6: #endif switch (level) { # if defined(IPPROTO_IP) case IPPROTO_IP: switch (type) { # if defined(IP_RECVDSTADDR) /* 4.4BSD */ case IP_RECVDSTADDR: inspected = anc_inspect_ip_recvdstaddr(level, type, data, ret); break; # endif # if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST) /* GNU/Linux */ case IP_PKTINFO: inspected = anc_inspect_ip_pktinfo(level, type, data, ret); break; # endif } break; # endif # if defined(IPPROTO_IPV6) case IPPROTO_IPV6: switch (type) { # if defined(IPV6_PKTINFO) && defined(HAVE_TYPE_STRUCT_IN6_PKTINFO) /* RFC 3542 */ case IPV6_PKTINFO: inspected = anc_inspect_ipv6_pktinfo(level, type, data, ret); break; # endif } break; # endif } break; } if (!inspected) { rb_str_cat2(ret, " "); rb_str_append(ret, rb_str_dump(data)); } rb_str_cat2(ret, ">"); return ret; }
#int ⇒ Integer
# File 'ext/socket/ancdata.c', line 389
static VALUE ancillary_int(VALUE self) { VALUE data; int i; data = ancillary_data(self); 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)); return INT2NUM(i); }
#ip_pktinfo ⇒ Array
, ...
Extracts addr, ifindex and spec_dst from IP_PKTINFO ancillary data.
IP_PKTINFO is not standard.
Supported platform: GNU/Linux
addr = Addrinfo.ip("127.0.0.1")
ifindex = 0
spec_dest = Addrinfo.ip("127.0.0.1")
ancdata = Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dest)
p ancdata.ip_pktinfo
#=> [#<Addrinfo: 127.0.0.1>, 0, #<Addrinfo: 127.0.0.1>]
# File 'ext/socket/ancdata.c', line 485
static VALUE ancillary_ip_pktinfo(VALUE self) { int level, type; VALUE data; struct in_pktinfo pktinfo; struct sockaddr_in sa; VALUE v_spec_dst, v_addr; level = ancillary_level(self); type = ancillary_type(self); data = ancillary_data(self); if (level != IPPROTO_IP || type != IP_PKTINFO || RSTRING_LEN(data) != sizeof(struct in_pktinfo)) { rb_raise(rb_eTypeError, "IP_PKTINFO ancillary data expected"); } memcpy(&pktinfo, RSTRING_PTR(data), sizeof(struct in_pktinfo)); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; memcpy(&sa.sin_addr, &pktinfo.ipi_addr, sizeof(sa.sin_addr)); v_addr = rsock_addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET, 0, 0, Qnil, Qnil); sa.sin_family = AF_INET; memcpy(&sa.sin_addr, &pktinfo.ipi_spec_dst, sizeof(sa.sin_addr)); v_spec_dst = rsock_addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET, 0, 0, Qnil, Qnil); return rb_ary_new3(3, v_addr, UINT2NUM(pktinfo.ipi_ifindex), v_spec_dst); }
#ipv6_pktinfo ⇒ Array
, ifindex
Extracts addr and ifindex from IPV6_PKTINFO ancillary data.
IPV6_PKTINFO is defined by RFC 3542.
addr = Addrinfo.ip("::1")
ifindex = 0
ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex)
p ancdata.ipv6_pktinfo #=> [#<Addrinfo: ::1>, 0]
# File 'ext/socket/ancdata.c', line 603
static VALUE ancillary_ipv6_pktinfo(VALUE self) { struct in6_pktinfo pktinfo; struct sockaddr_in6 sa; VALUE v_addr; extract_ipv6_pktinfo(self, &pktinfo, &sa); v_addr = rsock_addrinfo_new((struct sockaddr *)&sa, (socklen_t)sizeof(sa), PF_INET6, 0, 0, Qnil, Qnil); return rb_ary_new3(2, v_addr, UINT2NUM(pktinfo.ipi6_ifindex)); }
#ipv6_pktinfo_addr ⇒ addr
Extracts addr from IPV6_PKTINFO ancillary data.
IPV6_PKTINFO is defined by RFC 3542.
addr = Addrinfo.ip("::1")
ifindex = 0
ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex)
p ancdata.ipv6_pktinfo_addr #=> #<Addrinfo: ::1>
# File 'ext/socket/ancdata.c', line 633
static VALUE ancillary_ipv6_pktinfo_addr(VALUE self) { struct in6_pktinfo pktinfo; struct sockaddr_in6 sa; extract_ipv6_pktinfo(self, &pktinfo, &sa); return rsock_addrinfo_new((struct sockaddr *)&sa, (socklen_t)sizeof(sa), PF_INET6, 0, 0, Qnil, Qnil); }
#ipv6_pktinfo_ifindex ⇒ addr
Extracts ifindex from IPV6_PKTINFO ancillary data.
IPV6_PKTINFO is defined by RFC 3542.
addr = Addrinfo.ip("::1")
ifindex = 0
ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex)
p ancdata.ipv6_pktinfo_ifindex #=> 0
# File 'ext/socket/ancdata.c', line 660
static VALUE ancillary_ipv6_pktinfo_ifindex(VALUE self) { struct in6_pktinfo pktinfo; struct sockaddr_in6 sa; extract_ipv6_pktinfo(self, &pktinfo, &sa); return UINT2NUM(pktinfo.ipi6_ifindex); }
#level ⇒ Integer
# File 'ext/socket/ancdata.c', line 136
static VALUE ancillary_level_m(VALUE self) { return INT2NUM(ancillary_level(self)); }
#timestamp ⇒ Time
returns the timestamp as a time object.
ancillarydata should be one of following type:
-
SOL_SOCKET/SCM_TIMESTAMP (microsecond) GNU/Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOS X
-
SOL_SOCKET/SCM_TIMESTAMPNS (nanosecond) GNU/Linux
-
SOL_SOCKET/SCM_BINTIME (2**(-64) second) FreeBSD
Addrinfo.udp(“127.0.0.1”, 0).bind {|s1|
Addrinfo.udp("127.0.0.1", 0).bind {|s2| s1.setsockopt(:SOCKET, :TIMESTAMP, true) s2.send "a", 0, s1.local_address ctl = s1.recvmsg.last p ctl #=> #<Socket::AncillaryData: INET SOCKET TIMESTAMP 2009-02-24 17:35:46.775581> t = ctl. p t #=> 2009-02-24 17:35:46 +0900 p t.usec #=> 775581 p t.nsec #=> 775581000 }
}
# File 'ext/socket/ancdata.c', line 300
static VALUE ancillary_timestamp(VALUE self) { int level, type; VALUE data; VALUE result = Qnil; level = ancillary_level(self); type = ancillary_type(self); data = ancillary_data(self); # ifdef SCM_TIMESTAMP if (level == SOL_SOCKET && type == SCM_TIMESTAMP && RSTRING_LEN(data) == sizeof(struct timeval)) { struct timeval tv; memcpy((char*)&tv, RSTRING_PTR(data), sizeof(tv)); result = rb_time_new(tv.tv_sec, tv.tv_usec); } # endif # ifdef SCM_TIMESTAMPNS if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS && RSTRING_LEN(data) == sizeof(struct timespec)) { struct timespec ts; memcpy((char*)&ts, RSTRING_PTR(data), sizeof(ts)); result = rb_time_nano_new(ts.tv_sec, ts.tv_nsec); } # endif #define add(x,y) (rb_funcall((x), '+', 1, (y))) #define mul(x,y) (rb_funcall((x), '*', 1, (y))) #define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) # ifdef SCM_BINTIME if (level == SOL_SOCKET && type == SCM_BINTIME && RSTRING_LEN(data) == sizeof(struct bintime)) { struct bintime bt; VALUE d, timev; memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt)); d = ULL2NUM(0x100000000ULL); d = mul(d,d); timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d)); result = rb_time_num_new(timev, Qnil); } # endif if (result == Qnil) rb_raise(rb_eTypeError, "timestamp ancillary data expected"); return result; }
#type ⇒ Integer
# File 'ext/socket/ancdata.c', line 158
static VALUE ancillary_type_m(VALUE self) { return INT2NUM(ancillary_type(self)); }
#unix_rights ⇒ array
-of
-IOs
?
returns the array of ::IO
objects for SCM_RIGHTS control message in UNIX domain socket.
The class of the ::IO
objects in the array is ::IO
or ::Socket
.
The array is attached to ancillarydata when it is instantiated. For example, BasicSocket#recvmsg attach the array when receives a SCM_RIGHTS control message and :scm_rights=>true option is given.
# recvmsg needs :scm_rights=>true for unix_rights
s1, s2 = UNIXSocket.pair
p s1 #=> #<UNIXSocket:fd 3>
s1.sendmsg "stdin and a socket", 0, nil, Socket::AncillaryData.unix_rights(STDIN, s1)
_, _, _, ctl = s2.recvmsg(:scm_rights=>true)
p ctl #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7>
p ctl.unix_rights #=> [#<IO:fd 6>, #<Socket:fd 7>]
p File.identical?(STDIN, ctl.unix_rights[0]) #=> true
p File.identical?(s1, ctl.unix_rights[1]) #=> true
# If :scm_rights=>true is not given, unix_rights returns nil
s1, s2 = UNIXSocket.pair
s1.sendmsg "stdin and a socket", 0, nil, Socket::AncillaryData.unix_rights(STDIN, s1)
_, _, _, ctl = s2.recvmsg
p ctl #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7>
p ctl.unix_rights #=> nil
# File 'ext/socket/ancdata.c', line 257
static VALUE ancillary_unix_rights(VALUE self) { int level, type; level = ancillary_level(self); type = ancillary_type(self); if (level != SOL_SOCKET || type != SCM_RIGHTS) rb_raise(rb_eTypeError, "SCM_RIGHTS ancillary data expected"); return rb_attr_get(self, rb_intern("unix_rights")); }