1. Socket::
  2. AncillaryData

class Socket::AncillaryData

Socket::AncillaryData represents the ancillary data (control information) used by sendmsg and recvmsg system call. It contains socketfamily, control message (cmsg)level, cmsgtype and cmsgdata.

Public Class Methods

Source
static VALUEancillary_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 newSocket::AncillaryData object which contains a int as data.

The size and endian is dependent on the host.

require'socket'pSocket::AncillaryData.int(:UNIX,:SOCKET,:RIGHTS,STDERR.fileno)#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>
Source
static VALUEancillary_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 =0spec_dst =Addrinfo.ip("127.0.0.1")pSocket::AncillaryData.ip_pktinfo(addr,ifindex,spec_dst)#=> #<Socket::AncillaryData: INET IP PKTINFO 127.0.0.1 ifindex:0 spec_dst:127.0.0.1>
Source
static VALUEancillary_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 =0pSocket::AncillaryData.ipv6_pktinfo(addr,ifindex)#=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO ::1 ifindex:0>
Source
static VALUEancillary_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 oncmsg_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.

pSocket::AncillaryData.new(:INET,:TCP,:NODELAY,"")#=> #<Socket::AncillaryData: INET TCP NODELAY "">pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"")#=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO "">
Source
static VALUEancillary_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 newSocket::AncillaryData object which contains file descriptors as data.

pSocket::AncillaryData.unix_rights(STDERR)#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2>

Public Instance Methods

Source
static VALUEancillary_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 ofancillarydata.

ancdata =Socket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"")ancdata.cmsg_is?(Socket::IPPROTO_IPV6,Socket::IPV6_PKTINFO)#=> trueancdata.cmsg_is?(:IPV6,:PKTINFO)#=> trueancdata.cmsg_is?(:IP,:PKTINFO)#=> falseancdata.cmsg_is?(:SOCKET,:RIGHTS)#=> false
Source
static VALUEancillary_data(VALUE self){    VALUE v = rb_attr_get(self, rb_intern("data"));    StringValue(v);    return v;}

returns the cmsg data as a string.

pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"").data#=> ""
Source
static VALUEancillary_family_m(VALUE self){    return INT2NUM(ancillary_family(self));}

returns the socket family as an integer.

pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"").family#=> 10
Source
static VALUEancillary_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.

pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"").inspect#=> "#<Socket::AncillaryData: INET6 IPV6 PKTINFO \"\">"
Source
static VALUEancillary_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 inancillarydata as an int.

The size and endian is dependent on the host.

ancdata =Socket::AncillaryData.int(:UNIX,:SOCKET,:RIGHTS,STDERR.fileno)pancdata.int#=> 2
Source
static VALUEancillary_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 =0spec_dest =Addrinfo.ip("127.0.0.1")ancdata =Socket::AncillaryData.ip_pktinfo(addr,ifindex,spec_dest)pancdata.ip_pktinfo#=> [#<Addrinfo: 127.0.0.1>, 0, #<Addrinfo: 127.0.0.1>]
Source
static VALUEancillary_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 =0ancdata =Socket::AncillaryData.ipv6_pktinfo(addr,ifindex)pancdata.ipv6_pktinfo#=> [#<Addrinfo: ::1>, 0]
Source
static VALUEancillary_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 =0ancdata =Socket::AncillaryData.ipv6_pktinfo(addr,ifindex)pancdata.ipv6_pktinfo_addr#=> #<Addrinfo: ::1>
Source
static VALUEancillary_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 =0ancdata =Socket::AncillaryData.ipv6_pktinfo(addr,ifindex)pancdata.ipv6_pktinfo_ifindex#=> 0
Source
static VALUEancillary_level_m(VALUE self){    return INT2NUM(ancillary_level(self));}

returns the cmsg level as an integer.

pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"").level#=> 41
Source
static VALUEancillary_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_addressctl =s1.recvmsg.lastpctl#=> #<Socket::AncillaryData: INET SOCKET TIMESTAMP 2009-02-24 17:35:46.775581>t =ctl.timestamppt#=> 2009-02-24 17:35:46 +0900pt.usec#=> 775581pt.nsec#=> 775581000}

    }

Source
static VALUEancillary_type_m(VALUE self){    return INT2NUM(ancillary_type(self));}

returns the cmsg type as an integer.

pSocket::AncillaryData.new(:INET6,:IPV6,:PKTINFO,"").type#=> 2
Source
static VALUEancillary_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 ofIO objects for SCM_RIGHTS control message in UNIX domain socket.

The class of theIO objects in the array isIO orSocket.

The array is attached toancillarydata 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_rightss1,s2 =UNIXSocket.pairps1#=> #<UNIXSocket:fd 3>s1.sendmsg"stdin and a socket",0,nil,Socket::AncillaryData.unix_rights(STDIN,s1)_,_,_,ctl =s2.recvmsg(:scm_rights=>true)pctl#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7>pctl.unix_rights#=> [#<IO:fd 6>, #<Socket:fd 7>]pFile.identical?(STDIN,ctl.unix_rights[0])#=> truepFile.identical?(s1,ctl.unix_rights[1])#=> true# If :scm_rights=>true is not given, unix_rights returns nils1,s2 =UNIXSocket.pairs1.sendmsg"stdin and a socket",0,nil,Socket::AncillaryData.unix_rights(STDIN,s1)_,_,_,ctl =s2.recvmsgpctl#=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7>pctl.unix_rights#=> nil