# Sendmsg/recvmsg

**URL:** <https://www.ruby-forum.com/t/sendmsg-recvmsg/246521>\
**Category:** Development\
**Created:** [January 30, 2009, 9:37am UTC](https://www.ruby-forum.com/t/sendmsg-recvmsg/246521 "2009-01-30T09:37:30Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [January 30, 2009, 9:37am UTC](https://www.ruby-forum.com/t/sendmsg-recvmsg/246521/1 "2009-01-30T09:37:30Z")

</div>

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
