first commit
CI / Build (clang, clang++, ubuntu-22.04, linux, 3.10, master, x86,x86_64) (push) Canceled after 0s
CI / Build (clang, clang++, windows-2022, windows, 3.10, master, x86,x86_64) (push) Canceled after 0s
CI / Release (push) Canceled after 0s

This commit is contained in:
2026-09-13 22:18:11 +01:00
commit 08c03ac533
6701 changed files with 1577314 additions and 0 deletions
@@ -0,0 +1,56 @@
mruby-socket
============
"mruby-socket" mrbgem provides BSD socket interface for mruby.
API is compatible with CRuby's "socket" library.
## Example
```sh
% vi kame.rb
s = TCPSocket.open("www.kame.net", 80)
s.write("GET / HTTP/1.0\r\n\r\n")
puts s.read
s.close
% mruby kame.rb
HTTP/1.1 200 OK
Date: Tue, 21 May 2013 04:31:30 GMT
...
```
## Requirement
- [mruby-io](https://github.com/mruby/mruby/tree/master/mrbgems/mruby-io) mrbgem
- [iij/mruby-mtest](https://github.com/iij/mruby-mtest) mrgbem to run tests
- system must have RFC3493 basic socket interface
- and some POSIX API...
## TODO
- add missing methods
- write more tests
- fix possible descriptor leakage (see XXX comments)
- `UNIXSocket#recv_io` `UNIXSocket#send_io`
## License
Copyright (c) 2013 Internet Initiative Japan Inc.
Copyright (c) 2017 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
MRuby::Gem::Specification.new('mruby-socket') do |spec|
spec.license = 'MIT'
spec.authors = ['Internet Initiative Japan', 'mruby developers']
spec.summary = 'standard socket class'
spec.cc.include_paths << "#{build.root}/src"
#spec.cc.defines << "HAVE_SA_LEN=0"
# If Windows, use winsock
if for_windows?
spec.linker.libraries << "wsock32"
spec.linker.libraries << "ws2_32"
end
spec.add_dependency('mruby-io', :core => 'mruby-io')
spec.add_dependency('mruby-pack', :core => 'mruby-pack')
# spec.add_dependency('mruby-mtest')
end
@@ -0,0 +1,621 @@
class Addrinfo
def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0)
@hostname = nil
if sockaddr.is_a? Array
sary = sockaddr
if sary[0] == 'AF_INET' || sary[0] == 'AF_INET6'
@sockaddr = Socket.sockaddr_in(sary[1], sary[3])
@hostname = sary[2]
elsif sary[0] == 'AF_UNIX'
@sockaddr = Socket.sockaddr_un(sary[1])
end
else
@sockaddr = sockaddr.dup
end
if family == Socket::PF_UNSPEC or family == nil
@family = Socket._sockaddr_family(@sockaddr)
else
@family = family
end
@socktype = socktype
@protocol = protocol
@canonname = nil
end
def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0, &block)
a = self.getaddrinfo(nodename, service, family, socktype, protocol, flags)
a.each { |ai| block.call(ai) }
a
end
def self.ip(host)
Addrinfo.new(Socket.sockaddr_in(0, host))
end
def self.tcp(host, port)
Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0]
end
def self.udp(host, port)
Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0]
end
def self.unix(path, socktype=Socket::SOCK_STREAM)
Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype)
end
def afamily
@family
end
#def bind
attr_reader :canonname
#def connect
#def connect_from
#def connect_to
#def family_addrinfo(host, port=nil)
#def getnameinfo(flags=0)
# Socket.getnameinfo
#end
def inspect
if ipv4? or ipv6?
if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0)
proto = 'TCP'
elsif @protocol == Socket::IPPROTO_UDP or (@socktype == Socket::SOCK_DGRAM and @protocol == 0)
proto = 'UDP'
else
proto = '???'
end
"#<Addrinfo: #{inspect_sockaddr} #{proto}>"
else
"#<Addrinfo: #{self.unix_path} SOCK_STREAM>"
end
end
def inspect_sockaddr
if ipv4?
a, p = ip_unpack
"#{a}:#{p}"
elsif ipv6?
a, p = ip_unpack
"[#{a}]:#{p}"
elsif unix?
unix_path
else
'???'
end
end
def ip?
ipv4? or ipv6?
end
def ip_address
ip_unpack[0]
end
def ip_port
ip_unpack[1]
end
def ip_unpack
h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)
[ h, p.to_i ]
end
def ipv4?
@family == Socket::AF_INET
end
#def ipv4_loopback?
#def ipv4_multicast?
#def ipv4_private?
def ipv6?
@family == Socket::AF_INET6
end
#def ipv6_loopback?
#def ipv6_mc_global?
#def ipv6_mc_linklocal?
#def ipv6_mc_nodelocal?
#def ipv6_mc_orilocal?
#def ipv6_mc_sitelocal?
#def ipv6_multicast?
#def ipv6_to_ipv4
#def ipv6_unspecified
#def ipv6_v4compat?
#def ipv6_v4mapped?
#def listen(backlog=5)
def pfamily
@family
end
attr_reader :protocol
attr_reader :socktype
def _to_array
case @family
when Socket::AF_INET
s = "AF_INET"
when Socket::AF_INET6
s = "AF_INET6"
when Socket::AF_UNIX
s = "AF_UNIX"
else
s = "(unknown AF)"
end
addr, port = self.getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)
[ s, port.to_i, addr, addr ]
end
def to_sockaddr
@sockaddr
end
alias to_s to_sockaddr
def unix?
@family == Socket::AF_UNIX
end
end
class BasicSocket < IO
@@do_not_reverse_lookup = true
def self.do_not_reverse_lookup
@@do_not_reverse_lookup
end
def self.do_not_reverse_lookup=(val)
@@do_not_reverse_lookup = val ? true : false
end
def initialize(*args)
super(*args)
self._is_socket = true
@do_not_reverse_lookup = @@do_not_reverse_lookup
end
def self.for_fd(fd)
super(fd, "r+")
end
#def connect_address
def local_address
Addrinfo.new self.getsockname
end
def recv_nonblock(maxlen, flags=0)
begin
_setnonblock(true)
recv(maxlen, flags)
ensure
_setnonblock(false)
end
end
def remote_address
Addrinfo.new self.getpeername
end
attr_accessor :do_not_reverse_lookup
end
class IPSocket < BasicSocket
def self.getaddress(host)
Addrinfo.ip(host).ip_address
end
def addr
Addrinfo.new(self.getsockname)._to_array
end
def peeraddr
Addrinfo.new(self.getpeername)._to_array
end
def recvfrom(maxlen, flags=0)
msg, sa = _recvfrom(maxlen, flags)
[ msg, Addrinfo.new(sa)._to_array ]
end
end
class TCPSocket < IPSocket
def initialize(host, service, local_host=nil, local_service=nil)
if @init_with_fd
super(host, service)
else
s = nil
e = SocketError
Addrinfo.foreach(host, service) { |ai|
begin
s = Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0)
if local_host or local_service
local_host ||= (ai.afamily == Socket::AF_INET) ? "0.0.0.0" : "::"
local_service ||= "0"
bi = Addrinfo.getaddrinfo(local_host, local_service, ai.afamily, ai.socktype)[0]
Socket._bind(s, bi.to_sockaddr)
end
Socket._connect(s, ai.to_sockaddr)
super(s, "r+")
return
rescue => e0
e = e0
end
}
raise e
end
end
def self.new_with_prelude pre, *args
o = self._allocate
o.instance_eval(&pre)
o.initialize(*args)
o
end
#def self.gethostbyname(host)
end
class TCPServer < TCPSocket
def initialize(host=nil, service)
ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0]
@init_with_fd = true
super(Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0), "r+")
if Socket.const_defined?(:SO_REUSEADDR)
self.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
end
Socket._bind(self.fileno, ai.to_sockaddr)
listen(5)
self
end
def accept
fd = self.sysaccept
begin
TCPSocket.new_with_prelude(proc { @init_with_fd = true }, fd, "r+")
rescue
IO._sysclose(fd) rescue nil
raise
end
end
def accept_nonblock
begin
self._setnonblock(true)
self.accept
ensure
self._setnonblock(false)
end
end
def listen(backlog)
Socket._listen(self.fileno, backlog)
0
end
def sysaccept
Socket._accept(self.fileno)
end
end
class UDPSocket < IPSocket
def initialize(af=Socket::AF_INET)
super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+")
@af = af
self
end
def bind(host, port)
Socket._bind(self.fileno, _sockaddr_in(port, host))
0
end
def connect(host, port)
Socket._connect(self.fileno, _sockaddr_in(port, host))
0
end
def recvfrom_nonblock(*args)
s = self
begin
self._setnonblock(true)
self.recvfrom(*args)
ensure
# XXX: self is a SystemcallException here! (should be bug)
s._setnonblock(false)
end
end
def send(mesg, flags, host=nil, port=nil)
if port
super(mesg, flags, _sockaddr_in(port, host))
elsif host
super(mesg, flags, host)
else
super(mesg, flags)
end
end
def _sockaddr_in(port, host)
ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0]
ai.to_sockaddr
end
end
class Socket < BasicSocket
def initialize(domain, type, protocol=0)
super(Socket._socket(domain, type, protocol), "r+")
end
#def self.accept_loop
def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0)
Addrinfo.getaddrinfo(nodename, servname, family, socktype, protocol, flags).map { |ai|
ary = ai._to_array
ary[2] = nodename
ary[4] = ai.afamily
ary[5] = ai.socktype
ary[6] = ai.protocol
ary
}
end
#def self.getnameinfo
#def self.ip_address_list
def self.open(*args)
new(args)
end
def self.sockaddr_in(port, host)
ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0]
ai.to_sockaddr
end
#def self.tcp
#def self.tcp_server_loop
#def self.tcp_server_sockets
#def self.udp_server_loop
#def self.udp_server_loop_on
#def self.udp_server_recv
#def self.udp_server_sockets
#def self.unix(path)
#def self.unix_server_loop
#def self.unix_server_socket
def self.unpack_sockaddr_in(sa)
Addrinfo.new(sa).ip_unpack.reverse
end
def self.unpack_sockaddr_un(sa)
Addrinfo.new(sa).unix_path
end
class << self
alias pack_sockaddr_in sockaddr_in
alias pack_sockaddr_un sockaddr_un
alias pair socketpair
end
def accept
fd, addr = self.sysaccept
[ Socket.for_fd(fd), addr ]
end
def accept_nonblock
begin
self._setnonblock(true)
self.accept
ensure
self._setnonblock(false)
end
end
def bind(sockaddr)
sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo
Socket._bind(self.fileno, sockaddr)
0
end
def connect(sockaddr)
sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo
Socket._connect(self.fileno, sockaddr)
0
end
def connect_nonblock(sockaddr)
begin
self._setnonblock(true)
self.connect(sockaddr)
ensure
self._setnonblock(false)
end
end
#def ipv6only!
def listen(backlog)
Socket._listen(self.fileno, backlog)
0
end
def recvfrom(maxlen, flags=0)
msg, sa = _recvfrom(maxlen, flags)
socktype = self.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).int
[ msg, Addrinfo.new(sa, Socket::PF_UNSPEC, socktype) ]
end
def recvfrom_nonblock(*args)
begin
self._setnonblock(true)
self._recvfrom(*args)
ensure
self._setnonblock(false)
end
end
def sysaccept
Socket._accept2(self.fileno)
end
end
class UNIXSocket < BasicSocket
def initialize(path, &block)
if self.is_a? UNIXServer
super(path, "r")
else
super(Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0), "r+")
Socket._connect(self.fileno, Socket.sockaddr_un(path))
if block_given?
begin
yield self
ensure
begin
self.close unless self.closed?
rescue StandardError
end
end
end
end
end
def self.socketpair(type=Socket::SOCK_STREAM, protocol=0)
a = Socket.socketpair(Socket::AF_UNIX, type, protocol)
[ UNIXSocket.for_fd(a[0]), UNIXSocket.for_fd(a[1]) ]
end
class << self
alias pair socketpair
end
def addr
[ "AF_UNIX", path ]
end
def path
Addrinfo.new(self.getsockname).unix_path
end
def peeraddr
[ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ]
end
#def recv_io
def recvfrom(maxlen, flags=0)
msg, sa = _recvfrom(maxlen, flags)
path = (sa.size > 0) ? Addrinfo.new(sa).unix_path : ""
[ msg, [ "AF_UNIX", path ] ]
end
#def send_io
end
class UNIXServer < UNIXSocket
def initialize(path)
fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
begin
super(fd)
Socket._bind(fd, Socket.pack_sockaddr_un(path))
self.listen(5)
rescue => e
IO._sysclose(fd) rescue nil
raise e
end
if block_given?
begin
yield self
ensure
self.close rescue nil unless self.closed?
end
end
end
def accept
fd = self.sysaccept
begin
sock = UNIXSocket.for_fd(fd)
rescue
IO._sysclose(fd) rescue nil
end
sock
end
def accept_nonblock
begin
self._setnonblock(true)
self.accept
ensure
self._setnonblock(false)
end
end
def listen(backlog)
Socket._listen(self.fileno, backlog)
0
end
def sysaccept
Socket._accept(self.fileno)
end
end
class Socket
include Constants
end
class Socket
class Option
def initialize(family, level, optname, data)
@family = family
@level = level
@optname = optname
@data = data
end
def self.bool(family, level, optname, bool)
self.new(family, level, optname, [(bool ? 1 : 0)].pack('i'))
end
def self.int(family, level, optname, integer)
self.new(family, level, optname, [integer].pack('i'))
end
#def self.linger(family, level, optname, integer)
#end
attr_reader :data, :family, :level, :optname
def bool
@data.unpack('i')[0] != 0
end
def inspect
"#<Socket::Option: family:#{@family} level:#{@level} optname:#{@optname} #{@data.inspect}>"
end
def int
@data.unpack('i')[0]
end
def linger
raise NotImplementedError.new
end
def unpack(template)
raise NotImplementedError.new
end
end
end
class SocketError < StandardError; end
@@ -0,0 +1,459 @@
#if defined(AF_INET)
define_const(AF_INET);
#endif
#if defined(PF_INET)
define_const(PF_INET);
#endif
#if defined(AF_INET6)
define_const(AF_INET6);
#endif
#if defined(PF_INET6)
define_const(PF_INET6);
#endif
#if defined(AF_LINK)
define_const(AF_LINK);
#endif
#if defined(PF_LINK)
define_const(PF_LINK);
#endif
#if defined(AF_LOCAL)
define_const(AF_LOCAL);
#endif
#if defined(PF_LOCAL)
define_const(PF_LOCAL);
#endif
#if defined(AF_UNIX)
define_const(AF_UNIX);
#endif
#if defined(PF_UNIX)
define_const(PF_UNIX);
#endif
#if defined(AF_MAX)
define_const(AF_MAX);
#endif
#if defined(AF_UNSPEC)
define_const(AF_UNSPEC);
#endif
#if defined(PF_UNSPEC)
define_const(PF_UNSPEC);
#endif
#if defined(AF_ROUTE)
define_const(AF_ROUTE);
#endif
#if defined(PF_ROUTE)
define_const(PF_ROUTE);
#endif
#if defined(AI_CANONNAME)
define_const(AI_CANONNAME);
#endif
#if defined(AI_FQDN)
define_const(AI_FQDN);
#endif
#if defined(AI_NUMERICHOST)
define_const(AI_NUMERICHOST);
#endif
#if defined(AI_NUMERICSERV)
define_const(AI_NUMERICSERV);
#endif
#if defined(AI_PASSIVE)
define_const(AI_PASSIVE);
#endif
#if defined(IP_ADD_MEMBERSHIP)
define_const(IP_ADD_MEMBERSHIP);
#endif
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
define_const(IP_ADD_SOURCE_MEMBERSHIP);
#endif
#if defined(IP_BLOCK_SOURCE)
define_const(IP_BLOCK_SOURCE);
#endif
#if defined(IP_DROP_MEMBERSHIP)
define_const(IP_DROP_MEMBERSHIP);
#endif
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
define_const(IP_DROP_SOURCE_MEMBERSHIP);
#endif
#if defined(IP_FREEBIND)
define_const(IP_FREEBIND);
#endif
#if defined(IP_HDRINCL)
define_const(IP_HDRINCL);
#endif
#if defined(IP_IPSEC_POLICY)
define_const(IP_IPSEC_POLICY);
#endif
#if defined(IP_MINTTL)
define_const(IP_MINTTL);
#endif
#if defined(IP_MSFILTER)
define_const(IP_MSFILTER);
#endif
#if defined(IP_MTU)
define_const(IP_MTU);
#endif
#if defined(IP_MTU_DISCOVER)
define_const(IP_MTU_DISCOVER);
#endif
#if defined(IP_MULTICAST_ALL)
define_const(IP_MULTICAST_ALL);
#endif
#if defined(IP_MULTICAST_IF)
define_const(IP_MULTICAST_IF);
#endif
#if defined(IP_MULTICAST_LOOP)
define_const(IP_MULTICAST_LOOP);
#endif
#if defined(IP_MULTICAST_TTL)
define_const(IP_MULTICAST_TTL);
#endif
#if defined(IP_OPTIONS)
define_const(IP_OPTIONS);
#endif
#if defined(IP_ORIGDSTADDR)
define_const(IP_ORIGDSTADDR);
#endif
#if defined(IP_PASSSEC)
define_const(IP_PASSSEC);
#endif
#if defined(IP_PKTINFO)
define_const(IP_PKTINFO);
#endif
#if defined(IP_PKTOPTIONS)
define_const(IP_PKTOPTIONS);
#endif
#if defined(IP_PMTUDISC_DO)
define_const(IP_PMTUDISC_DO);
#endif
#if defined(IP_PMTUDISC_DONT)
define_const(IP_PMTUDISC_DONT);
#endif
#if defined(IP_PMTUDISC_PROBE)
define_const(IP_PMTUDISC_PROBE);
#endif
#if defined(IP_PMTUDISC_WANT)
define_const(IP_PMTUDISC_WANT);
#endif
#if defined(IP_RECVDSTADDR)
define_const(IP_RECVDSTADDR);
#endif
#if defined(IP_RECVERR)
define_const(IP_RECVERR);
#endif
#if defined(IP_RECVOPTS)
define_const(IP_RECVOPTS);
#endif
#if defined(IP_RECVORIGDSTADDR)
define_const(IP_RECVORIGDSTADDR);
#endif
#if defined(IP_RECVRETOPTS)
define_const(IP_RECVRETOPTS);
#endif
#if defined(IP_RECVTOS)
define_const(IP_RECVTOS);
#endif
#if defined(IP_RECVTTL)
define_const(IP_RECVTTL);
#endif
#if defined(IP_RETOPTS)
define_const(IP_RETOPTS);
#endif
#if defined(IP_ROUTER_ALERT)
define_const(IP_ROUTER_ALERT);
#endif
#if defined(IP_TOS)
define_const(IP_TOS);
#endif
#if defined(IP_TRANSPARENT)
define_const(IP_TRANSPARENT);
#endif
#if defined(IP_TTL)
define_const(IP_TTL);
#endif
#if defined(IP_UNBLOCK_SOURCE)
define_const(IP_UNBLOCK_SOURCE);
#endif
#if defined(IP_XFRM_POLICY)
define_const(IP_XFRM_POLICY);
#endif
#if defined(IPV6_JOIN_GROUP)
define_const(IPV6_JOIN_GROUP);
#endif
#if defined(IPV6_LEAVE_GROUP)
define_const(IPV6_LEAVE_GROUP);
#endif
#if defined(IPV6_MULTICAST_HOPS)
define_const(IPV6_MULTICAST_HOPS);
#endif
#if defined(IPV6_MULTICAST_IF)
define_const(IPV6_MULTICAST_IF);
#endif
#if defined(IPV6_MULTICAST_LOOP)
define_const(IPV6_MULTICAST_LOOP);
#endif
#if defined(IPV6_UNICAST_HOPS)
define_const(IPV6_UNICAST_HOPS);
#endif
#if defined(IPV6_V6ONLY)
define_const(IPV6_V6ONLY);
#endif
#if defined(IPPROTO_AH) || defined(_WINSOCKAPI_)
define_const(IPPROTO_AH);
#endif
#if defined(IPPROTO_DSTOPTS) || defined(_WINSOCKAPI_)
define_const(IPPROTO_DSTOPTS);
#endif
#if defined(IPPROTO_ESP) || defined(_WINSOCKAPI_)
define_const(IPPROTO_ESP);
#endif
#if defined(IPPROTO_FRAGMENT) || defined(_WINSOCKAPI_)
define_const(IPPROTO_FRAGMENT);
#endif
#if defined(IPPROTO_ICMP) || defined(_WINSOCKAPI_)
define_const(IPPROTO_ICMP);
#endif
#if defined(IPPROTO_ICMPV6) || defined(_WINSOCKAPI_)
define_const(IPPROTO_ICMPV6);
#endif
#if defined(IPPROTO_IP) || defined(_WINSOCKAPI_)
define_const(IPPROTO_IP);
#endif
#if defined(IPPROTO_IPV6) || defined(_WINSOCKAPI_)
define_const(IPPROTO_IPV6);
#endif
#if defined(IPPROTO_NONE) || defined(_WINSOCKAPI_)
define_const(IPPROTO_NONE);
#endif
#if defined(IPPROTO_RAW) || defined(_WINSOCKAPI_)
define_const(IPPROTO_RAW);
#endif
#if defined(IPPROTO_ROUTING) || defined(_WINSOCKAPI_)
define_const(IPPROTO_ROUTING);
#endif
#if defined(IPPROTO_TCP) || defined(_WINSOCKAPI_)
define_const(IPPROTO_TCP);
#endif
#if defined(IPPROTO_UDP) || defined(_WINSOCKAPI_)
define_const(IPPROTO_UDP);
#endif
#if defined(MCAST_BLOCK_SOURCE)
define_const(MCAST_BLOCK_SOURCE);
#endif
#if defined(MCAST_JOIN_GROUP)
define_const(MCAST_JOIN_GROUP);
#endif
#if defined(MCAST_JOIN_SOURCE_GROUP)
define_const(MCAST_JOIN_SOURCE_GROUP);
#endif
#if defined(MCAST_LEAVE_GROUP)
define_const(MCAST_LEAVE_GROUP);
#endif
#if defined(MCAST_LEAVE_SOURCE_GROUP)
define_const(MCAST_LEAVE_SOURCE_GROUP);
#endif
#if defined(MCAST_MSFILTER)
define_const(MCAST_MSFILTER);
#endif
#if defined(MCAST_UNBLOCK_SOURCE)
define_const(MCAST_UNBLOCK_SOURCE);
#endif
#if defined(MSG_BCAST)
define_const(MSG_BCAST);
#endif
#if defined(MSG_CTRUNC)
define_const(MSG_CTRUNC);
#endif
#if defined(MSG_DONTROUTE)
define_const(MSG_DONTROUTE);
#endif
#if defined(MSG_DONTWAIT)
define_const(MSG_DONTWAIT);
#endif
#if defined(MSG_EOR)
define_const(MSG_EOR);
#endif
#if defined(MSG_MCAST)
define_const(MSG_MCAST);
#endif
#if defined(MSG_NOSIGNAL)
define_const(MSG_NOSIGNAL);
#endif
#if defined(MSG_OOB)
define_const(MSG_OOB);
#endif
#if defined(MSG_PEEK)
define_const(MSG_PEEK);
#endif
#if defined(MSG_TRUNC)
define_const(MSG_TRUNC);
#endif
#if defined(MSG_WAITALL)
define_const(MSG_WAITALL);
#endif
#if defined(NI_DGRAM)
define_const(NI_DGRAM);
#endif
#if defined(NI_MAXHOST)
define_const(NI_MAXHOST);
#endif
#if defined(NI_MAXSERV)
define_const(NI_MAXSERV);
#endif
#if defined(NI_NAMEREQD)
define_const(NI_NAMEREQD);
#endif
#if defined(NI_NOFQDN)
define_const(NI_NOFQDN);
#endif
#if defined(NI_NUMERICHOST)
define_const(NI_NUMERICHOST);
#endif
#if defined(NI_NUMERICSERV)
define_const(NI_NUMERICSERV);
#endif
#if defined(SHUT_RD)
define_const(SHUT_RD);
#endif
#if defined(SHUT_WR)
define_const(SHUT_WR);
#endif
#if defined(SHUT_RDWR)
define_const(SHUT_RDWR);
#endif
#if defined(SO_BINDANY)
define_const(SO_BINDANY);
#endif
#if defined(SO_BROADCAST)
define_const(SO_BROADCAST);
#endif
#if defined(SO_DEBUG)
define_const(SO_DEBUG);
#endif
#if defined(SO_DONTROUTE)
define_const(SO_DONTROUTE);
#endif
#if defined(SO_ERROR)
define_const(SO_ERROR);
#endif
#if defined(SO_KEEPALIVE)
define_const(SO_KEEPALIVE);
#endif
#if defined(SO_LINGER)
define_const(SO_LINGER);
#endif
#if defined(SO_NOSIGPIPE)
define_const(SO_NOSIGPIPE);
#endif
#if defined(SO_OOBINLINE)
define_const(SO_OOBINLINE);
#endif
#if defined(SO_PEERCRED)
define_const(SO_PEERCRED);
#endif
#if defined(SO_RCVBUF)
define_const(SO_RCVBUF);
#endif
#if defined(SO_RCVLOWAT)
define_const(SO_RCVLOWAT);
#endif
#if defined(SO_RCVTIMEO)
define_const(SO_RCVTIMEO);
#endif
#if defined(SO_REUSEADDR)
define_const(SO_REUSEADDR);
#endif
#if defined(SO_REUSEPORT)
define_const(SO_REUSEPORT);
#endif
#if defined(SO_RTABLE)
define_const(SO_RTABLE);
#endif
#if defined(SO_SNDBUF)
define_const(SO_SNDBUF);
#endif
#if defined(SO_SNDLOWAT)
define_const(SO_SNDLOWAT);
#endif
#if defined(SO_SNDTIMEO)
define_const(SO_SNDTIMEO);
#endif
#if defined(SO_SPLICE)
define_const(SO_SPLICE);
#endif
#if defined(SO_TIMESTAMP)
define_const(SO_TIMESTAMP);
#endif
#if defined(SO_TYPE)
define_const(SO_TYPE);
#endif
#if defined(SOCK_DGRAM)
define_const(SOCK_DGRAM);
#endif
#if defined(SOCK_RAW)
define_const(SOCK_RAW);
#endif
#if defined(SOCK_SEQPACKET)
define_const(SOCK_SEQPACKET);
#endif
#if defined(SOCK_STREAM)
define_const(SOCK_STREAM);
#endif
#if defined(SOL_SOCKET)
define_const(SOL_SOCKET);
#endif
#if defined(SOL_IP)
define_const(SOL_IP);
#endif
#if defined(SOL_TCP)
define_const(SOL_TCP);
#endif
#if defined(TCP_CONGCTL)
define_const(TCP_CONGCTL);
#endif
#if defined(TCP_CONGESTION)
define_const(TCP_CONGESTION);
#endif
#if defined(TCP_CORK)
define_const(TCP_CORK);
#endif
#if defined(TCP_DEFER_ACCEPT)
define_const(TCP_DEFER_ACCEPT);
#endif
#if defined(TCP_INFO)
define_const(TCP_INFO);
#endif
#if defined(TCP_KEEPCNT)
define_const(TCP_KEEPCNT);
#endif
#if defined(TCP_KEEPIDLE)
define_const(TCP_KEEPIDLE);
#endif
#if defined(TCP_KEEPINIT)
define_const(TCP_KEEPINIT);
#endif
#if defined(TCP_KEEPINTVL)
define_const(TCP_KEEPINTVL);
#endif
#if defined(TCP_LINGER2)
define_const(TCP_LINGER2);
#endif
#if defined(TCP_MAXSEG)
define_const(TCP_MAXSEG);
#endif
#if defined(TCP_MD5SIG)
define_const(TCP_MD5SIG);
#endif
#if defined(TCP_NODELAY)
define_const(TCP_NODELAY);
#endif
#if defined(TCP_QUICKACK)
define_const(TCP_QUICKACK);
#endif
#if defined(TCP_SACK_ENABLE)
define_const(TCP_SACK_ENABLE);
#endif
#if defined(TCP_SYNCNT)
define_const(TCP_SYNCNT);
#endif
#if defined(TCP_WINDOW_CLAMP)
define_const(TCP_WINDOW_CLAMP);
#endif
@@ -0,0 +1,165 @@
AF_INET
PF_INET
AF_INET6
PF_INET6
AF_LINK
PF_LINK
AF_LOCAL
PF_LOCAL
AF_UNIX
PF_UNIX
AF_MAX
AF_UNSPEC
PF_UNSPEC
AF_ROUTE
PF_ROUTE
AI_CANONNAME
AI_FQDN
AI_NUMERICHOST
AI_NUMERICSERV
AI_PASSIVE
IP_ADD_MEMBERSHIP
IP_ADD_SOURCE_MEMBERSHIP
IP_BLOCK_SOURCE
IP_DROP_MEMBERSHIP
IP_DROP_SOURCE_MEMBERSHIP
IP_FREEBIND
IP_HDRINCL
IP_IPSEC_POLICY
IP_MINTTL
IP_MSFILTER
IP_MTU
IP_MTU_DISCOVER
IP_MULTICAST_ALL
IP_MULTICAST_IF
IP_MULTICAST_LOOP
IP_MULTICAST_TTL
IP_OPTIONS
IP_ORIGDSTADDR
IP_PASSSEC
IP_PKTINFO
IP_PKTOPTIONS
IP_PMTUDISC_DO
IP_PMTUDISC_DONT
IP_PMTUDISC_PROBE
IP_PMTUDISC_WANT
IP_RECVDSTADDR
IP_RECVERR
IP_RECVOPTS
IP_RECVORIGDSTADDR
IP_RECVRETOPTS
IP_RECVTOS
IP_RECVTTL
IP_RETOPTS
IP_ROUTER_ALERT
IP_TOS
IP_TRANSPARENT
IP_TTL
IP_UNBLOCK_SOURCE
IP_XFRM_POLICY
IPV6_JOIN_GROUP
IPV6_LEAVE_GROUP
IPV6_MULTICAST_HOPS
IPV6_MULTICAST_IF
IPV6_MULTICAST_LOOP
IPV6_UNICAST_HOPS
IPV6_V6ONLY
IPPROTO_AH
IPPROTO_DSTOPTS
IPPROTO_ESP
IPPROTO_FRAGMENT
IPPROTO_ICMP
IPPROTO_ICMPV6
IPPROTO_IP
IPPROTO_IPV6
IPPROTO_NONE
IPPROTO_RAW
IPPROTO_ROUTING
IPPROTO_TCP
IPPROTO_UDP
MCAST_BLOCK_SOURCE
MCAST_JOIN_GROUP
MCAST_JOIN_SOURCE_GROUP
MCAST_LEAVE_GROUP
MCAST_LEAVE_SOURCE_GROUP
MCAST_MSFILTER
MCAST_UNBLOCK_SOURCE
MSG_BCAST
MSG_CTRUNC
MSG_DONTROUTE
MSG_DONTWAIT
MSG_EOR
MSG_MCAST
MSG_NOSIGNAL
MSG_OOB
MSG_PEEK
MSG_TRUNC
MSG_WAITALL
NI_DGRAM
NI_MAXHOST
NI_MAXSERV
NI_NAMEREQD
NI_NOFQDN
NI_NUMERICHOST
NI_NUMERICSERV
SHUT_RD
SHUT_WR
SHUT_RDWR
SO_BINDANY
SO_BROADCAST
SO_DEBUG
SO_DONTROUTE
SO_ERROR
SO_KEEPALIVE
SO_LINGER
SO_NOSIGPIPE
SO_OOBINLINE
SO_PEERCRED
SO_RCVBUF
SO_RCVLOWAT
SO_RCVTIMEO
SO_REUSEADDR
SO_REUSEPORT
SO_RTABLE
SO_SNDBUF
SO_SNDLOWAT
SO_SNDTIMEO
SO_SPLICE
SO_TIMESTAMP
SO_TYPE
SOCK_DGRAM
SOCK_RAW
SOCK_SEQPACKET
SOCK_STREAM
SOL_SOCKET
SOL_IP
SOL_TCP
TCP_CONGCTL
TCP_CONGESTION
TCP_CORK
TCP_DEFER_ACCEPT
TCP_INFO
TCP_KEEPCNT
TCP_KEEPIDLE
TCP_KEEPINIT
TCP_KEEPINTVL
TCP_LINGER2
TCP_MAXSEG
TCP_MD5SIG
TCP_NODELAY
TCP_QUICKACK
TCP_SACK_ENABLE
TCP_SYNCNT
TCP_WINDOW_CLAMP
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env ruby
Dir.chdir(File.dirname($0))
f = File.open("const.cstub", "w")
IO.readlines("const.def").each { |name|
name.sub(/^#.*/, "")
name.strip!
next if name.empty?
f.write <<CODE
#if defined(#{name})#{name.start_with?('IPPROTO_') ? ' || defined(_WINSOCKAPI_)' : ''}
define_const(#{name});
#endif
CODE
}
@@ -0,0 +1,957 @@
/*
** socket.c - Socket module
**
** See Copyright Notice in mruby.h
*/
#ifdef _WIN32
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <winerror.h>
#define SHUT_RDWR SD_BOTH
#ifndef _SSIZE_T_DEFINED
typedef int ssize_t;
#endif
typedef int fsize_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
typedef size_t fsize_t;
#endif
#include <stddef.h>
#include <string.h>
#include "mruby.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/data.h"
#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
#include "mruby/error.h"
#include "mruby/ext/io.h"
#if !defined(HAVE_SA_LEN)
#if (defined(BSD) && (BSD >= 199006))
#define HAVE_SA_LEN 1
#else
#define HAVE_SA_LEN 0
#endif
#endif
#define E_SOCKET_ERROR (mrb_class_get(mrb, "SocketError"))
#if !defined(mrb_cptr)
#define mrb_cptr_value(m,p) mrb_voidp_value((m),(p))
#define mrb_cptr(o) mrb_voidp(o)
#define mrb_cptr_p(o) mrb_voidp_p(o)
#endif
#ifdef _WIN32
static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
{
if (af == AF_INET)
{
struct sockaddr_in in;
memset(&in, 0, sizeof(in));
in.sin_family = AF_INET;
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
getnameinfo((struct sockaddr *)&in, sizeof(struct
sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
else if (af == AF_INET6)
{
struct sockaddr_in6 in;
memset(&in, 0, sizeof(in));
in.sin6_family = AF_INET6;
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
getnameinfo((struct sockaddr *)&in, sizeof(struct
sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
return NULL;
}
static int inet_pton(int af, const char *src, void *dst)
{
struct addrinfo hints, *res, *ressave;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = af;
if (getaddrinfo(src, NULL, &hints, &res) != 0)
{
printf("Couldn't resolve host %s\n", src);
return -1;
}
ressave = res;
while (res)
{
memcpy(dst, res->ai_addr, res->ai_addrlen);
res = res->ai_next;
}
freeaddrinfo(ressave);
return 0;
}
#endif
static mrb_value
mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
{
struct addrinfo hints, *res0, *res;
mrb_value ai, ary, family, lastai, nodename, protocol, sa, service, socktype;
mrb_int flags;
int arena_idx, error;
const char *hostname = NULL, *servname = NULL;
ary = mrb_ary_new(mrb);
arena_idx = mrb_gc_arena_save(mrb); /* ary must be on arena! */
family = socktype = protocol = mrb_nil_value();
flags = 0;
mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
if (mrb_string_p(nodename)) {
hostname = RSTRING_CSTR(mrb, nodename);
} else if (mrb_nil_p(nodename)) {
hostname = NULL;
} else {
mrb_raise(mrb, E_TYPE_ERROR, "nodename must be String or nil");
}
if (mrb_string_p(service)) {
servname = RSTRING_CSTR(mrb, service);
} else if (mrb_fixnum_p(service)) {
servname = RSTRING_PTR(mrb_fixnum_to_str(mrb, service, 10));
} else if (mrb_nil_p(service)) {
servname = NULL;
} else {
mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Fixnum, or nil");
}
memset(&hints, 0, sizeof(hints));
hints.ai_flags = (int)flags;
if (mrb_fixnum_p(family)) {
hints.ai_family = (int)mrb_fixnum(family);
}
if (mrb_fixnum_p(socktype)) {
hints.ai_socktype = (int)mrb_fixnum(socktype);
}
if (mrb_fixnum_p(protocol)) {
hints.ai_protocol = (int)mrb_fixnum(protocol);
}
lastai = mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "_lastai"));
if (mrb_cptr_p(lastai)) {
freeaddrinfo((struct addrinfo*)mrb_cptr(lastai));
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
}
error = getaddrinfo(hostname, servname, &hints, &res0);
if (error) {
mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %s", gai_strerror(error));
}
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_cptr_value(mrb, res0));
for (res = res0; res != NULL; res = res->ai_next) {
sa = mrb_str_new(mrb, (char*)res->ai_addr, res->ai_addrlen);
ai = mrb_funcall(mrb, klass, "new", 4, sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol));
mrb_ary_push(mrb, ary, ai);
mrb_gc_arena_restore(mrb, arena_idx);
}
freeaddrinfo(res0);
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
return ary;
}
static mrb_value
mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
{
mrb_int flags;
mrb_value ary, host, sastr, serv;
int error;
flags = 0;
mrb_get_args(mrb, "|i", &flags);
host = mrb_str_buf_new(mrb, NI_MAXHOST);
serv = mrb_str_buf_new(mrb, NI_MAXSERV);
sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
if (!mrb_string_p(sastr)) {
mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr");
}
error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags);
if (error) {
mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
}
ary = mrb_ary_new_capa(mrb, 2);
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
mrb_ary_push(mrb, ary, host);
mrb_str_resize(mrb, serv, strlen(RSTRING_PTR(serv)));
mrb_ary_push(mrb, ary, serv);
return ary;
}
#ifndef _WIN32
static mrb_value
mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
{
mrb_value sastr;
sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX)
mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address");
if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) {
return mrb_str_new(mrb, "", 0);
} else {
return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
}
}
#endif
static mrb_value
sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen)
{
mrb_value ary, host;
unsigned short port;
const char *afstr;
switch (sa->sa_family) {
case AF_INET:
afstr = "AF_INET";
port = ((struct sockaddr_in *)sa)->sin_port;
break;
case AF_INET6:
afstr = "AF_INET6";
port = ((struct sockaddr_in6 *)sa)->sin6_port;
break;
default:
mrb_raise(mrb, E_ARGUMENT_ERROR, "bad af");
return mrb_nil_value();
}
port = ntohs(port);
host = mrb_str_buf_new(mrb, NI_MAXHOST);
if (getnameinfo(sa, salen, RSTRING_PTR(host), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == -1)
mrb_sys_fail(mrb, "getnameinfo");
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
ary = mrb_ary_new_capa(mrb, 4);
mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, afstr));
mrb_ary_push(mrb, ary, mrb_fixnum_value(port));
mrb_ary_push(mrb, ary, host);
mrb_ary_push(mrb, ary, host);
return ary;
}
static int
socket_fd(mrb_state *mrb, mrb_value sock)
{
return (int)mrb_fixnum(mrb_funcall(mrb, sock, "fileno", 0));
}
static int
socket_family(int s)
{
struct sockaddr_storage ss;
socklen_t salen;
salen = sizeof(ss);
if (getsockname(s, (struct sockaddr *)&ss, &salen) == -1)
return AF_UNSPEC;
return ss.ss_family;
}
static mrb_value
mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
{
#ifdef HAVE_GETPEEREID
mrb_value ary;
gid_t egid;
uid_t euid;
int s;
s = socket_fd(mrb, self);
if (getpeereid(s, &euid, &egid) != 0)
mrb_sys_fail(mrb, "getpeereid");
ary = mrb_ary_new_capa(mrb, 2);
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)euid));
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid));
return ary;
#else
mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system");
return mrb_nil_value();
#endif
}
static mrb_value
mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self)
{
struct sockaddr_storage ss;
socklen_t salen;
salen = sizeof(ss);
if (getpeername(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0)
mrb_sys_fail(mrb, "getpeername");
return mrb_str_new(mrb, (char*)&ss, salen);
}
static mrb_value
mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self)
{
struct sockaddr_storage ss;
socklen_t salen;
salen = sizeof(ss);
if (getsockname(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0)
mrb_sys_fail(mrb, "getsockname");
return mrb_str_new(mrb, (char*)&ss, salen);
}
static mrb_value
mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self)
{
char opt[8];
int s;
mrb_int family, level, optname;
mrb_value c, data;
socklen_t optlen;
mrb_get_args(mrb, "ii", &level, &optname);
s = socket_fd(mrb, self);
optlen = sizeof(opt);
if (getsockopt(s, (int)level, (int)optname, opt, &optlen) == -1)
mrb_sys_fail(mrb, "getsockopt");
c = mrb_const_get(mrb, mrb_obj_value(mrb_class_get(mrb, "Socket")), mrb_intern_lit(mrb, "Option"));
family = socket_family(s);
data = mrb_str_new(mrb, opt, optlen);
return mrb_funcall(mrb, c, "new", 4, mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data);
}
static mrb_value
mrb_basicsocket_recv(mrb_state *mrb, mrb_value self)
{
ssize_t n;
mrb_int maxlen, flags = 0;
mrb_value buf;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
buf = mrb_str_buf_new(mrb, maxlen);
n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags);
if (n == -1)
mrb_sys_fail(mrb, "recv");
mrb_str_resize(mrb, buf, (mrb_int)n);
return buf;
}
static mrb_value
mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self)
{
ssize_t n;
mrb_int maxlen, flags = 0;
mrb_value ary, buf, sa;
socklen_t socklen;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
buf = mrb_str_buf_new(mrb, maxlen);
socklen = sizeof(struct sockaddr_storage);
sa = mrb_str_buf_new(mrb, socklen);
n = recvfrom(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr *)RSTRING_PTR(sa), &socklen);
if (n == -1)
mrb_sys_fail(mrb, "recvfrom");
mrb_str_resize(mrb, buf, (mrb_int)n);
mrb_str_resize(mrb, sa, (mrb_int)socklen);
ary = mrb_ary_new_capa(mrb, 2);
mrb_ary_push(mrb, ary, buf);
mrb_ary_push(mrb, ary, sa);
return ary;
}
static mrb_value
mrb_basicsocket_send(mrb_state *mrb, mrb_value self)
{
ssize_t n;
mrb_int flags;
mrb_value dest, mesg;
dest = mrb_nil_value();
mrb_get_args(mrb, "Si|S", &mesg, &flags, &dest);
if (mrb_nil_p(dest)) {
n = send(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags);
} else {
n = sendto(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags, (const struct sockaddr*)RSTRING_PTR(dest), (fsize_t)RSTRING_LEN(dest));
}
if (n == -1)
mrb_sys_fail(mrb, "send");
return mrb_fixnum_value((mrb_int)n);
}
static mrb_value
mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self)
{
int fd, flags;
mrb_bool nonblocking;
#ifdef _WIN32
u_long mode = 1;
#endif
mrb_get_args(mrb, "b", &nonblocking);
fd = socket_fd(mrb, self);
#ifdef _WIN32
flags = ioctlsocket(fd, FIONBIO, &mode);
if (flags != NO_ERROR)
mrb_sys_fail(mrb, "ioctlsocket");
#else
flags = fcntl(fd, F_GETFL, 0);
if (flags == 1)
mrb_sys_fail(mrb, "fcntl");
if (nonblocking)
flags |= O_NONBLOCK;
else
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1)
mrb_sys_fail(mrb, "fcntl");
#endif
return mrb_nil_value();
}
static mrb_value
mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
{
int s;
mrb_int argc, level = 0, optname;
mrb_value optval, so;
argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval);
if (argc == 3) {
if (!mrb_fixnum_p(so)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "level is not an integer");
}
level = mrb_fixnum(so);
if (mrb_string_p(optval)) {
/* that's good */
} else if (mrb_true_p(optval) || mrb_false_p(optval)) {
mrb_int i = mrb_test(optval) ? 1 : 0;
optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
} else if (mrb_fixnum_p(optval)) {
if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) {
char uc = (char)mrb_fixnum(optval);
optval = mrb_str_new(mrb, &uc, sizeof(uc));
} else {
mrb_int i = mrb_fixnum(optval);
optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
}
} else {
mrb_raise(mrb, E_ARGUMENT_ERROR, "optval should be true, false, an integer, or a string");
}
} else if (argc == 1) {
if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0));
optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
optval = mrb_funcall(mrb, so, "data", 0);
} else {
mrb_argnum_error(mrb, argc, 3, 3);
}
s = socket_fd(mrb, self);
if (setsockopt(s, (int)level, (int)optname, RSTRING_PTR(optval), (socklen_t)RSTRING_LEN(optval)) == -1)
mrb_sys_fail(mrb, "setsockopt");
return mrb_fixnum_value(0);
}
static mrb_value
mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self)
{
mrb_int how = SHUT_RDWR;
mrb_get_args(mrb, "|i", &how);
if (shutdown(socket_fd(mrb, self), (int)how) != 0)
mrb_sys_fail(mrb, "shutdown");
return mrb_fixnum_value(0);
}
static mrb_value
mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self)
{
mrb_bool b;
struct mrb_io *io_p;
mrb_get_args(mrb, "b", &b);
io_p = (struct mrb_io*)DATA_PTR(self);
if (io_p) {
io_p->is_socket = b;
}
return mrb_bool_value(b);
}
static mrb_value
mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass)
{
mrb_int af, n;
char *addr, buf[50];
mrb_get_args(mrb, "is", &af, &addr, &n);
if ((af == AF_INET && n != 4) || (af == AF_INET6 && n != 16))
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
if (inet_ntop((int)af, addr, buf, sizeof(buf)) == NULL)
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
return mrb_str_new_cstr(mrb, buf);
}
static mrb_value
mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass)
{
mrb_int af, n;
char *bp, buf[50];
mrb_get_args(mrb, "is", &af, &bp, &n);
if ((size_t)n > sizeof(buf) - 1)
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
memcpy(buf, bp, n);
buf[n] = '\0';
if (af == AF_INET) {
struct in_addr in;
if (inet_pton(AF_INET, buf, (void *)&in.s_addr) != 1)
goto invalid;
return mrb_str_new(mrb, (char*)&in.s_addr, 4);
} else if (af == AF_INET6) {
struct in6_addr in6;
if (inet_pton(AF_INET6, buf, (void *)&in6.s6_addr) != 1)
goto invalid;
return mrb_str_new(mrb, (char*)&in6.s6_addr, 16);
} else
mrb_raise(mrb, E_ARGUMENT_ERROR, "unsupported address family");
invalid:
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
return mrb_nil_value(); /* dummy */
}
static mrb_value
mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self)
{
struct sockaddr_storage ss;
socklen_t socklen;
mrb_value a, buf, pair;
mrb_int flags, maxlen;
ssize_t n;
int fd;
fd = socket_fd(mrb, self);
flags = 0;
mrb_get_args(mrb, "i|i", &maxlen, &flags);
buf = mrb_str_buf_new(mrb, maxlen);
socklen = sizeof(ss);
n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags,
(struct sockaddr *)&ss, &socklen);
if (n == -1) {
mrb_sys_fail(mrb, "recvfrom");
}
mrb_str_resize(mrb, buf, (mrb_int)n);
a = sa2addrlist(mrb, (struct sockaddr *)&ss, socklen);
pair = mrb_ary_new_capa(mrb, 2);
mrb_ary_push(mrb, pair, buf);
mrb_ary_push(mrb, pair, a);
return pair;
}
static mrb_value
mrb_socket_gethostname(mrb_state *mrb, mrb_value cls)
{
mrb_value buf;
size_t bufsize;
#ifdef HOST_NAME_MAX
bufsize = HOST_NAME_MAX + 1;
#else
bufsize = 256;
#endif
buf = mrb_str_buf_new(mrb, (mrb_int)bufsize);
if (gethostname(RSTRING_PTR(buf), (fsize_t)bufsize) != 0)
mrb_sys_fail(mrb, "gethostname");
mrb_str_resize(mrb, buf, (mrb_int)strlen(RSTRING_PTR(buf)));
return buf;
}
static mrb_value
mrb_socket_accept(mrb_state *mrb, mrb_value klass)
{
int s1;
mrb_int s0;
mrb_get_args(mrb, "i", &s0);
s1 = (int)accept(s0, NULL, NULL);
if (s1 == -1) {
mrb_sys_fail(mrb, "accept");
}
return mrb_fixnum_value(s1);
}
static mrb_value
mrb_socket_accept2(mrb_state *mrb, mrb_value klass)
{
mrb_value ary, sastr;
int s1;
mrb_int s0;
socklen_t socklen;
mrb_get_args(mrb, "i", &s0);
socklen = sizeof(struct sockaddr_storage);
sastr = mrb_str_buf_new(mrb, socklen);
s1 = (int)accept(s0, (struct sockaddr *)RSTRING_PTR(sastr), &socklen);
if (s1 == -1) {
mrb_sys_fail(mrb, "accept");
}
// XXX: possible descriptor leakage here!
mrb_str_resize(mrb, sastr, socklen);
ary = mrb_ary_new_capa(mrb, 2);
mrb_ary_push(mrb, ary, mrb_fixnum_value(s1));
mrb_ary_push(mrb, ary, sastr);
return ary;
}
static mrb_value
mrb_socket_bind(mrb_state *mrb, mrb_value klass)
{
mrb_value sastr;
mrb_int s;
mrb_get_args(mrb, "iS", &s, &sastr);
if (bind((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
mrb_sys_fail(mrb, "bind");
}
return mrb_nil_value();
}
static mrb_value
mrb_socket_connect(mrb_state *mrb, mrb_value klass)
{
mrb_value sastr;
mrb_int s;
mrb_get_args(mrb, "iS", &s, &sastr);
if (connect((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
mrb_sys_fail(mrb, "connect");
}
return mrb_nil_value();
}
static mrb_value
mrb_socket_listen(mrb_state *mrb, mrb_value klass)
{
mrb_int backlog, s;
mrb_get_args(mrb, "ii", &s, &backlog);
if (listen((int)s, (int)backlog) == -1) {
mrb_sys_fail(mrb, "listen");
}
return mrb_nil_value();
}
static mrb_value
mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass)
{
const struct sockaddr *sa;
mrb_value str;
mrb_get_args(mrb, "S", &str);
if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) {
mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
}
sa = (const struct sockaddr *)RSTRING_PTR(str);
return mrb_fixnum_value(sa->sa_family);
}
static mrb_value
mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
mrb_raise(mrb, E_NOTIMP_ERROR, "sockaddr_un unsupported on Windows");
return mrb_nil_value();
#else
struct sockaddr_un *sunp;
mrb_value path, s;
mrb_get_args(mrb, "S", &path);
if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %d bytes)", (int)sizeof(sunp->sun_path) - 1);
}
s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
sunp = (struct sockaddr_un *)RSTRING_PTR(s);
#if HAVE_SA_LEN
sunp->sun_len = sizeof(struct sockaddr_un);
#endif
sunp->sun_family = AF_UNIX;
memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
sunp->sun_path[RSTRING_LEN(path)] = '\0';
mrb_str_resize(mrb, s, sizeof(struct sockaddr_un));
return s;
#endif
}
static mrb_value
mrb_socket_socketpair(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
mrb_raise(mrb, E_NOTIMP_ERROR, "socketpair unsupported on Windows");
return mrb_nil_value();
#else
mrb_value ary;
mrb_int domain, type, protocol;
int sv[2];
mrb_get_args(mrb, "iii", &domain, &type, &protocol);
if (socketpair(domain, type, protocol, sv) == -1) {
mrb_sys_fail(mrb, "socketpair");
}
// XXX: possible descriptor leakage here!
ary = mrb_ary_new_capa(mrb, 2);
mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[0]));
mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[1]));
return ary;
#endif
}
static mrb_value
mrb_socket_socket(mrb_state *mrb, mrb_value klass)
{
mrb_int domain, type, protocol;
int s;
mrb_get_args(mrb, "iii", &domain, &type, &protocol);
s = (int)socket((int)domain, (int)type, (int)protocol);
if (s == -1)
mrb_sys_fail(mrb, "socket");
return mrb_fixnum_value(s);
}
static mrb_value
mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass)
{
struct RClass *c = mrb_class_ptr(klass);
enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
/* copied from mrb_instance_alloc() */
if (ttype == 0) ttype = MRB_TT_OBJECT;
return mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c));
}
/* Windows overrides for IO methods on BasicSocket objects.
* This is because sockets on Windows are not the same as file
* descriptors, and thus functions which operate on file descriptors
* will break on socket descriptors.
*/
#ifdef _WIN32
static mrb_value
mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self)
{
if (closesocket(socket_fd(mrb, self)) != NO_ERROR)
mrb_raise(mrb, E_SOCKET_ERROR, "closesocket unsuccessful");
return mrb_nil_value();
}
#define E_EOF_ERROR (mrb_class_get(mrb, "EOFError"))
static mrb_value
mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self)
{
int sd, ret;
mrb_value buf = mrb_nil_value();
mrb_int maxlen;
mrb_get_args(mrb, "i|S", &maxlen, &buf);
if (maxlen < 0) {
return mrb_nil_value();
}
if (mrb_nil_p(buf)) {
buf = mrb_str_new(mrb, NULL, maxlen);
}
if (RSTRING_LEN(buf) != maxlen) {
buf = mrb_str_resize(mrb, buf, maxlen);
}
sd = socket_fd(mrb, self);
ret = recv(sd, RSTRING_PTR(buf), (int)maxlen, 0);
switch (ret) {
case 0: /* EOF */
if (maxlen == 0) {
buf = mrb_str_new_cstr(mrb, "");
} else {
mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File");
}
break;
case SOCKET_ERROR: /* Error */
mrb_sys_fail(mrb, "recv");
break;
default:
if (RSTRING_LEN(buf) != ret) {
buf = mrb_str_resize(mrb, buf, ret);
}
break;
}
return buf;
}
static mrb_value
mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self)
{
mrb_raise(mrb, E_NOTIMP_ERROR, "sysseek not implemented for windows sockets");
return mrb_nil_value();
}
static mrb_value
mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self)
{
int n;
SOCKET sd;
mrb_value str;
sd = socket_fd(mrb, self);
mrb_get_args(mrb, "S", &str);
n = send(sd, RSTRING_PTR(str), (int)RSTRING_LEN(str), 0);
if (n == SOCKET_ERROR)
mrb_sys_fail(mrb, "send");
return mrb_fixnum_value(n);
}
#endif
void
mrb_mruby_socket_gem_init(mrb_state* mrb)
{
struct RClass *io, *ai, *sock, *bsock, *ipsock, *tcpsock;
struct RClass *constants;
#ifdef _WIN32
WSADATA wsaData;
int result;
result = WSAStartup(MAKEWORD(2,2), &wsaData);
if (result != NO_ERROR)
mrb_raise(mrb, E_RUNTIME_ERROR, "WSAStartup failed");
#endif
ai = mrb_define_class(mrb, "Addrinfo", mrb->object_class);
mrb_mod_cv_set(mrb, ai, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
mrb_define_class_method(mrb, ai, "getaddrinfo", mrb_addrinfo_getaddrinfo, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(4));
mrb_define_method(mrb, ai, "getnameinfo", mrb_addrinfo_getnameinfo, MRB_ARGS_OPT(1));
#ifndef _WIN32
mrb_define_method(mrb, ai, "unix_path", mrb_addrinfo_unix_path, MRB_ARGS_NONE());
#endif
io = mrb_class_get(mrb, "IO");
bsock = mrb_define_class(mrb, "BasicSocket", io);
mrb_define_method(mrb, bsock, "_recvfrom", mrb_basicsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_method(mrb, bsock, "_setnonblock", mrb_basicsocket_setnonblock, MRB_ARGS_REQ(1));
mrb_define_method(mrb, bsock, "getpeereid", mrb_basicsocket_getpeereid, MRB_ARGS_NONE());
mrb_define_method(mrb, bsock, "getpeername", mrb_basicsocket_getpeername, MRB_ARGS_NONE());
mrb_define_method(mrb, bsock, "getsockname", mrb_basicsocket_getsockname, MRB_ARGS_NONE());
mrb_define_method(mrb, bsock, "getsockopt", mrb_basicsocket_getsockopt, MRB_ARGS_REQ(2));
mrb_define_method(mrb, bsock, "recv", mrb_basicsocket_recv, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
// #recvmsg(maxlen, flags=0)
mrb_define_method(mrb, bsock, "send", mrb_basicsocket_send, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(1));
// #sendmsg
// #sendmsg_nonblock
mrb_define_method(mrb, bsock, "setsockopt", mrb_basicsocket_setsockopt, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(2));
mrb_define_method(mrb, bsock, "shutdown", mrb_basicsocket_shutdown, MRB_ARGS_OPT(1));
mrb_define_method(mrb, bsock, "_is_socket=", mrb_basicsocket_set_is_socket, MRB_ARGS_REQ(1));
ipsock = mrb_define_class(mrb, "IPSocket", bsock);
mrb_define_class_method(mrb, ipsock, "ntop", mrb_ipsocket_ntop, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, ipsock, "pton", mrb_ipsocket_pton, MRB_ARGS_REQ(2));
mrb_define_method(mrb, ipsock, "recvfrom", mrb_ipsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
tcpsock = mrb_define_class(mrb, "TCPSocket", ipsock);
mrb_define_class_method(mrb, tcpsock, "_allocate", mrb_tcpsocket_allocate, MRB_ARGS_NONE());
mrb_define_class(mrb, "TCPServer", tcpsock);
mrb_define_class(mrb, "UDPSocket", ipsock);
//#recvfrom_nonblock
sock = mrb_define_class(mrb, "Socket", bsock);
mrb_define_class_method(mrb, sock, "_accept", mrb_socket_accept, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, sock, "_accept2", mrb_socket_accept2, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, sock, "_bind", mrb_socket_bind, MRB_ARGS_REQ(3));
mrb_define_class_method(mrb, sock, "_connect", mrb_socket_connect, MRB_ARGS_REQ(3));
mrb_define_class_method(mrb, sock, "_listen", mrb_socket_listen, MRB_ARGS_REQ(2));
mrb_define_class_method(mrb, sock, "_sockaddr_family", mrb_socket_sockaddr_family, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, sock, "_socket", mrb_socket_socket, MRB_ARGS_REQ(3));
//mrb_define_class_method(mrb, sock, "gethostbyaddr", mrb_socket_gethostbyaddr, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
//mrb_define_class_method(mrb, sock, "gethostbyname", mrb_socket_gethostbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_class_method(mrb, sock, "gethostname", mrb_socket_gethostname, MRB_ARGS_NONE());
//mrb_define_class_method(mrb, sock, "getservbyname", mrb_socket_getservbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
//mrb_define_class_method(mrb, sock, "getservbyport", mrb_socket_getservbyport, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_class_method(mrb, sock, "sockaddr_un", mrb_socket_sockaddr_un, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, sock, "socketpair", mrb_socket_socketpair, MRB_ARGS_REQ(3));
//mrb_define_method(mrb, sock, "sysaccept", mrb_socket_accept, MRB_ARGS_NONE());
#ifndef _WIN32
mrb_define_class(mrb, "UNIXSocket", bsock);
//mrb_define_class_method(mrb, usock, "pair", mrb_unixsocket_open, MRB_ARGS_OPT(2));
//mrb_define_class_method(mrb, usock, "socketpair", mrb_unixsocket_open, MRB_ARGS_OPT(2));
//mrb_define_method(mrb, usock, "recv_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
//mrb_define_method(mrb, usock, "recvfrom", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
//mrb_define_method(mrb, usock, "send_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
#endif
/* Windows IO Method Overrides on BasicSocket */
#ifdef _WIN32
mrb_define_method(mrb, bsock, "close", mrb_win32_basicsocket_close, MRB_ARGS_NONE());
mrb_define_method(mrb, bsock, "sysread", mrb_win32_basicsocket_sysread, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_method(mrb, bsock, "sysseek", mrb_win32_basicsocket_sysseek, MRB_ARGS_REQ(1));
mrb_define_method(mrb, bsock, "syswrite", mrb_win32_basicsocket_syswrite, MRB_ARGS_REQ(1));
#endif
constants = mrb_define_module_under(mrb, sock, "Constants");
#define define_const(SYM) \
do { \
mrb_define_const(mrb, constants, #SYM, mrb_fixnum_value(SYM)); \
} while (0)
#include "const.cstub"
}
void
mrb_mruby_socket_gem_final(mrb_state* mrb)
{
mrb_value ai;
ai = mrb_mod_cv_get(mrb, mrb_class_get(mrb, "Addrinfo"), mrb_intern_lit(mrb, "_lastai"));
if (mrb_cptr_p(ai)) {
freeaddrinfo((struct addrinfo*)mrb_cptr(ai));
}
#ifdef _WIN32
WSACleanup();
#endif
}
@@ -0,0 +1,91 @@
assert('Addrinfo') do
assert_equal(Class, Addrinfo.class)
end
assert('super class of Addrinfo') do
assert_equal(Object, Addrinfo.superclass)
end
assert('Addrinfo.getaddrinfo') do
ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM)
assert_true(ary.size >= 1)
ai = ary[0]
assert_equal(ai.afamily, Socket::AF_INET)
assert_equal(ai.pfamily, Socket::PF_INET)
assert_equal(ai.socktype, Socket::SOCK_STREAM)
assert_equal(ai.ip_address, '127.0.0.1')
assert_equal(ai.ip_port, 53)
end
assert('Addrinfo.foreach') do
# assume Addrinfo.getaddrinfo works well
a = Addrinfo.getaddrinfo("localhost", 80)
b = []
Addrinfo.foreach("localhost", 80) { |ai| b << ai }
assert_equal(a.size, b.size)
end
assert('Addrinfo.ip') do
ai = Addrinfo.ip('127.0.0.1')
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(0, ai.ip_port)
assert_equal(0, ai.socktype)
assert_equal(0, ai.protocol)
end
assert('Addrinfo.tcp') do
ai = Addrinfo.tcp('127.0.0.1', 25)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(25, ai.ip_port)
assert_equal(Socket::SOCK_STREAM, ai.socktype)
assert_equal(Socket::IPPROTO_TCP, ai.protocol)
end
assert('Addrinfo.udp') do
ai = Addrinfo.udp('127.0.0.1', 53)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(53, ai.ip_port)
assert_equal(Socket::SOCK_DGRAM, ai.socktype)
assert_equal(Socket::IPPROTO_UDP, ai.protocol)
end
assert('Addrinfo.unix') do
skip "unix is not supported on Windows" if SocketTest.win?
a1 = Addrinfo.unix('/tmp/sock')
assert_true(a1.unix?)
assert_equal('/tmp/sock', a1.unix_path)
assert_equal(Socket::SOCK_STREAM, a1.socktype)
a2 = Addrinfo.unix('/tmp/sock', Socket::SOCK_DGRAM)
assert_equal(Socket::SOCK_DGRAM, a2.socktype)
end
assert('Addrinfo#afamily') do
skip "afamily is not supported on Windows" if SocketTest.win?
ai4 = Addrinfo.new(Socket.sockaddr_in(1, '127.0.0.1'))
ai6 = Addrinfo.new(Socket.sockaddr_in(1, '::1'))
aiu = Addrinfo.new(Socket.sockaddr_un('/tmp/sock'))
assert_equal(Socket::AF_INET, ai4.afamily)
assert_equal(Socket::AF_INET6, ai6.afamily)
assert_equal(Socket::AF_UNIX, aiu.afamily)
end
# assert('Addrinfo#canonname') do
# #getnameinfo
# assert('Addrinfo#inspect') do
# assert('Addrinfo#inspect_socket') do
# assert('Addrinfo#ip?') do
# assert('Addrinfo#ip_address') do
# assert('Addrinfo#ip_port') do
# assert('Addrinfo#ip_unpack') do
# assert('Addrinfo#ipv4?') do
# assert('Addrinfo#ipv6?') do
# assert('Addrinfo#pfamily') do
# assert('Addrinfo#protocol') do
# assert('Addrinfo#socktype') do
# assert('Addrinfo#to_sockaddr') do
# assert('Addrinfo#unix?') do
# #unix_path
@@ -0,0 +1,17 @@
assert('BasicSocket') do
assert_equal(Class, BasicSocket.class)
end
assert('super class of BasicSocket') do
assert_equal(IO, BasicSocket.superclass)
end
assert('BasicSocket.do_not_reverse_lookup') do
assert_equal(BasicSocket.do_not_reverse_lookup, true)
end
assert('BasicSocket.do_not_reverse_lookup=') do
BasicSocket.do_not_reverse_lookup = false
assert_equal(BasicSocket.do_not_reverse_lookup, false)
BasicSocket.do_not_reverse_lookup = true
end
@@ -0,0 +1,44 @@
unless SocketTest.win?
# Note: most of tests below will fail if UDPSocket is broken.
assert('IPSocket.getaddress') do
l = IPSocket.getaddress("localhost")
assert_true (l == "127.0.0.1" or l == "::1")
end
assert('IPSocket.addr') do
localhost = "127.0.0.1"
s = UDPSocket.new
s.bind(localhost, 0)
port = Addrinfo.new(s.getsockname).ip_port
a = s.addr
assert_equal "AF_INET", a[0]
assert_equal port, a[1]
assert_equal localhost, a[2]
assert_equal localhost, a[3]
s.close
true
end
assert('IPSocket.peeraddr') do
localhost = "127.0.0.1"
server = UDPSocket.new
server.bind(localhost, 0)
port = server.local_address.ip_port
client = UDPSocket.new
client.connect(localhost, port)
a = client.peeraddr
assert_equal "AF_INET", a[0]
assert_equal port, a[1]
assert_equal localhost, a[2]
assert_equal localhost, a[3]
client.close
server.close
true
end
end # win?
@@ -0,0 +1,38 @@
unless SocketTest.win?
assert('Socket.gethostname') do
assert_true(Socket.gethostname.is_a? String)
end
assert('Socket::getaddrinfo') do
ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM)
assert_true ret.size >= 1
a = ret[0]
assert_equal "AF_INET", a[0]
assert_equal 53, a[1]
# documents says it's a hostname but CRuby returns an address
#assert_equal "127.0.0.1", a[2]
assert_equal "127.0.0.1", a[3]
assert_equal Socket::AF_INET, a[4]
assert_equal Socket::SOCK_DGRAM, a[5]
assert_equal Socket::IPPROTO_UDP, a[6] unless SocketTest.cygwin?
end
assert('Socket#recvfrom') do
begin
sstr = "abcdefg"
s = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
c = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
c.send sstr, 0, s.getsockname
rstr, ai = s.recvfrom sstr.size
assert_equal sstr, rstr
assert_equal "127.0.0.1", ai.ip_address
ensure
s.close rescue nil
c.close rescue nil
end
end
end # win?
@@ -0,0 +1,84 @@
#include <stdlib.h>
#include "mruby.h"
#include "mruby/error.h"
#if defined(_WIN32) || defined(_WIN64)
#include <io.h>
#if defined(_MSC_VER) || \
(defined(MRB_MINGW32_VERSION) && MRB_MINGW32_VERSION < 3021) || \
(defined(MRB_MINGW64_VERSION) && MRB_MINGW64_VERSION < 4000)
#include <fcntl.h>
#include <sys/stat.h>
#define open _open
#define close _close
#define unlink _unlink
static int
mkstemp(char *p)
{
int fd;
char* fname = _mktemp(p);
if (fname == NULL)
return -1;
fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
if (fd >= 0)
return fd;
return -1;
}
#endif
#else
#include <unistd.h>
#endif
mrb_value
mrb_sockettest_tmppath(mrb_state *mrb, mrb_value klass)
{
char name[] = "mruby-socket.XXXXXXXX";
int fd = mkstemp(name);
if (fd == -1) {
mrb_sys_fail(mrb, 0);
}
if (close(fd) == -1) {
mrb_sys_fail(mrb, 0);
}
if (unlink(name) == -1) {
mrb_sys_fail(mrb, 0);
}
return mrb_str_new_cstr(mrb, name);
}
mrb_value
mrb_sockettest_win_p(mrb_state *mrb, mrb_value klass)
{
#ifdef _WIN32
return mrb_true_value();
#else
return mrb_false_value();
#endif
}
mrb_value
mrb_sockettest_cygwin_p(mrb_state *mrb, mrb_value klass)
{
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
return mrb_true_value();
#else
return mrb_false_value();
#endif
}
void
mrb_mruby_socket_gem_test(mrb_state* mrb)
{
struct RClass *c = mrb_define_module(mrb, "SocketTest");
mrb_define_class_method(mrb, c, "tmppath", mrb_sockettest_tmppath, MRB_ARGS_NONE());
mrb_define_class_method(mrb, c, "win?", mrb_sockettest_win_p, MRB_ARGS_NONE());
mrb_define_class_method(mrb, c, "cygwin?", mrb_sockettest_cygwin_p, MRB_ARGS_NONE());
}
@@ -0,0 +1,4 @@
#assert('TCPSocket.gethostbyname') do
#assert('TCPSocket.new') do
#assert('TCPSocket#close') do
#assert('TCPSocket#write') do
@@ -0,0 +1,16 @@
assert('UDPSocket.new') do
s = UDPSocket.new
assert_true(s.is_a? UDPSocket)
s.close
s = UDPSocket.new(Socket::AF_INET6)
assert_true(s.is_a? UDPSocket)
s.close
true
end
#assert('UDPSocket#connect') do
#assert('UDPSocket#send') do
#assert('UDPSocket#recv') do
#assert('UDPSocket#bind') do
#assert('UDPSocket#recvfrom_nonblock') do
@@ -0,0 +1,130 @@
unless SocketTest.win? || SocketTest.cygwin?
def unixserver_test_block
path = SocketTest.tmppath
File.unlink path rescue nil
begin
result = yield path
ensure
File.unlink path rescue nil
end
result
end
def with_unix_server
unixserver_test_block do |path|
UNIXServer.open(path) { |server|
yield path, server
}
end
end
def with_unix_client
with_unix_server do |path, server|
UNIXSocket.open(path) do |csock|
ssock = server.accept
begin
yield path, server, ssock, csock
ensure
ssock.close unless ssock.closed? rescue nil
end
end
end
end
assert('UNIXServer.new') do
unixserver_test_block do |path|
server = UNIXServer.new(path)
assert_true server.is_a? UNIXServer
server.close
File.unlink path
s2 = nil
result = UNIXServer.open(path) { |s1|
assert_true s1.is_a? UNIXServer
s2 = s1
1234
}
assert_equal 1234, result
assert_true s2.is_a? UNIXServer
assert_true s2.closed?
end
end
# assert('UNIXServer#accept_nonblock') - would block if fails
assert('UNIXServer#addr') do
with_unix_server do |path, server|
assert_equal [ "AF_UNIX", path], server.addr
end
end
assert('UNIXServer#path') do
with_unix_server do |path, server|
assert_equal path, server.path
end
end
# assert('UNIXServer#peeraddr') - will raise a runtime exception
assert('UNIXServer#listen') do
with_unix_server do |path, server|
assert_equal 0, server.listen(1)
end
end
assert('UNIXServer#sysaccept') do
with_unix_server do |path, server|
UNIXSocket.open(path) do |csock|
begin
fd = server.sysaccept
assert_true fd.kind_of? Integer
ensure
IO._sysclose(fd) rescue nil
end
end
end
end
assert('UNIXSocket.new') do
with_unix_server do |path, server|
c = UNIXSocket.new(path)
assert_true c.is_a? UNIXSocket
c.close
true
end
end
assert('UNIXSocket#addr') do
with_unix_client do |path, server, ssock, csock|
assert_equal [ "AF_UNIX", path ], ssock.addr
assert_equal [ "AF_UNIX", "" ], csock.addr
end
end
assert('UNIXSocket#path') do
with_unix_client do |path, server, ssock, csock|
assert_equal path, ssock.path
assert_equal "", csock.path
end
end
assert('UNIXSocket#peeraddr') do
with_unix_client do |path, server, ssock, csock|
assert_equal [ "AF_UNIX", "" ], ssock.peeraddr
assert_equal [ "AF_UNIX", path ], csock.peeraddr
end
end
assert('UNIXSocket#recvfrom') do
with_unix_client do |path, server, ssock, csock|
str = "0123456789"
ssock.send str, 0
a = csock.recvfrom(8)
assert_equal str[0, 8], a[0]
assert_equal "AF_UNIX", a[1][0]
# a[1][1] would be "" or something
end
end
end # SocketTest.win?