On this page
class Socket::AncillaryData
Socket::AncillaryData represents the ancillary data (control information) used by sendmsg and recvmsg system call. It contains socket family, control message (cmsg) level, cmsg type and cmsg data.
Public Class Methods
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)));
}
Creates a new Socket::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>
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)));
}
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>
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)));
}
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>
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;
}
family should be an integer, a string or a symbol.
Socket::AF_INET, “AF_INET”, “INET”, :AF_INET, :INET
Socket::AF_UNIX, “AF_UNIX”, “UNIX”, :AF_UNIX, :UNIX
etc.
cmsg_level should be an integer, a string or a symbol.
Socket::SOL_SOCKET, “SOL_SOCKET”, “SOCKET”, :SOL_SOCKET and :SOCKET
Socket::IPPROTO_IP, “IP” and :IP
Socket::IPPROTO_IPV6, “IPV6” and :IPV6
Socket::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.
Socket::SCM_RIGHTS, “SCM_RIGHTS”, “RIGHTS”, :SCM_RIGHTS, :RIGHTS for SOL_SOCKET
Socket::IP_RECVTTL, “RECVTTL” and :RECVTTL for IPPROTO_IP
Socket::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 "">
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;
}
Creates a new Socket::AncillaryData object which contains file descriptors as data.
p Socket::AncillaryData.unix_rights(STDERR)
#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>
Public Instance Methods
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;
}
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
static VALUE
ancillary_data(VALUE self)
{
VALUE v = rb_attr_get(self, rb_intern("data"));
StringValue(v);
return v;
}
returns the cmsg data as a string.
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").data
#=> ""
static VALUE
ancillary_family_m(VALUE self)
{
return INT2NUM(ancillary_family(self));
}
returns the socket family as an integer.
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").family
#=> 10
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;
}
returns a string which shows ancillarydata in human-readable form.
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").inspect
#=> "#<Socket::AncillaryData: INET6 IPV6 PKTINFO \"\">"
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);
}
Returns the data in ancillarydata as an int.
The size and endian is dependent on the host.
ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, STDERR.fileno)
p ancdata.int #=> 2
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);
}
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>]
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));
}
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]
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);
}
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>
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);
}
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
static VALUE
ancillary_level_m(VALUE self)
{
return INT2NUM(ancillary_level(self));
}
returns the cmsg level as an integer.
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").level
#=> 41
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;
}
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.timestamp p t #=> 2009-02-24 17:35:46 +0900 p t.usec #=> 775581 p t.nsec #=> 775581000 }
}
static VALUE
ancillary_type_m(VALUE self)
{
return INT2NUM(ancillary_type(self));
}
returns the cmsg type as an integer.
p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").type
#=> 2
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"));
}
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
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.