Sendmsg/recvmsg

socket e$B$G!"e(Bsendmsg, recvmsg e$B%7%9%F%`%3!<%k$r;H$($k$h$&$K$9$ke(B
e$B$N$O$I$&$G$7$g$&$+!#e(B

sendmsg, recvmsg e$B$Oe(B POSIX e$B$GDj5A$5$l$F$$$^$9!#$=$7$F!“e(BIPv6
e$B$G$Oe(B sendmsg, recvmsg e$B$NJd=u%G!<%?$r;H$&5!G=$,e(B RFC
e$B$GDj5A$5e(B
e$B$l$F$$$^$9!#$?$H$($P%Q%1%C%H$,FO$$$?%”%I%l%9$rF@$kEy$N5!G=$,e(B
e$B$"$j$^$9!#e(B

e$BJd=u%G!<%?$Oe(B OS e$B$G$$$m$$$m$J5!G=$rDs6!$9$k$N$KJXMx$G!"$?$H$(e(B
e$B$P!“e(BLinux e$B$de(B 4.4BSD e$B$K$b8GM-$N5!G=$,$”$j$^$9!#e(B
(IPv4 e$B$G%Q%1%C%H$,FO$$$?%"%I%l%9$rF@$k$H$+!#e(BLinux e$B$Ne(B ip(7)
e$B$H$+e(B 4.4BSD e$B$Ne(B ip(4) e$B$H$+$K:$C$F$$$^$9!#e(BIP
e$B0J30$K$b5!G=$,$"e(B
e$B$j$^$9$,e(B)

UDP e$B%5!<%P$G%Q%1%C%H$,FO$$$?%"%I%l%9$rF@$k$N$,I,MW$G$"$k$3$He(B
e$B$Oe(B Socket.ip_address_list e$B$N$H$-$K=R$Y$^$7$?$,!"e(Bsendmsg,
recvmsg e$B$r;H$&$H!“e(BIPv6 e$B$K$D$$$F$O!“8D!9$Ne(B IP
e$B%”%I%l%9Kh$K%=e(B
e$B%1%C%H$r:n$i$J$/$F$bFO$$$?%”%I%l%9$+$iJV;v$rJV$9$3$H$,$G$-$^e(B
e$B$9!#$3$l$K$h$j!“e(Bdhcp e$B$J$I$GF0E*$K%”%I%l%9$,A}$($k>u67$r4JC1e(B
e$B$K07$($^$9!#e(B

e$B$J$*!“Jd=u%G!<%?$K$Oe(B Socket::AncillaryData e$B$H$$$&%/%i%9$r?7e(B
e$B@_$7$F$”$j$^$9!#$3$l$Oe(B inspect e$B$GCf?H$r8+$d$9$/I=<($9$k$J$Ie(B
Socket::Option e$B$HF1MM$JM}M3$G$9!#e(B

% svn diff --diff-cmd diff -x ‘-u -p’
Index: ext/.document

— ext/.document (revision 21891)
+++ ext/.document (working copy)
@@ -20,6 +20,7 @@ socket/udpsocket.c
socket/unixsocket.c
socket/unixserver.c
socket/socket.c
+socket/ancdata.c
socket/constants.c
socket/lib/socket.rb
stringio/stringio.c
Index: ext/socket/rubysocket.h

— ext/socket/rubysocket.h (revision 21891)
+++ ext/socket/rubysocket.h (working copy)
@@ -189,6 +189,7 @@ int socktype_arg(VALUE type);
int level_arg(VALUE level);
int optname_arg(int level, VALUE optname);
int shutdown_how_arg(VALUE how);
+int cmsg_type_arg(int level, VALUE optname);

int rb_getaddrinfo(const char *node, const char *service, const struct
addrinfo *hints, struct addrinfo **res);
int rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char
*host, size_t hostlen, char *serv, size_t servlen, int flags);
@@ -251,6 +252,7 @@ void Init_udpsocket(void);
void Init_unixsocket(void);
void Init_unixserver(void);
void Init_socket_constants(void);
+void Init_ancdata(void);
void Init_addrinfo(void);
void Init_socket_init(void);

Index: ext/socket/init.c

— ext/socket/init.c (revision 21891)
+++ ext/socket/init.c (working copy)
@@ -519,6 +519,7 @@ Init_socket_init()
Init_udpsocket();
Init_unixsocket();
Init_unixserver();

  • Init_ancdata();
    Init_addrinfo();
    Init_socket_constants();
    }
    Index: ext/socket/constants.c
    ===================================================================
    — ext/socket/constants.c (revision 21891)
    +++ ext/socket/constants.c (working copy)
    @@ -92,6 +92,25 @@ shutdown_how_arg(VALUE how)
    return constant_arg(how, shutdown_how_to_int, “unknown shutdown
    argument”);
    }

+int
+cmsg_type_arg(int level, VALUE optname)
+{

  • switch (level) {
  •  case SOL_SOCKET:
    
  •    return constant_arg(optname, scm_optname_to_int, "unknown UNIX 
    

control message");

  •  case IPPROTO_IP:
    
  •    return constant_arg(optname, ip_optname_to_int, "unknown IP 
    

control message");

  •  case IPPROTO_IPV6:
    
  •    return constant_arg(optname, ipv6_optname_to_int, "unknown IPv6 
    

control message");

  •  case IPPROTO_TCP:
    
  •    return constant_arg(optname, tcp_optname_to_int, "unknown TCP 
    

control message");

  •  case IPPROTO_UDP:
    
  •    return constant_arg(optname, udp_optname_to_int, "unknown UDP 
    

control message");

  •  default:
    
  •    return NUM2INT(optname);
    
  • }
    +}

static void
sock_define_const(const char *name, int value, VALUE mConst)
{
Index: ext/socket/depend

— ext/socket/depend (revision 21891)
+++ ext/socket/depend (working copy)
@@ -13,6 +13,7 @@ sockssocket.o: sockssocket.c $(SOCK_HEAD
udpsocket.o: udpsocket.c $(SOCK_HEADERS)
unixsocket.o: unixsocket.c $(SOCK_HEADERS)
unixserver.o: unixserver.c $(SOCK_HEADERS)
+ancdata.o: ancdata.c $(SOCK_HEADERS)
raddrinfo.o: raddrinfo.c $(SOCK_HEADERS)

getnameinfo.o: getnameinfo.c $(arch_hdrdir)/ruby/config.h addrinfo.h
sockport.h
Index: ext/socket/mkconstants.rb

— ext/socket/mkconstants.rb (revision 21891)
+++ ext/socket/mkconstants.rb (working copy)
@@ -249,11 +249,19 @@ def_name_to_int(“ipv6_optname_to_int”, /
def_name_to_int(“tcp_optname_to_int”, /\ATCP_/, “TCP_”)
def_name_to_int(“udp_optname_to_int”, /\AUDP_/, “UDP_”)
def_name_to_int(“shutdown_how_to_int”, /\ASHUT_/, “SHUT_”)
+def_name_to_int(“scm_optname_to_int”, /\ASCM_/, “SCM_”)

def_intern(‘intern_family’, /\AAF_/)
def_intern(‘intern_protocol_family’, /\APF_/)
def_intern(‘intern_socktype’, /\ASOCK_/)
def_intern(‘intern_ipproto’, /\AIPPROTO_/)
+def_intern(‘intern_level’, /\A(SOL_SOCKET\z|IPPROTO_)/,
/\A(SOL_|IPPROTO_)/)
+#def_intern(‘intern_so_optname’, /\ASO_/, “SO_”)
+def_intern(‘intern_ip_optname’, /\AIP_/, “IP_”)
+def_intern(‘intern_ipv6_optname’, /\AIPV6_/, “IPV6_”)
+def_intern(‘intern_tcp_optname’, /\ATCP_/, “TCP_”)
+def_intern(‘intern_udp_optname’, /\AUDP_/, “UDP_”)
+def_intern(‘intern_scm_optname’, /\ASCM_/, “SCM_”)

result = ERB.new(<<‘EOS’, nil, ‘%’).result(binding)
/* autogenerated file */
@@ -475,6 +483,35 @@ IP_DROP_MEMBERSHIP
IP_DEFAULT_MULTICAST_TTL
IP_DEFAULT_MULTICAST_LOOP
IP_MAX_MEMBERSHIPS
+IP_ROUTER_ALERT
+IP_PKTINFO
+IP_PKTOPTIONS
+IP_MTU_DISCOVER
+IP_RECVERR
+IP_RECVTOS
+IP_MTU
+IP_FREEBIND
+IP_IPSEC_POLICY
+IP_XFRM_POLICY
+IP_PASSSEC
+IP_PMTUDISC_DONT
+IP_PMTUDISC_WANT
+IP_PMTUDISC_DO
+IP_UNBLOCK_SOURCE
+IP_BLOCK_SOURCE
+IP_ADD_SOURCE_MEMBERSHIP
+IP_DROP_SOURCE_MEMBERSHIP
+IP_MSFILTER
+
+MCAST_JOIN_GROUP
+MCAST_BLOCK_SOURCE
+MCAST_UNBLOCK_SOURCE
+MCAST_LEAVE_GROUP
+MCAST_JOIN_SOURCE_GROUP
+MCAST_LEAVE_SOURCE_GROUP
+MCAST_MSFILTER
+MCAST_EXCLUDE
+MCAST_INCLUDE

SO_DEBUG
SO_REUSEADDR
@@ -610,3 +647,6 @@ INET_ADDRSTRLEN
INET6_ADDRSTRLEN

SOMAXCONN
+
+SCM_RIGHTS
+SCM_CREDENTIALS
Index: ext/socket/extconf.rb

— ext/socket/extconf.rb (revision 21891)
+++ ext/socket/extconf.rb (working copy)
@@ -254,6 +254,7 @@ $objs = [
“udpsocket.#{$OBJEXT}”,
“unixsocket.#{$OBJEXT}”,
“unixserver.#{$OBJEXT}”,

  • “ancdata.#{$OBJEXT}”,
    “raddrinfo.#{$OBJEXT}”
    ]

Index: ext/socket/ancdata.c

— ext/socket/ancdata.c (revision 0)
+++ ext/socket/ancdata.c (revision 0)
@@ -0,0 +1,1070 @@
+#include “rubysocket.h”
+
+#if defined(HAVE_ST_MSG_CONTROL)
+static VALUE rb_cAncillaryData;
+
+static VALUE
+constant_to_sym(int constant, ID (*intern_const)(int))
+{

  • ID name = intern_const(constant);
  • if (name) {
  •    return ID2SYM(name);
    
  • }
  • return INT2NUM(constant);
    +}

+static VALUE
+cmsg_type_to_sym(int level, int cmsg_type)
+{

  • switch (level) {
  •  case SOL_SOCKET:
    
  •    return constant_to_sym(cmsg_type, intern_scm_optname);
    
  •  case IPPROTO_IP:
    
  •    return constant_to_sym(cmsg_type, intern_ip_optname);
    
  •  case IPPROTO_IPV6:
    
  •    return constant_to_sym(cmsg_type, intern_ipv6_optname);
    
  •  case IPPROTO_TCP:
    
  •    return constant_to_sym(cmsg_type, intern_tcp_optname);
    
  •  case IPPROTO_UDP:
    
  •    return constant_to_sym(cmsg_type, intern_udp_optname);
    
  •  default:
    
  •    return INT2NUM(cmsg_type);
    
  • }
    +}

+/*

    • call-seq:
    • Socket::AncillaryData.new(cmsg_level, cmsg_type, cmsg_data) ->
      ancillarydata
    • 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
      • etc.
    • cmsg_type should be an integer, a string or a symbol.
    • If a string/symbol is specified, it is interepreted 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(:IPV6, :PKTINFO, “”)
    • #=> #<Socket::AncillaryData: IPV6 PKTINFO “”>
  • */
    +static VALUE
    +ancillary_initialize(VALUE self, VALUE vlevel, VALUE vtype, VALUE data)
    +{
  • int level;
  • StringValue(data);
  • level = level_arg(vlevel);
  • rb_ivar_set(self, rb_intern(“level”), INT2NUM(level));
  • rb_ivar_set(self, rb_intern(“type”), INT2NUM(cmsg_type_arg(level,
    vtype)));
  • rb_ivar_set(self, rb_intern(“data”), data);
  • return self;
    +}

+static VALUE
+ancdata_new(int level, int type, VALUE data)
+{

  • NEWOBJ(obj, struct RObject);
  • OBJSETUP(obj, rb_cAncillaryData, T_OBJECT);
  • StringValue(data);
  • ancillary_initialize((VALUE)obj, INT2NUM(level), INT2NUM(type),
    data);
  • return (VALUE)obj;
    +}

+static int
+ancillary_level(VALUE self)
+{

  • VALUE v = rb_attr_get(self, rb_intern(“level”));
  • return NUM2INT(v);
    +}

+/*

    • call-seq:
    • ancillarydata.level => integer
    • returns the cmsg level as an integer.
    • p Socket::AncillaryData.new(:IPV6, :PKTINFO, “”).level
    • #=> 41
  • */
    +static VALUE
    +ancillary_level_m(VALUE self)
    +{
  • return INT2NUM(ancillary_level(self));
    +}

+static int
+ancillary_type(VALUE self)
+{

  • VALUE v = rb_attr_get(self, rb_intern(“type”));
  • return NUM2INT(v);
    +}

+/*

    • call-seq:
    • ancillarydata.type => integer
    • returns the cmsg type as an integer.
    • p Socket::AncillaryData.new(:IPV6, :PKTINFO, “”).type
    • #=> 2
  • */
    +static VALUE
    +ancillary_type_m(VALUE self)
    +{
  • return INT2NUM(ancillary_type(self));
    +}

+/*

    • call-seq:
    • ancillarydata.data => string
    • returns the cmsg data as a string.
    • p Socket::AncillaryData.new(:IPV6, :PKTINFO, “”).data
    • #=> “”
  • */
    +static VALUE
    +ancillary_data(VALUE self)
    +{
  • VALUE v = rb_attr_get(self, rb_intern(“data”));
  • StringValue(v);
  • return v;
    +}

+/*

    • call-seq:
    • Socket::AncillaryData.int(cmsg_level, cmsg_type, integer) =>
      ancillarydata
    • Creates a new Socket::AncillaryData object which contains a int as
      data.
    • The size and endian is dependent on the host.
    • p Socket::AncillaryData.int(:SOCKET, :RIGHTS, STDERR.fileno)
    • #=> #<Socket::AncillaryData: SOCKET RIGHTS 2>
  • */
    +static VALUE
    +ancillary_s_int(VALUE klass, VALUE vlevel, VALUE vtype, VALUE integer)
    +{
  • int level = level_arg(vlevel);
  • int type = cmsg_type_arg(level, vtype);
  • int i = NUM2INT(integer);
  • return ancdata_new(level, type, rb_str_new((char*)&i, sizeof(i)));
    +}

+/*

    • call-seq:
    • ancillarydata.int => integer
    • Returns the data in ancillarydata as an int.
    • The size and endian is dependent on the host.
    • ancdata = Socket::AncillaryData.int(:SOCKET, :RIGHTS,
      STDERR.fileno)
    • p ancdata.int => 2
  • */
    +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);
    +}

+static VALUE
+ancillary_s_ip_pktinfo(VALUE self, VALUE v_addr, VALUE v_ifindex, VALUE
v_spec_dst)
+{
+#if defined(IPPROTO_IP) && defined(IP_PKTINFO) /* GNU/Linux */

  • unsigned int ifindex;
  • struct sockaddr_in sa;
  • struct in_pktinfo pktinfo;
  • SockAddrStringValue(v_addr);
  • ifindex = NUM2UINT(v_ifindex);
  • 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(IPPROTO_IP, IP_PKTINFO, rb_str_new((char
    *)&pktinfo, sizeof(pktinfo)));
    +#else
  • rb_notimplement();
    +#endif
    +}

+static VALUE
+ancillary_ip_pktinfo(VALUE self)
+{
+#if defined(IPPROTO_IP) && defined(IP_PKTINFO) /* GNU/Linux */

  • 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 = 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 = 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);
    +#else
  • rb_notimplement();
    +#endif
    +}

+static VALUE
+ancillary_s_ipv6_pktinfo(VALUE self, VALUE v_addr, VALUE v_ifindex)
+{
+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */

  • 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(IPPROTO_IPV6, IPV6_PKTINFO, rb_str_new((char
    *)&pktinfo, sizeof(pktinfo)));
    +#else
  • rb_notimplement();
    +#endif
    +}

+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */
+static void
+extract_ipv6_pktinfo(VALUE self, struct in6_pktinfo *pktinfo_ptr,
struct sockaddr_in6 *sa_ptr)
+{

  • int level, type;
  • VALUE data;
  • level = ancillary_level(self);
  • type = ancillary_type(self);
  • data = ancillary_data(self);
  • if (level != IPPROTO_IPV6 || type != IPV6_PKTINFO ||
  •    RSTRING_LEN(data) != sizeof(struct in6_pktinfo)) {
    
  •    rb_raise(rb_eTypeError, "IPV6_PKTINFO ancillary data 
    

expected");

  • }
  • memcpy(pktinfo_ptr, RSTRING_PTR(data), sizeof(*pktinfo_ptr));
  • memset(sa_ptr, 0, sizeof(*sa_ptr));
  • sa_ptr->sin6_family = AF_INET6;
  • memcpy(&sa_ptr->sin6_addr, &pktinfo_ptr->ipi6_addr,
    sizeof(sa_ptr->sin6_addr));
  • if (IN6_IS_ADDR_LINKLOCAL(&sa_ptr->sin6_addr))
  •    sa_ptr->sin6_scope_id = pktinfo_ptr->ipi6_ifindex;
    

+}
+#endif
+
+static VALUE
+ancillary_ipv6_pktinfo(VALUE self)
+{
+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */

  • struct in6_pktinfo pktinfo;
  • struct sockaddr_in6 sa;
  • VALUE v_addr;
  • extract_ipv6_pktinfo(self, &pktinfo, &sa);
  • v_addr = addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET6,
    0, 0, Qnil, Qnil);
  • return rb_ary_new3(2, v_addr, UINT2NUM(pktinfo.ipi6_ifindex));
    +#else
  • rb_notimplement();
    +#endif
    +}

+static VALUE
+ancillary_ipv6_pktinfo_addr(VALUE self)
+{
+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */

  • struct in6_pktinfo pktinfo;
  • struct sockaddr_in6 sa;
  • extract_ipv6_pktinfo(self, &pktinfo, &sa);
  • return addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET6,
    0, 0, Qnil, Qnil);
    +#else
  • rb_notimplement();
    +#endif
    +}

+static VALUE
+ancillary_ipv6_pktinfo_ifindex(VALUE self)
+{
+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */

  • struct in6_pktinfo pktinfo;
  • struct sockaddr_in6 sa;
  • extract_ipv6_pktinfo(self, &pktinfo, &sa);
  • return UINT2NUM(pktinfo.ipi6_ifindex);
    +#else
  • rb_notimplement();
    +#endif
    +}

+#if defined(SOL_SOCKET) && defined(SCM_RIGHTS) /* 4.4BSD */
+static int
+anc_inspect_socket_rights(int level, int type, VALUE data, VALUE ret)
+{

  • if (level == SOL_SOCKET && type == SCM_RIGHTS &&
  •    0 < RSTRING_LEN(data) && (RSTRING_LEN(data) % sizeof(int) == 
    

0)) {

  •    long off;
    
  •    for (off = 0; off < RSTRING_LEN(data); off += sizeof(int)) {
    
  •        int fd;
    
  •        memcpy((char*)&fd, RSTRING_PTR(data)+off, sizeof(int));
    
  •        rb_str_catf(ret, " %d", fd);
    
  •    }
    
  •    return 0;
    
  • }
  • else {
  •    return -1;
    
  • }
    +}
    +#endif

+#if defined(IPPROTO_IP) && defined(IP_RECVDSTADDR) /* 4.4BSD */
+static int
+anc_inspect_ip_recvdstaddr(int level, int type, VALUE data, VALUE ret)
+{

  • if (level == IPPROTO_IP && type == IP_RECVDSTADDR &&
  •    RSTRING_LEN(data) == sizeof(struct in_addr)) {
    
  •    struct in_addr addr;
    
  •    char addrbuf[INET_ADDRSTRLEN];
    
  •    memcpy(&addr, RSTRING_PTR(data), sizeof(addr));
    
  •    if (inet_ntop(AF_INET, &addr, addrbuf, sizeof(addrbuf)) == 
    

NULL)

  •        rb_str_cat2(ret, " invalid-address");
    
  •    else
    
  •        rb_str_catf(ret, " %s", addrbuf);
    
  •    return 0;
    
  • }
  • else {
  •    return -1;
    
  • }
    +}
    +#endif

+#if defined(IPPROTO_IP) && defined(IP_PKTINFO) /* GNU/Linux */
+static int
+anc_inspect_ip_pktinfo(int level, int type, VALUE data, VALUE ret)
+{

  • if (level == IPPROTO_IP && type == IP_PKTINFO &&
  •    RSTRING_LEN(data) == sizeof(struct in_pktinfo)) {
    
  •    struct in_pktinfo pktinfo;
    
  •    char buf[INET_ADDRSTRLEN > IFNAMSIZ ? INET_ADDRSTRLEN : 
    

IFNAMSIZ];

  •    memcpy(&pktinfo, RSTRING_PTR(data), sizeof(pktinfo));
    
  •    if (inet_ntop(AF_INET, &pktinfo.ipi_addr, buf, sizeof(buf)) == 
    

NULL)

  •        rb_str_cat2(ret, " addr:invalid-address");
    
  •    else
    
  •        rb_str_catf(ret, " addr:%s", buf);
    
  •    if (if_indextoname(pktinfo.ipi_ifindex, buf) == NULL)
    
  •        rb_str_catf(ret, " ifindex:%d", pktinfo.ipi_ifindex);
    
  •    else
    
  •        rb_str_catf(ret, " %s", buf);
    
  •    if (inet_ntop(AF_INET, &pktinfo.ipi_spec_dst, buf, sizeof(buf)) 
    

== NULL)

  •        rb_str_cat2(ret, " spec_dst:invalid-address");
    
  •    else
    
  •        rb_str_catf(ret, " spec_dst:%s", buf);
    
  •    return 0;
    
  • }
  • else {
  •    return -1;
    
  • }
    +}
    +#endif

+#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */
+static int
+anc_inspect_ipv6_pktinfo(int level, int type, VALUE data, VALUE ret)
+{

  • if (level == IPPROTO_IPV6 && type == IPV6_PKTINFO &&
  •    RSTRING_LEN(data) == sizeof(struct in6_pktinfo)) {
    
  •    struct in6_pktinfo *pktinfo = (struct in6_pktinfo 
    

*)RSTRING_PTR(data);

  •    struct in6_addr addr;
    
  •    unsigned int ifindex;
    
  •    char addrbuf[INET6_ADDRSTRLEN], ifbuf[IFNAMSIZ];
    
  •    memcpy(&addr, &pktinfo->ipi6_addr, sizeof(addr));
    
  •    memcpy(&ifindex, &pktinfo->ipi6_ifindex, sizeof(ifindex));
    
  •    if (inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) == 
    

NULL)

  •        rb_str_cat2(ret, " invalid-address");
    
  •    else
    
  •        rb_str_catf(ret, " %s", addrbuf);
    
  •    if (if_indextoname(ifindex, ifbuf) == NULL)
    
  •        rb_str_catf(ret, " ifindex:%d", ifindex);
    
  •    else
    
  •        rb_str_catf(ret, " %s", ifbuf);
    
  •    return 0;
    
  • }
  • else {
  •    return -1;
    
  • }
    +}
    +#endif

+/*

    • call-seq:
    • ancillarydata.inspect => string
    • returns a string which shows ancillarydata in human-readable form.
    • Socket::AncillaryData.new(:IPV6, :PKTINFO, “”).inspect
    • #=> #<Socket::AncillaryData: IPV6 PKTINFO “”>
  • */
    +static VALUE
    +ancillary_inspect(VALUE self)
    +{
  • VALUE ret;
  • int level, type;
  • VALUE data;
  • ID level_id;
  • VALUE vtype;
  • level = ancillary_level(self);
  • type = ancillary_type(self);
  • data = ancillary_data(self);
  • ret = rb_sprintf("#<%s: ", rb_obj_classname(self));
  • level_id = intern_level(level);
  • if (level_id)
  •    rb_str_cat2(ret, rb_id2name(level_id));
    
  • else
  •    rb_str_catf(ret, "cmsg_level:%d", level);
    
  • vtype = cmsg_type_to_sym(level, type);
  • if (SYMBOL_P(vtype))
  •    rb_str_catf(ret, " %s", rb_id2name(SYM2ID(vtype)));
    
  • else
  •    rb_str_catf(ret, " cmsg_type:%d", type);
    
  • switch (level) {
    +# if defined(SOL_SOCKET)
  •  case SOL_SOCKET:
    
  •    switch (type) {
    

+# if defined(SCM_RIGHTS) /* 4.4BSD */

  •      case SCM_RIGHTS: if (anc_inspect_socket_rights(level, type, 
    

data, ret) == -1) goto dump; break;
+# endif

  •      default: goto dump;
    
  •    }
    
  •    break;
    

+# endif
+
+# if defined(IPPROTO_IP)

  •  case IPPROTO_IP:
    
  •    switch (type) {
    

+# if defined(IP_RECVDSTADDR) /* 4.4BSD */

  •      case IP_RECVDSTADDR: if (anc_inspect_ip_recvdstaddr(level, 
    

type, data, ret) == -1) goto dump; break;
+# endif
+# if defined(IP_PKTINFO) /* GNU/Linux */

  •      case IP_PKTINFO: if (anc_inspect_ip_pktinfo(level, type, 
    

data, ret) == -1) goto dump; break;
+# endif

  •      default: goto dump;
    
  •    }
    
  •    break;
    

+# endif
+
+# if defined(IPPROTO_IPV6)

  •  case IPPROTO_IPV6:
    
  •    switch (type) {
    

+# if defined(IPV6_PKTINFO) /* RFC 3542 */

  •      case IPV6_PKTINFO: if (anc_inspect_ipv6_pktinfo(level, type, 
    

data, ret) == -1) goto dump; break;
+# endif

  •      default: goto dump;
    
  •    }
    
  •    break;
    

+# endif
+

  •  default:
    
  •  dump:
    
  •    data = rb_str_dump(data);
    
  •    rb_str_catf(ret, " %s", StringValueCStr(data));
    
  • }
  • rb_str_cat2(ret, “>”);
  • return ret;
    +}

+/*

    • call-seq:
    • ancillarydata.cmsg_is?(level, type) => true or false
    • tests the level and type of ancillarydata.
    • ancdata = Socket::AncillaryData.new(: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_cmsg_is_p(VALUE self, VALUE vlevel, VALUE vtype)
    +{
  • int level = level_arg(vlevel);
  • int type = cmsg_type_arg(level, vtype);
  • if (ancillary_level(self) == level &&
  •    ancillary_type(self) == type)
    
  •    return Qtrue;
    
  • else
  •    return Qfalse;
    

+}
+
+#endif
+
+#if defined(HAVE_SENDMSG)
+struct sendmsg_args_struct {

  • int fd;
  • const struct msghdr *msg;
  • int flags;
    +};

+static VALUE
+nogvl_sendmsg_func(void *ptr)
+{

  • struct sendmsg_args_struct *args = ptr;
  • return sendmsg(args->fd, args->msg, args->flags);
    +}

+static ssize_t
+rb_sendmsg(int fd, const struct msghdr *msg, int flags)
+{

  • struct sendmsg_args_struct args;
  • args.fd = fd;
  • args.msg = msg;
  • args.flags = flags;
  • return rb_thread_blocking_region(nogvl_sendmsg_func, &args,
    RUBY_UBF_IO, 0);
    +}

+static VALUE
+bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+{

  • rb_io_t *fptr;
  • VALUE data, vflags, dest_sockaddr;
  • VALUE *controls_ptr;
  • int controls_num;
  • struct msghdr mh;
  • struct iovec iov;
    +#if defined(HAVE_ST_MSG_CONTROL)
  • volatile VALUE controls_str = 0;
    +#endif
  • int flags;
  • ssize_t ss;
  • rb_secure(4);
  • data = vflags = dest_sockaddr = Qnil;
  • controls_ptr = NULL;
  • controls_num = 0;
  • if (argc == 0)
  •    rb_raise(rb_eArgError, "mesg argument required");
    
  • data = argv[0];
  • if (1 < argc) vflags = argv[1];
  • if (2 < argc) dest_sockaddr = argv[2];
  • if (3 < argc) { controls_ptr = &argv[3]; controls_num = argc - 3; }
  • StringValue(data);
  • if (controls_num) {
    +#if defined(HAVE_ST_MSG_CONTROL)
  • int i;
  • int last_pad = 0;
  •    controls_str = rb_str_tmp_new(0);
    
  •    for (i = 0; i < controls_num; i++) {
    
  •        VALUE elt = controls_ptr[i], v;
    
  •        VALUE vlevel, vtype;
    
  •        int level, type;
    
  •        VALUE cdata;
    
  •        long oldlen;
    
  •        struct cmsghdr *cmh;
    
  •        size_t cspace;
    
  •        v = rb_check_convert_type(elt, T_ARRAY, "Array", "to_ary");
    
  •        if (!NIL_P(v)) {
    
  •            elt = v;
    
  •            if (RARRAY_LEN(elt) != 3)
    
  •                rb_raise(rb_eArgError, "an element of controls 
    

should be 3-elements array");

  •            vlevel = rb_ary_entry(elt, 0);
    
  •            vtype = rb_ary_entry(elt, 1);
    
  •            cdata = rb_ary_entry(elt, 2);
    
  •        }
    
  •        else {
    
  •            vlevel = rb_funcall(elt, rb_intern("level"), 0);
    
  •            vtype = rb_funcall(elt, rb_intern("type"), 0);
    
  •            cdata = rb_funcall(elt, rb_intern("data"), 0);
    
  •        }
    
  •        level = level_arg(vlevel);
    
  •        type = cmsg_type_arg(level, vtype);
    
  •        StringValue(cdata);
    
  •        oldlen = RSTRING_LEN(controls_str);
    
  •        cspace = CMSG_SPACE(RSTRING_LEN(cdata));
    
  •        rb_str_resize(controls_str, oldlen + cspace);
    
  •        cmh = (struct cmsghdr *)(RSTRING_PTR(controls_str)+oldlen);
    
  •        memset((char *)cmh, 0, cspace);
    
  •        cmh->cmsg_level = level;
    
  •        cmh->cmsg_type = type;
    
  •        cmh->cmsg_len = CMSG_LEN(RSTRING_LEN(cdata));
    
  •        MEMCPY(CMSG_DATA(cmh), RSTRING_PTR(cdata), char, 
    

RSTRING_LEN(cdata));

  •  last_pad = cspace - cmh->cmsg_len;
    
  •    }
    
  • if (last_pad) {
  •  rb_str_set_len(controls_str, RSTRING_LEN(controls_str)-last_pad);
    
  • }
    +#else
  • rb_raise(rb_eNotImpError, “control message for sendmsg is
    unimplemented”);
    +#endif
  • }
  • flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
    +#ifdef MSG_DONTWAIT
  • if (nonblock)
  •    flags |= MSG_DONTWAIT;
    

+#endif
+

  • if (!NIL_P(dest_sockaddr))
  • SockAddrStringValue(dest_sockaddr);
  • GetOpenFile(sock, fptr);
  • retry:
  • memset(&mh, 0, sizeof(mh));
  • if (!NIL_P(dest_sockaddr)) {
  •    mh.msg_name = RSTRING_PTR(dest_sockaddr);
    
  •    mh.msg_namelen = RSTRING_LEN(dest_sockaddr);
    
  • }
  • mh.msg_iovlen = 1;
  • mh.msg_iov = &iov;
  • iov.iov_base = RSTRING_PTR(data);
  • iov.iov_len = RSTRING_LEN(data);
    +#if defined(HAVE_ST_MSG_CONTROL)
  • if (controls_str) {
  •    mh.msg_control = RSTRING_PTR(controls_str);
    
  •    mh.msg_controllen = RSTRING_LEN(controls_str);
    
  • }
  • else {
  •    mh.msg_control = NULL;
    
  •    mh.msg_controllen = 0;
    
  • }
    +#endif
  • rb_io_check_closed(fptr);
  • if (nonblock)
  •    rb_io_set_nonblock(fptr);
    
  • ss = rb_sendmsg(fptr->fd, &mh, flags);
  • if (!nonblock && rb_io_wait_writable(fptr->fd)) {
  •    rb_io_check_closed(fptr);
    
  •    goto retry;
    
  • }
  • if (ss == -1)
  • rb_sys_fail(“sendmsg(2)”);
  • return SSIZET2NUM(ss);
    +}
    +#else
    +static VALUE
    +bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
    +{
  • rb_notimplement();
    +}
    +#endif

+/*

    • call-seq:
    • basicsocket.sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls)
      => sent_len
    • sendmsg sends a message using sendmsg(2) system call in blocking
      manner.
    • mesg is a string to send.
    • flags is bitwise OR of MSG_* constants such as Socket::MSG_OOB.
    • dest_sockaddr is a destination socket address for connection-less
      socket.
    • It should be a sockaddr such as a result of Socket.sockaddr_in.
    • An AddrInfo object can be used too.
    • controls is a list of ancillary data.
    • The element of controls should be Socket::AncillaryData or
    • 3-elements array.
    • The 3-element array should contains cmsg_level, cmsg_type and data.
    • The return value, sent_len, is an integer which is the number of
      bytes sent.
    • sendmsg can be used to implement send_io as follows:
    • use Socket::AncillaryData.

    • ancdata = Socket::AncillaryData.int(:SOCKET, :RIGHTS, io.fileno)
    • sock.sendmsg(“a”, 0, nil, ancdata)
    • use 3-element array.

    • ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack(“i!”)]
    • sock.sendmsg("\0", 0, nil, ancdata)
  • */
    +static VALUE
    +bsock_sendmsg(int argc, VALUE *argv, VALUE sock)
    +{
  • return bsock_sendmsg_internal(argc, argv, sock, 0);
    +}

+/*

    • call-seq:
    • basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil,
      *controls) => sent_len
    • sendmsg_nonblock sends a message using sendmsg(2) system call in
      non-blocking manner.
    • It is similar to BasicSocket#sendmsg
    • but the non-blocking flag is set before the system call
    • and it doesn’t retry the system call.
  • */
    +static VALUE
    +bsock_sendmsg_nonblock(int argc, VALUE *argv, VALUE sock)
    +{
  • return bsock_sendmsg_internal(argc, argv, sock, 1);
    +}

+#if defined(HAVE_RECVMSG)
+struct recvmsg_args_struct {

  • int fd;
  • struct msghdr *msg;
  • int flags;
    +};

+static VALUE
+nogvl_recvmsg_func(void *ptr)
+{

  • struct recvmsg_args_struct *args = ptr;
  • return recvmsg(args->fd, args->msg, args->flags);
    +}

+static ssize_t
+rb_recvmsg(int fd, struct msghdr *msg, int flags)
+{

  • struct recvmsg_args_struct args;
  • args.fd = fd;
  • args.msg = msg;
  • args.flags = flags;
  • return rb_thread_blocking_region(nogvl_recvmsg_func, &args,
    RUBY_UBF_IO, 0);
    +}

+static VALUE
+bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
+{

  • rb_io_t *fptr;
  • VALUE vmaxdatlen, vmaxctllen, vflags;
  • int grow_buffer;
  • size_t maxdatlen, maxctllen;
  • int flags, orig_flags;
  • struct msghdr mh;
  • struct iovec iov;
    +#if defined(HAVE_ST_MSG_CONTROL)
  • struct cmsghdr *cmh;
    +#endif
  • char namebuf[1024];
  • char datbuf0[4096], *datbuf;
  • char ctlbuf0[4096], *ctlbuf;
  • VALUE dat_str = Qnil;
  • VALUE ctl_str = Qnil;
  • VALUE ret;
  • ssize_t ss;
  • rb_secure(4);
  • rb_scan_args(argc, argv, “03”, &vmaxdatlen, &vflags, &vmaxctllen);
  • maxdatlen = NIL_P(vmaxdatlen) ? sizeof(datbuf0) :
    NUM2SIZET(vmaxdatlen);
  • maxctllen = NIL_P(vmaxctllen) ? sizeof(ctlbuf0) :
    NUM2SIZET(vmaxctllen);
  • flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
    +#ifdef MSG_DONTWAIT
  • if (nonblock)
  •    flags |= MSG_DONTWAIT;
    

+#endif

  • orig_flags = flags;
  • grow_buffer = NIL_P(vmaxdatlen) || NIL_P(vmaxctllen);
  • GetOpenFile(sock, fptr);
  • if (rb_io_read_pending(fptr)) {
  •    rb_raise(rb_eIOError, "recvfrom for buffered IO");
    
  • }

+#if !defined(HAVE_ST_MSG_CONTROL)

  • if (grow_buffer) {
  • int socktype, optlen = sizeof(socktype);
  •    if (getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, 
    

&optlen) == -1) {

  •  rb_sys_fail("getsockopt(SO_TYPE)");
    
  • }
  • if (socktype == SOCK_STREAM)
  •  grow_buffer = 0;
    
  • }
    +#endif
  • retry:
  • if (maxdatlen <= sizeof(datbuf0))
  •    datbuf = datbuf0;
    
  • else {
  •    if (NIL_P(dat_str))
    
  •        dat_str = rb_str_tmp_new(maxdatlen);
    
  •    else
    
  •        rb_str_resize(dat_str, maxdatlen);
    
  •    datbuf = RSTRING_PTR(dat_str);
    
  • }
  • if (maxctllen <= sizeof(ctlbuf0))
  •    ctlbuf = ctlbuf0;
    
  • else {
  •    if (NIL_P(ctl_str))
    
  •        ctl_str = rb_str_tmp_new(maxctllen);
    
  •    else
    
  •        rb_str_resize(ctl_str, maxctllen);
    
  •    ctlbuf = RSTRING_PTR(ctl_str);
    
  • }
  • memset(&mh, 0, sizeof(mh));
  • memset(namebuf, 0, sizeof(namebuf));
  • mh.msg_name = namebuf;
  • mh.msg_namelen = sizeof(namebuf);
  • mh.msg_iov = &iov;
  • mh.msg_iovlen = 1;
  • iov.iov_base = datbuf;
  • iov.iov_len = maxdatlen;

+#if defined(HAVE_ST_MSG_CONTROL)

  • mh.msg_control = ctlbuf;
  • mh.msg_controllen = maxctllen;
    +#endif
  • if (grow_buffer)
  •    flags |= MSG_PEEK;
    
  • rb_io_check_closed(fptr);
  • if (nonblock)
  •    rb_io_set_nonblock(fptr);
    
  • ss = rb_recvmsg(fptr->fd, &mh, flags);
  • if (!nonblock && rb_io_wait_readable(fptr->fd)) {
  •    rb_io_check_closed(fptr);
    
  •    goto retry;
    
  • }
  • if (grow_buffer) {
  • int grown = 0;
    +#if defined(HAVE_ST_MSG_CONTROL)
  •    if (NIL_P(vmaxdatlen) && (mh.msg_flags & MSG_TRUNC)) {
    
  •  maxdatlen *= 2;
    
  •  grown = 1;
    
  • }
  •    if (NIL_P(vmaxctllen) && (mh.msg_flags & MSG_CTRUNC)) {
    
  •  maxctllen *= 2;
    
  •  grown = 1;
    
  • }
    +#else
  • if (NIL_P(vmaxdatlen) && ss != -1 && ss == iov.iov_len) {
  •  maxdatlen *= 2;
    
  •  grown = 1;
    
  • }
    +#endif
  • if (grown) {
  •  goto retry;
    
  • }
  • else {
  •        grow_buffer = 0;
    
  •        if (flags != orig_flags) {
    
  •            flags = orig_flags;
    
  •            goto retry;
    
  •        }
    
  •    }
    
  • }
  • if (ss == -1)
  • rb_sys_fail(“recvmsg(2)”);
  • if (NIL_P(dat_str))
  •    dat_str = rb_tainted_str_new(datbuf, ss);
    
  • else {
  •    rb_str_resize(dat_str, ss);
    
  •    OBJ_TAINT(dat_str);
    
  •    RBASIC(dat_str)->klass = rb_cString;
    
  • }
  • ret = rb_ary_new3(3, dat_str,
  •                     io_socket_addrinfo(sock, mh.msg_name, 
    

mh.msg_namelen),
+#if defined(HAVE_ST_MSG_CONTROL)

  •   INT2NUM(mh.msg_flags)
    

+#else

  •   Qnil
    

+#endif

  •   );
    

+#if defined(HAVE_ST_MSG_CONTROL)

  • if (mh.msg_controllen) {
  •    for (cmh = CMSG_FIRSTHDR(&mh); cmh != NULL; cmh = 
    

CMSG_NXTHDR(&mh, cmh)) {

  •        VALUE ctl;
    
  •        size_t clen;
    
  •        if (cmh->cmsg_len == 0) {
    
  •            rb_raise(rb_eIOError, "invalid control message 
    

(cmsg_len == 0)");

  •        }
    
  •        clen = (char*)cmh + cmh->cmsg_len - (char*)CMSG_DATA(cmh);
    
  •        ctl = ancdata_new(cmh->cmsg_level, cmh->cmsg_type, 
    

rb_tainted_str_new((char*)CMSG_DATA(cmh), clen));

  •        rb_ary_push(ret, ctl);
    
  •    }
    
  • }
    +#endif
  • return ret;
    +}
    +#else
    +static VALUE
    +bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
    +{
  • rb_notimplement();
    +}
    +#endif

+/*

    • call-seq:
    • basicsocket.recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil)
      => [mesg, sender_addrinfo, rflags, *controls]
    • recvmsg receives a message using recvmsg(2) system call in blocking
      manner.
    • maxmesglen is the maximum length of mesg to receive.
    • flags is bitwise OR of MSG_* constants such as Socket::MSG_PEEK.
    • maxcontrolslen is the maximum length of controls (ancillary data)
      to receive.
    • The return value is 4-elements array.
    • mesg is a string of the received message.
    • sender_addrinfo is a sender socket address for connection-less
      socket.
    • It is an AddrInfo object.
    • For connection-oriented socket such as TCP, sender_addrinfo is
      platform dependent.
    • rflags is a flags on the received message which is bitwise OR of
      MSG_* constants such as Socket::MSG_TRUNC.
    • It will be nil if the system uses 4.3BSD style old recvmsg system
      call.
    • controls is ancillary data which is an array of
      Socket::AncillaryData objects such as:
    • #<Socket::AncillaryData: SOCKET RIGHTS 7>
    • maxmesglen and maxcontrolslen can be nil.
    • In that case, the buffer will be grown until the message is not
      truncated.
    • Internally, MSG_PEEK is used and MSG_TRUNC/MSG_CTRUNC are checked.
    • sendmsg can be used to implement recv_io as follows:
    • mesg, sender_sockaddr, rflags, *controls = sock.recvmsg
    • controls.each {|ancdata|
    • if ancdata.level == Socket::SOL_SOCKET && ancdata.type == 
      

Socket::SCM_RIGHTS

    •   return IO.new(ancdata.int)
      
    • end
      
    • }
  • */
    +static VALUE
    +bsock_recvmsg(int argc, VALUE *argv, VALUE sock)
    +{
  • return bsock_recvmsg_internal(argc, argv, sock, 0);
    +}

+/*

    • call-seq:
    • basicsocket.recvmsg_nonblock(maxdatalen=nil, flags=0,
      maxcontrollen=nil) => [data, sender_addrinfo, rflags, *controls]
    • recvmsg receives a message using recvmsg(2) system call in
      non-blocking manner.
    • It is similar to BasicSocket#recvmsg
    • but non-blocking flag is set before the system call
    • and it doesn’t retry the system call.
  • */
    +static VALUE
    +bsock_recvmsg_nonblock(int argc, VALUE *argv, VALUE sock)
    +{
  • return bsock_recvmsg_internal(argc, argv, sock, 1);
    +}

+void
+Init_ancdata(void)
+{

  • rb_define_method(rb_cBasicSocket, “sendmsg”, bsock_sendmsg, -1);
  • rb_define_method(rb_cBasicSocket, “sendmsg_nonblock”,
    bsock_sendmsg_nonblock, -1);
  • rb_define_method(rb_cBasicSocket, “recvmsg”, bsock_recvmsg, -1);
  • rb_define_method(rb_cBasicSocket, “recvmsg_nonblock”,
    bsock_recvmsg_nonblock, -1);

+#if defined(HAVE_ST_MSG_CONTROL)

  • rb_cAncillaryData = rb_define_class_under(rb_cSocket,
    “AncillaryData”, rb_cObject);

  • rb_define_method(rb_cAncillaryData, “initialize”,
    ancillary_initialize, 3);

  • rb_define_method(rb_cAncillaryData, “inspect”, ancillary_inspect,
    0);

  • rb_define_method(rb_cAncillaryData, “level”, ancillary_level_m, 0);

  • rb_define_method(rb_cAncillaryData, “type”, ancillary_type_m, 0);

  • rb_define_method(rb_cAncillaryData, “data”, ancillary_data, 0);

  • rb_define_method(rb_cAncillaryData, “cmsg_is?”,
    ancillary_cmsg_is_p, 2);

  • rb_define_singleton_method(rb_cAncillaryData, “int”,
    ancillary_s_int, 3);

  • rb_define_method(rb_cAncillaryData, “int”, ancillary_int, 0);

  • rb_define_singleton_method(rb_cAncillaryData, “ip_pktinfo”,
    ancillary_s_ip_pktinfo, 3);

  • rb_define_method(rb_cAncillaryData, “ip_pktinfo”,
    ancillary_ip_pktinfo, 0);

  • rb_define_singleton_method(rb_cAncillaryData, “ipv6_pktinfo”,
    ancillary_s_ipv6_pktinfo, 2);

  • rb_define_method(rb_cAncillaryData, “ipv6_pktinfo”,
    ancillary_ipv6_pktinfo, 0);

  • rb_define_method(rb_cAncillaryData, “ipv6_pktinfo_addr”,
    ancillary_ipv6_pktinfo_addr, 0);

  • rb_define_method(rb_cAncillaryData, “ipv6_pktinfo_ifindex”,
    ancillary_ipv6_pktinfo_ifindex, 0);
    +#endif
    +}
    Index: test/socket/test_unix.rb
    ===================================================================
    — test/socket/test_unix.rb (revision 21891)
    +++ test/socket/test_unix.rb (working copy)
    @@ -31,6 +31,118 @@ class TestUNIXSocket < Test::Unit::TestC
    end
    end

  • def test_fd_passing_n

  • io_ary = []

  • return if !defined?(Socket::SCM_RIGHTS)

  • io_ary.concat IO.pipe

  • io_ary.concat IO.pipe

  • io_ary.concat IO.pipe

  • send_io_ary = []

  • io_ary.each {|io|

  •  send_io_ary << io
    
  •  UNIXSocket.pair {|s1, s2|
    
  •    begin
    
  •      ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, 
    

Socket::SCM_RIGHTS,

  •                                      send_io_ary.map {|io| 
    

io.fileno }.pack(“i!*”)])

  •    rescue NotImplementedError
    
  •      return
    
  •    end
    
  •    assert_equal(1, ret)
    
  •    ret = s2.recvmsg
    
  •    data, srcaddr, flags, *ctls = ret
    
  •    recv_io_ary = []
    
  •    ctls.each {|ctl|
    
  •      next if ctl.level != Socket::SOL_SOCKET || ctl.type != 
    

Socket::SCM_RIGHTS

  •      recv_io_ary.concat ctl.data.unpack("i!*").map {|fd| 
    

IO.new(fd) }

  •    }
    
  •    assert_equal(send_io_ary.length, recv_io_ary.length)
    
  •    send_io_ary.length.times {|i|
    
  •      assert_not_equal(send_io_ary[i].fileno, 
    

recv_io_ary[i].fileno)

  •      assert(File.identical?(send_io_ary[i], recv_io_ary[i]))
    
  •    }
    
  •  }
    
  • }
  • ensure
  • io_ary.each {|io| io.close if !io.closed? }
  • end
  • def test_sendmsg
  • return if !defined?(Socket::SCM_RIGHTS)
  • IO.pipe {|r1, w|
  •  UNIXSocket.pair {|s1, s2|
    
  •    begin
    
  •      ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, 
    

Socket::SCM_RIGHTS, [r1.fileno].pack(“i!”)])

  •    rescue NotImplementedError
    
  •      return
    
  •    end
    
  •    assert_equal(1, ret)
    
  •    r2 = s2.recv_io
    
  •    begin
    
  •      assert(File.identical?(r1, r2))
    
  •    ensure
    
  •      r2.close
    
  •    end
    
  •  }
    
  • }
  • end
  • def test_sendmsg_ancillarydata
  • return if !defined?(Socket::SCM_RIGHTS)
  • return if !defined?(Socket::AncillaryData)
  • IO.pipe {|r1, w|
  •  UNIXSocket.pair {|s1, s2|
    
  •    begin
    
  •      ad = Socket::AncillaryData.int(:SOCKET, :RIGHTS, r1.fileno)
    
  •      ret = s1.sendmsg("\0", 0, nil, ad)
    
  •    rescue NotImplementedError
    
  •      return
    
  •    end
    
  •    assert_equal(1, ret)
    
  •    r2 = s2.recv_io
    
  •    begin
    
  •      assert(File.identical?(r1, r2))
    
  •    ensure
    
  •      r2.close
    
  •    end
    
  •  }
    
  • }
  • end
  • def test_recvmsg
  • return if !defined?(Socket::SCM_RIGHTS)
  • IO.pipe {|r1, w|
  •  UNIXSocket.pair {|s1, s2|
    
  •    s1.send_io(r1)
    
  •    ret = s2.recvmsg
    
  •    data, srcaddr, flags, *ctls = ret
    
  •    assert_equal("\0", data)
    
  • if flags == nil
  • struct msghdr is 4.3BSD style (msg_accrights field).

  • assert_instance_of(Array, ctls)
  • assert_equal(0, ctls.length)
  • else
  • struct msghdr is POSIX/4.4BSD style (msg_control field).

  • assert_equal(0, flags & (Socket::MSG_TRUNC|Socket::MSG_CTRUNC))
  • assert_instance_of(AddrInfo, srcaddr)
  • assert_instance_of(Array, ctls)
  • assert_equal(1, ctls.length)
  • assert_instance_of(Socket::AncillaryData, ctls[0])
  • assert_equal(Socket::SOL_SOCKET, ctls[0].level)
  • assert_equal(Socket::SCM_RIGHTS, ctls[0].type)
  • assert_instance_of(String, ctls[0].data)
  • fd, rest = ctls[0].data.unpack(“i!a*”)
  • assert_equal("", rest)
  • r2 = IO.new(fd)
  • begin
  •  assert(File.identical?(r1, r2))
    
  • ensure
  •  r2.close
    
  • end
  • end
  •  }
    
  • }
  • end
  • def bound_unix_socket(klass)
    tmpfile = Tempfile.new(“testrubysock”)
    path = tmpfile.path