rubysl-socket 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +0 -1
  3. data/.travis.yml +7 -0
  4. data/README.md +2 -2
  5. data/Rakefile +0 -1
  6. data/lib/rubysl/socket.rb +2 -0
  7. data/lib/rubysl/socket/socket.rb +1210 -0
  8. data/lib/{rubysl-socket → rubysl/socket}/version.rb +1 -1
  9. data/lib/socket.rb +1 -0
  10. data/rubysl-socket.gemspec +17 -18
  11. data/spec/addrinfo/afamily_spec.rb +5 -0
  12. data/spec/addrinfo/bind_spec.rb +5 -0
  13. data/spec/addrinfo/canonname_spec.rb +5 -0
  14. data/spec/addrinfo/connect_from_spec.rb +5 -0
  15. data/spec/addrinfo/connect_spec.rb +5 -0
  16. data/spec/addrinfo/connect_to_spec.rb +5 -0
  17. data/spec/addrinfo/family_addrinfo_spec.rb +5 -0
  18. data/spec/addrinfo/foreach_spec.rb +5 -0
  19. data/spec/addrinfo/getaddrinfo_spec.rb +5 -0
  20. data/spec/addrinfo/getnameinfo_spec.rb +5 -0
  21. data/spec/addrinfo/inspect_sockaddr_spec.rb +5 -0
  22. data/spec/addrinfo/inspect_spec.rb +5 -0
  23. data/spec/addrinfo/ip_address_spec.rb +5 -0
  24. data/spec/addrinfo/ip_port_spec.rb +5 -0
  25. data/spec/addrinfo/ip_spec.rb +9 -0
  26. data/spec/addrinfo/ip_unpack_spec.rb +5 -0
  27. data/spec/addrinfo/ipv4_loopback_spec.rb +5 -0
  28. data/spec/addrinfo/ipv4_multicast_spec.rb +5 -0
  29. data/spec/addrinfo/ipv4_private_spec.rb +5 -0
  30. data/spec/addrinfo/ipv4_spec.rb +5 -0
  31. data/spec/addrinfo/ipv6_linklocal_spec.rb +5 -0
  32. data/spec/addrinfo/ipv6_loopback_spec.rb +5 -0
  33. data/spec/addrinfo/ipv6_mc_global_spec.rb +5 -0
  34. data/spec/addrinfo/ipv6_mc_linklocal_spec.rb +5 -0
  35. data/spec/addrinfo/ipv6_mc_nodelocal_spec.rb +5 -0
  36. data/spec/addrinfo/ipv6_mc_orglocal_spec.rb +5 -0
  37. data/spec/addrinfo/ipv6_mc_sitelocal_spec.rb +5 -0
  38. data/spec/addrinfo/ipv6_multicast_spec.rb +5 -0
  39. data/spec/addrinfo/ipv6_sitelocal_spec.rb +5 -0
  40. data/spec/addrinfo/ipv6_spec.rb +5 -0
  41. data/spec/addrinfo/ipv6_to_ipv4_spec.rb +5 -0
  42. data/spec/addrinfo/ipv6_unspecified_spec.rb +5 -0
  43. data/spec/addrinfo/ipv6_v4compat_spec.rb +5 -0
  44. data/spec/addrinfo/ipv6_v4mapped_spec.rb +5 -0
  45. data/spec/addrinfo/listen_spec.rb +5 -0
  46. data/spec/addrinfo/marshal_dump_spec.rb +5 -0
  47. data/spec/addrinfo/marshal_load_spec.rb +5 -0
  48. data/spec/addrinfo/pfamily_spec.rb +5 -0
  49. data/spec/addrinfo/protocol_spec.rb +5 -0
  50. data/spec/addrinfo/socktype_spec.rb +5 -0
  51. data/spec/addrinfo/tcp_spec.rb +5 -0
  52. data/spec/addrinfo/to_s_spec.rb +5 -0
  53. data/spec/addrinfo/to_sockaddr_spec.rb +5 -0
  54. data/spec/addrinfo/udp_spec.rb +5 -0
  55. data/spec/addrinfo/unix_path_spec.rb +5 -0
  56. data/spec/addrinfo/unix_spec.rb +9 -0
  57. data/spec/basicsocket/close_read_spec.rb +42 -0
  58. data/spec/basicsocket/close_write_spec.rb +42 -0
  59. data/spec/basicsocket/do_not_reverse_lookup_spec.rb +78 -0
  60. data/spec/basicsocket/for_fd_spec.rb +37 -0
  61. data/spec/basicsocket/getpeername_spec.rb +24 -0
  62. data/spec/basicsocket/getsockname_spec.rb +27 -0
  63. data/spec/basicsocket/getsockopt_spec.rb +54 -0
  64. data/spec/basicsocket/ioctl_spec.rb +22 -0
  65. data/spec/basicsocket/recv_nonblock_spec.rb +6 -0
  66. data/spec/basicsocket/recv_spec.rb +76 -0
  67. data/spec/basicsocket/send_spec.rb +81 -0
  68. data/spec/basicsocket/setsockopt_spec.rb +333 -0
  69. data/spec/basicsocket/shutdown_spec.rb +5 -0
  70. data/spec/constants/constants_spec.rb +63 -0
  71. data/spec/fixtures/classes.rb +174 -0
  72. data/spec/fixtures/send_io.txt +1 -0
  73. data/spec/ipsocket/addr_spec.rb +72 -0
  74. data/spec/ipsocket/getaddress_spec.rb +26 -0
  75. data/spec/ipsocket/peeraddr_spec.rb +79 -0
  76. data/spec/ipsocket/recvfrom_spec.rb +64 -0
  77. data/spec/option/int_spec.rb +27 -0
  78. data/spec/option/linger_spec.rb +52 -0
  79. data/spec/option/new_spec.rb +32 -0
  80. data/spec/shared/pack_sockaddr.rb +26 -0
  81. data/spec/shared/partially_closable_sockets.rb +13 -0
  82. data/spec/shared/recv_nonblock.rb +33 -0
  83. data/spec/shared/socketpair.rb +35 -0
  84. data/spec/socket/accept_nonblock_spec.rb +27 -0
  85. data/spec/socket/accept_spec.rb +1 -0
  86. data/spec/socket/bind_spec.rb +80 -0
  87. data/spec/socket/connect_nonblock_spec.rb +62 -0
  88. data/spec/socket/connect_spec.rb +1 -0
  89. data/spec/socket/for_fd_spec.rb +29 -0
  90. data/spec/socket/getaddrinfo_spec.rb +120 -0
  91. data/spec/socket/gethostbyaddr_spec.rb +1 -0
  92. data/spec/socket/gethostbyname_spec.rb +26 -0
  93. data/spec/socket/gethostname_spec.rb +9 -0
  94. data/spec/socket/getnameinfo_spec.rb +57 -0
  95. data/spec/socket/getservbyname_spec.rb +24 -0
  96. data/spec/socket/listen_spec.rb +21 -0
  97. data/spec/socket/new_spec.rb +109 -0
  98. data/spec/socket/pack_sockaddr_in_spec.rb +6 -0
  99. data/spec/socket/pack_sockaddr_un_spec.rb +6 -0
  100. data/spec/socket/pair_spec.rb +6 -0
  101. data/spec/socket/recvfrom_nonblock_spec.rb +1 -0
  102. data/spec/socket/recvfrom_spec.rb +1 -0
  103. data/spec/socket/sockaddr_in_spec.rb +6 -0
  104. data/spec/socket/sockaddr_un_spec.rb +6 -0
  105. data/spec/socket/socket_spec.rb +37 -0
  106. data/spec/socket/socketpair_spec.rb +6 -0
  107. data/spec/socket/sysaccept_spec.rb +1 -0
  108. data/spec/socket/unpack_sockaddr_in_spec.rb +16 -0
  109. data/spec/socket/unpack_sockaddr_un_spec.rb +2 -0
  110. data/spec/tcpserver/accept_nonblock_spec.rb +30 -0
  111. data/spec/tcpserver/accept_spec.rb +60 -0
  112. data/spec/tcpserver/gets_spec.rb +17 -0
  113. data/spec/tcpserver/listen_spec.rb +1 -0
  114. data/spec/tcpserver/new_spec.rb +88 -0
  115. data/spec/tcpserver/output_spec.rb +8 -0
  116. data/spec/tcpserver/readpartial_spec.rb +8 -0
  117. data/spec/tcpserver/sysaccept_spec.rb +1 -0
  118. data/spec/tcpsocket/gethostbyname_spec.rb +62 -0
  119. data/spec/tcpsocket/new_spec.rb +5 -0
  120. data/spec/tcpsocket/open_spec.rb +5 -0
  121. data/spec/tcpsocket/partially_closable_spec.rb +20 -0
  122. data/spec/tcpsocket/recv_nonblock_spec.rb +31 -0
  123. data/spec/tcpsocket/setsockopt_spec.rb +49 -0
  124. data/spec/tcpsocket/shared/new.rb +85 -0
  125. data/spec/udpsocket/bind_spec.rb +33 -0
  126. data/spec/udpsocket/connect_spec.rb +1 -0
  127. data/spec/udpsocket/new_spec.rb +1 -0
  128. data/spec/udpsocket/open_spec.rb +12 -0
  129. data/spec/udpsocket/recvfrom_nonblock_spec.rb +1 -0
  130. data/spec/udpsocket/send_spec.rb +57 -0
  131. data/spec/unixserver/accept_nonblock_spec.rb +33 -0
  132. data/spec/unixserver/accept_spec.rb +64 -0
  133. data/spec/unixserver/for_fd_spec.rb +32 -0
  134. data/spec/unixserver/new_spec.rb +5 -0
  135. data/spec/unixserver/open_spec.rb +25 -0
  136. data/spec/unixserver/shared/new.rb +23 -0
  137. data/spec/unixsocket/addr_spec.rb +37 -0
  138. data/spec/unixsocket/new_spec.rb +5 -0
  139. data/spec/unixsocket/open_spec.rb +26 -0
  140. data/spec/unixsocket/pair_spec.rb +38 -0
  141. data/spec/unixsocket/partially_closable_spec.rb +25 -0
  142. data/spec/unixsocket/path_spec.rb +29 -0
  143. data/spec/unixsocket/peeraddr_spec.rb +29 -0
  144. data/spec/unixsocket/recv_io_spec.rb +40 -0
  145. data/spec/unixsocket/recvfrom_spec.rb +48 -0
  146. data/spec/unixsocket/send_io_spec.rb +30 -0
  147. data/spec/unixsocket/shared/new.rb +25 -0
  148. metadata +328 -89
  149. data/lib/rubysl-socket.rb +0 -7
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5e21fdfc61f5707b0b303649a7305011531cae8
4
+ data.tar.gz: 05ce0441f53ad6b4346fe4e1517660dc3b108888
5
+ SHA512:
6
+ metadata.gz: f900e95109a6b7714a4c8c65c98b6bf213901166d6d63849767c6cadb61d86cbab41f77bdda795525ffed204707d0798d167c4d5b7ad6c7cf330d70db610c7f3
7
+ data.tar.gz: 7f6a2ec38d91765320511279cc3c3a7f2243f6ae958469ae19d1c2a2da6d8e7ebea9a74533206dce661cac9e5e8d4d0dc27357e782b1c61320f264ef6605c0a4
data/.gitignore CHANGED
@@ -15,4 +15,3 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- .rbx
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.8.7
7
+ - rbx-nightly-18mode
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RubySL::Socket
1
+ # Rubysl::Socket
2
2
 
3
3
  TODO: Write a gem description
4
4
 
@@ -24,6 +24,6 @@ TODO: Write usage instructions here
24
24
 
25
25
  1. Fork it
26
26
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Added some feature'`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
28
  4. Push to the branch (`git push origin my-new-feature`)
29
29
  5. Create new Pull Request
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ require "rubysl/socket/socket"
2
+ require "rubysl/socket/version"
@@ -0,0 +1,1210 @@
1
+ require 'fcntl'
2
+
3
+ class SocketError < StandardError
4
+ end
5
+
6
+ # @todo Socket#accept[_nonblock]
7
+ # @todo UNIXServer#accept[_nonblock]
8
+ # @todo UDPSocket#recvfrom
9
+
10
+ class BasicSocket < IO
11
+ FFI = Rubinius::FFI
12
+
13
+ class << self
14
+ def from_descriptor(fixnum)
15
+ sock = allocate()
16
+ sock.from_descriptor(fixnum)
17
+ return sock
18
+ end
19
+
20
+ alias :for_fd :from_descriptor
21
+ end
22
+
23
+ def from_descriptor(fixnum)
24
+ IO.setup self, fixnum, nil, true
25
+ return self
26
+ end
27
+
28
+ def self.do_not_reverse_lookup=(setting)
29
+ @no_reverse_lookup = setting
30
+ end
31
+
32
+ def self.do_not_reverse_lookup
33
+ @no_reverse_lookup ? true : false
34
+ end
35
+
36
+ def getsockopt(level, optname)
37
+ Socket::Foreign.getsockopt descriptor, level, optname
38
+ end
39
+
40
+ def setsockopt(level, optname, optval)
41
+ optval = 1 if optval == true
42
+ optval = 0 if optval == false
43
+
44
+ error = 0
45
+
46
+ case optval
47
+ when Fixnum then
48
+ FFI::MemoryPointer.new :socklen_t do |val|
49
+ val.write_int optval
50
+ error = Socket::Foreign.setsockopt(descriptor, level,
51
+ optname, val,
52
+ val.total)
53
+ end
54
+ when String then
55
+ FFI::MemoryPointer.new optval.size do |val|
56
+ val.write_string optval
57
+ error = Socket::Foreign.setsockopt(descriptor, level,
58
+ optname, val,
59
+ optval.size)
60
+ end
61
+ else
62
+ raise TypeError, "socket option should be a String, a Fixnum, true, or false"
63
+ end
64
+
65
+ Errno.handle "Unable to set socket option" unless error == 0
66
+
67
+ return 0
68
+ end
69
+
70
+ def getsockname()
71
+ return Socket::Foreign.getsockname(descriptor)
72
+ end
73
+
74
+ #
75
+ # Obtain peername information for this socket.
76
+ #
77
+ # @see Socket.getpeername
78
+ #
79
+ def getpeername()
80
+ Socket::Foreign.getpeername @descriptor
81
+ end
82
+
83
+ #
84
+ #
85
+ #
86
+ def send(message, flags, to = nil)
87
+ connect to if to
88
+
89
+ bytes = message.length
90
+ bytes_sent = 0
91
+
92
+ FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
93
+ buffer.write_string message
94
+ bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
95
+ Errno.handle 'send(2)' if bytes_sent < 0
96
+ end
97
+
98
+ bytes_sent
99
+ end
100
+
101
+ def recvfrom(bytes_to_read, flags = 0)
102
+ # FIXME 0 is knowledge from io.cpp
103
+ return socket_recv(bytes_to_read, flags, 0)
104
+ end
105
+
106
+ def recv(bytes_to_read, flags = 0)
107
+ # FIXME 0 is knowledge from io.cpp
108
+ return socket_recv(bytes_to_read, flags, 0)
109
+ end
110
+
111
+ def close_read
112
+ ensure_open
113
+
114
+ # If we were only in readonly mode, close it all together
115
+ if @mode & ACCMODE == RDONLY
116
+ return close
117
+ end
118
+
119
+ # MRI doesn't check if shutdown worked, so we don't.
120
+ Socket::Foreign.shutdown @descriptor, 0
121
+
122
+ @mode = WRONLY
123
+
124
+ nil
125
+ end
126
+
127
+ def close_write
128
+ ensure_open
129
+
130
+ # If we were only in writeonly mode, close it all together
131
+ if @mode & ACCMODE == WRONLY
132
+ return close
133
+ end
134
+
135
+ Socket::Foreign.shutdown @descriptor, 1
136
+
137
+ # Mark it as read only
138
+ @mode = RDONLY
139
+
140
+ nil
141
+ end
142
+
143
+ #
144
+ # Sets socket nonblocking and reads up to given number of bytes.
145
+ #
146
+ # @todo Should EWOULDBLOCK be passed unchanged? --rue
147
+ #
148
+ def recv_nonblock(bytes_to_read, flags = 0)
149
+ fcntl Fcntl::F_SETFL, Fcntl::O_NONBLOCK
150
+ socket_recv bytes_to_read, flags, 0
151
+ rescue Errno::EWOULDBLOCK
152
+ raise Errno::EAGAIN
153
+ end
154
+
155
+ def shutdown(how = 2)
156
+ err = Socket::Foreign.shutdown @descriptor, how
157
+ Errno.handle "shutdown" unless err == 0
158
+ end
159
+
160
+ end
161
+
162
+ class Socket < BasicSocket
163
+ FFI = Rubinius::FFI
164
+
165
+ # @todo Is omitting empty-value constants reasonable? --rue
166
+ module Constants
167
+ all_valid = FFI.config_hash("socket").reject {|name, value| value.empty? }
168
+
169
+ all_valid.each {|name, value| const_set name, Integer(value) }
170
+
171
+ # MRI compat. socket is a pretty screwed up API. All the constants in Constants
172
+ # must also be directly accessible on Socket itself. This means it's not enough
173
+ # to include Constants into Socket, because Socket#const_defined? must be able
174
+ # to see constants like AF_INET6 directly on Socket, but #const_defined? doesn't
175
+ # check inherited constants. O_o
176
+ #
177
+ all_valid.each {|name, value| Socket.const_set name, Integer(value) }
178
+
179
+
180
+ afamilies = all_valid.to_a.select { |name,| name =~ /^AF_/ }
181
+ afamilies.map! {|name, value| [value.to_i, name] }
182
+
183
+ pfamilies = all_valid.to_a.select { |name,| name =~ /^PF_/ }
184
+ pfamilies.map! {|name, value| [value.to_i, name] }
185
+
186
+ AF_TO_FAMILY = Hash[*afamilies.flatten]
187
+ PF_TO_FAMILY = Hash[*pfamilies.flatten]
188
+ end
189
+
190
+ module Foreign
191
+ extend FFI::Library
192
+
193
+ class Addrinfo < FFI::Struct
194
+ config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
195
+ :ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
196
+ end
197
+
198
+ attach_function :_bind, "bind", [:int, :pointer, :socklen_t], :int
199
+ attach_function :_connect, "connect", [:int, :pointer, :socklen_t], :int
200
+
201
+ attach_function :accept, [:int, :pointer, :pointer], :int
202
+ attach_function :close, [:int], :int
203
+ attach_function :shutdown, [:int, :int], :int
204
+ attach_function :listen, [:int, :int], :int
205
+ attach_function :socket, [:int, :int, :int], :int
206
+ attach_function :send, [:int, :pointer, :size_t, :int], :int
207
+ attach_function :recv, [:int, :pointer, :size_t, :int], :int
208
+ attach_function :recvfrom, [:int, :pointer, :size_t, :int,
209
+ :pointer, :pointer], :int
210
+
211
+ attach_function :_getsockopt,
212
+ "getsockopt", [:int, :int, :int, :pointer, :pointer], :int
213
+ attach_function :_getaddrinfo,
214
+ "getaddrinfo", [:string, :string, :pointer, :pointer], :int
215
+
216
+ attach_function :gai_strerror, [:int], :string
217
+ attach_function :setsockopt, [:int, :int, :int, :pointer, :socklen_t], :int
218
+ attach_function :freeaddrinfo, [:pointer], :void
219
+ attach_function :_getpeername, "getpeername", [:int, :pointer, :pointer], :int
220
+ attach_function :_getsockname, "getsockname", [:int, :pointer, :pointer], :int
221
+
222
+ attach_function :socketpair, [:int, :int, :int, :pointer], :int
223
+
224
+ attach_function :gethostname, [:pointer, :size_t], :int
225
+ attach_function :getservbyname, [:pointer, :pointer], :pointer
226
+
227
+ attach_function :htons, [:uint16_t], :uint16_t
228
+ attach_function :ntohs, [:uint16_t], :uint16_t
229
+
230
+ attach_function :_getnameinfo,
231
+ "getnameinfo", [:pointer, :socklen_t, :pointer, :socklen_t,
232
+ :pointer, :socklen_t, :int], :int
233
+
234
+ def self.bind(descriptor, sockaddr)
235
+ FFI::MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
236
+ sockaddr_p.write_string sockaddr, sockaddr.length
237
+
238
+ _bind descriptor, sockaddr_p, sockaddr.length
239
+ end
240
+ end
241
+
242
+ def self.connect(descriptor, sockaddr)
243
+ err = 0
244
+ FFI::MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
245
+ sockaddr_p.write_string sockaddr, sockaddr.length
246
+
247
+ err = _connect descriptor, sockaddr_p, sockaddr.length
248
+ end
249
+
250
+ err
251
+ end
252
+
253
+ def self.getsockopt(descriptor, level, optname)
254
+ FFI::MemoryPointer.new 256 do |val| # HACK magic number
255
+ FFI::MemoryPointer.new :socklen_t do |length|
256
+ length.write_int 256 # HACK magic number
257
+
258
+ err = _getsockopt descriptor, level, optname, val, length
259
+
260
+ Errno.handle "Unable to get socket option" unless err == 0
261
+
262
+ return val.read_string(length.read_int)
263
+ end
264
+ end
265
+ end
266
+
267
+ def self.getaddrinfo(host, service = nil, family = nil, socktype = nil, protocol = nil, flags = nil)
268
+ hints = Addrinfo.new
269
+ hints[:ai_family] = family || 0
270
+ hints[:ai_socktype] = socktype || 0
271
+ hints[:ai_protocol] = protocol || 0
272
+ hints[:ai_flags] = flags || 0
273
+
274
+ if host && (host.empty? || host == '<any>')
275
+ host = "0.0.0.0"
276
+ elsif host == '<broadcast>'
277
+ host = '255.255.255.255'
278
+ end
279
+
280
+ res_p = FFI::MemoryPointer.new :pointer
281
+
282
+ err = _getaddrinfo host, service, hints.pointer, res_p
283
+
284
+ raise SocketError, gai_strerror(err) unless err == 0
285
+
286
+ ptr = res_p.read_pointer
287
+
288
+ return [] unless ptr
289
+
290
+ res = Addrinfo.new ptr
291
+
292
+ addrinfos = []
293
+
294
+ while true
295
+ addrinfo = []
296
+ addrinfo << res[:ai_flags]
297
+ addrinfo << res[:ai_family]
298
+ addrinfo << res[:ai_socktype]
299
+ addrinfo << res[:ai_protocol]
300
+ addrinfo << res[:ai_addr].read_string(res[:ai_addrlen])
301
+ addrinfo << res[:ai_canonname]
302
+
303
+ addrinfos << addrinfo
304
+
305
+ break unless res[:ai_next]
306
+
307
+ res = Addrinfo.new res[:ai_next]
308
+ end
309
+
310
+ return addrinfos
311
+ ensure
312
+ hints.free if hints
313
+
314
+ if res_p
315
+ ptr = res_p.read_pointer
316
+
317
+ # Be sure to feed a legit pointer to freeaddrinfo
318
+ if ptr and !ptr.null?
319
+ freeaddrinfo ptr
320
+ end
321
+ res_p.free
322
+ end
323
+ end
324
+
325
+ def self.getaddress(host)
326
+ addrinfos = getaddrinfo(host)
327
+ unpack_sockaddr_in(addrinfos.first[4], false).first
328
+ end
329
+
330
+ def self.getnameinfo(sockaddr, flags = Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV,
331
+ reverse_lookup = !BasicSocket.do_not_reverse_lookup)
332
+ name_info = []
333
+ value = nil
334
+
335
+ FFI::MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
336
+ FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXHOST do |node|
337
+ FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXSERV do |service|
338
+ sockaddr_p.write_string sockaddr, sockaddr.length
339
+
340
+ if reverse_lookup then
341
+ err = _getnameinfo(sockaddr_p, sockaddr.length,
342
+ node, Socket::Constants::NI_MAXHOST, nil, 0, 0)
343
+
344
+ name_info[2] = node.read_string if err == 0
345
+ end
346
+
347
+ err = _getnameinfo(sockaddr_p, sockaddr.length,
348
+ node, Socket::Constants::NI_MAXHOST,
349
+ service, Socket::Constants::NI_MAXSERV,
350
+ flags)
351
+
352
+ unless err == 0 then
353
+ raise SocketError, gai_strerror(err)
354
+ end
355
+
356
+ sa_family = SockAddr_In.new(sockaddr)[:sin_family]
357
+
358
+ name_info[0] = Socket::Constants::AF_TO_FAMILY[sa_family]
359
+ name_info[1] = service.read_string
360
+ name_info[3] = node.read_string
361
+ end
362
+ end
363
+ end
364
+
365
+ name_info[2] = name_info[3] if name_info[2].nil?
366
+ name_info
367
+ end
368
+
369
+ def self.getpeername(descriptor)
370
+ FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
371
+ FFI::MemoryPointer.new :socklen_t do |len_p|
372
+ len_p.write_int 128
373
+
374
+ err = _getpeername descriptor, sockaddr_storage_p, len_p
375
+
376
+ Errno.handle 'getpeername(2)' unless err == 0
377
+
378
+ sockaddr_storage_p.read_string len_p.read_int
379
+ end
380
+ end
381
+ end
382
+
383
+ def self.getsockname(descriptor)
384
+ FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
385
+ FFI::MemoryPointer.new :socklen_t do |len_p|
386
+ len_p.write_int 128
387
+
388
+ err = _getsockname descriptor, sockaddr_storage_p, len_p
389
+
390
+ Errno.handle 'getsockname(2)' unless err == 0
391
+
392
+ sockaddr_storage_p.read_string len_p.read_int
393
+ end
394
+ end
395
+ end
396
+
397
+ def self.pack_sockaddr_in(host, port, family, type, flags)
398
+ hints = Addrinfo.new
399
+ hints[:ai_family] = family
400
+ hints[:ai_socktype] = type
401
+ hints[:ai_flags] = flags
402
+
403
+ if host && host.empty?
404
+ host = "0.0.0.0"
405
+ end
406
+
407
+ res_p = FFI::MemoryPointer.new :pointer
408
+
409
+ err = _getaddrinfo host, port.to_s, hints.pointer, res_p
410
+
411
+ raise SocketError, gai_strerror(err) unless err == 0
412
+
413
+ return [] if res_p.read_pointer.null?
414
+
415
+ res = Addrinfo.new res_p.read_pointer
416
+
417
+ return res[:ai_addr].read_string(res[:ai_addrlen])
418
+
419
+ ensure
420
+ hints.free if hints
421
+
422
+ if res_p then
423
+ ptr = res_p.read_pointer
424
+
425
+ freeaddrinfo ptr if ptr and not ptr.null?
426
+
427
+ res_p.free
428
+ end
429
+ end
430
+
431
+ def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
432
+ family, port, host, ip = getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
433
+ # On some systems this doesn't fail for families other than AF_INET(6)
434
+ # so we raise manually here.
435
+ raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' unless family =~ /AF_INET/
436
+ return host, ip, port.to_i
437
+ end
438
+ end
439
+
440
+ module ListenAndAccept
441
+ include IO::Socketable
442
+
443
+ def listen(backlog)
444
+ backlog = Rubinius::Type.coerce_to backlog, Fixnum, :to_int
445
+
446
+ err = Socket::Foreign.listen descriptor, backlog
447
+
448
+ Errno.handle 'listen(2)' unless err == 0
449
+
450
+ err
451
+ end
452
+
453
+ def accept
454
+ return if closed?
455
+
456
+ fd = super
457
+
458
+ socket = self.class.superclass.allocate
459
+ IO.setup socket, fd, nil, true
460
+ socket
461
+ end
462
+
463
+ #
464
+ # Set nonblocking and accept.
465
+ #
466
+ def accept_nonblock
467
+ return if closed?
468
+
469
+ fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
470
+
471
+ fd = Socket::Foreign.accept descriptor, nil, nil
472
+
473
+ Errno.handle 'accept(2)' if fd < 0
474
+
475
+ # TCPServer -> TCPSocket etc. *sigh*
476
+ socket = self.class.superclass.allocate
477
+ IO.setup socket, fd, nil, true
478
+ socket
479
+ end
480
+
481
+ def accept_nonblock2
482
+ return if closed?
483
+
484
+ fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
485
+
486
+ fd = Socket::Foreign.accept descriptor, nil, nil
487
+ if fd < 0
488
+ return nil if FFI::Platform::POSIX.errno == Errno::EAGAIN::Errno
489
+ Errno.handle 'accept(2)'
490
+ end
491
+
492
+ # TCPServer -> TCPSocket etc. *sigh*
493
+ socket = self.class.superclass.allocate
494
+ IO.setup socket, fd, nil, true
495
+ socket
496
+ end
497
+ end
498
+
499
+ include Socket::ListenAndAccept
500
+
501
+ class SockAddr_In < FFI::Struct
502
+ config("rbx.platform.sockaddr_in", :sin_family, :sin_port, :sin_addr, :sin_zero)
503
+
504
+ def initialize(sockaddrin)
505
+ @p = FFI::MemoryPointer.new sockaddrin.size
506
+ @p.write_string(sockaddrin)
507
+ super(@p)
508
+ end
509
+
510
+ def to_s
511
+ @p.read_string(@p.total)
512
+ end
513
+
514
+ end
515
+
516
+ # If we have the details to support unix sockets, do so.
517
+ if FFI.config("sockaddr_un.sun_family.offset") and Socket::Constants.const_defined?(:AF_UNIX)
518
+ class SockAddr_Un < FFI::Struct
519
+ config("rbx.platform.sockaddr_un", :sun_family, :sun_path)
520
+
521
+ def initialize(filename = nil)
522
+ maxfnsize = self.size - (FFI.config("sockaddr_un.sun_family.size") + 1)
523
+
524
+ if filename and filename.length > maxfnsize
525
+ raise ArgumentError, "too long unix socket path (max: #{maxfnsize}bytes)"
526
+ end
527
+ @p = FFI::MemoryPointer.new self.size
528
+ if filename
529
+ @p.write_string( [Socket::AF_UNIX].pack("s") + filename )
530
+ end
531
+ super @p
532
+ end
533
+
534
+ def to_s
535
+ @p.read_string self.size
536
+ end
537
+ end
538
+ end
539
+
540
+ def self.getaddrinfo(host, service, family = 0, socktype = 0,
541
+ protocol = 0, flags = 0)
542
+ if service
543
+ if service.kind_of? Fixnum
544
+ service = service.to_s
545
+ else
546
+ service = StringValue(service)
547
+ end
548
+ end
549
+
550
+ addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
551
+ protocol, flags)
552
+
553
+ addrinfos.map do |ai|
554
+ addrinfo = []
555
+ addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]
556
+
557
+ sockaddr = Foreign.unpack_sockaddr_in ai[4], !BasicSocket.do_not_reverse_lookup
558
+
559
+ addrinfo << sockaddr.pop # port
560
+ addrinfo.concat sockaddr # hosts
561
+ addrinfo << ai[1]
562
+ addrinfo << ai[2]
563
+ addrinfo << ai[3]
564
+ addrinfo
565
+ end
566
+ end
567
+
568
+ def self.getnameinfo(sockaddr, flags = 0)
569
+ port = nil
570
+ host = nil
571
+ family = Socket::AF_UNSPEC
572
+ if sockaddr.is_a?(Array)
573
+ if sockaddr.size == 3
574
+ af = sockaddr[0]
575
+ port = sockaddr[1]
576
+ host = sockaddr[2]
577
+ elsif sockaddr.size == 4
578
+ af = sockaddr[0]
579
+ port = sockaddr[1]
580
+ host = sockaddr[3] || sockaddr[2]
581
+ else
582
+ raise ArgumentError, "array size should be 3 or 4, #{sockaddr.size} given"
583
+ end
584
+
585
+ if family == "AF_INET"
586
+ family = Socket::AF_INET
587
+ elsif family == "AF_INET6"
588
+ family = Socket::AF_INET6
589
+ end
590
+ sockaddr = Socket::Foreign.pack_sockaddr_in(host, port, family, Socket::SOCK_DGRAM, 0)
591
+ end
592
+
593
+ family, port, host, ip = Socket::Foreign.getnameinfo(sockaddr, flags)
594
+ [host, port]
595
+ end
596
+
597
+ def self.gethostname
598
+ FFI::MemoryPointer.new :char, 1024 do |mp| #magic number 1024 comes from MRI
599
+ Socket::Foreign.gethostname(mp, 1024) # same here
600
+ return mp.read_string
601
+ end
602
+ end
603
+
604
+ def self.gethostbyname(hostname)
605
+ addrinfos = Socket.getaddrinfo(hostname, nil)
606
+
607
+ hostname = addrinfos.first[2]
608
+ family = addrinfos.first[4]
609
+ addresses = []
610
+ alternatives = []
611
+ addrinfos.each do |a|
612
+ alternatives << a[2] unless a[2] == hostname
613
+ # transform addresses to packed strings
614
+ if a[4] == family
615
+ sockaddr = Socket.sockaddr_in(1, a[3])
616
+ if family == AF_INET
617
+ # IPv4 address
618
+ offset = FFI.config("sockaddr_in.sin_addr.offset")
619
+ size = FFI.config("sockaddr_in.sin_addr.size")
620
+ addresses << sockaddr.byteslice(offset, size)
621
+ elsif family == AF_INET6
622
+ # Ipv6 address
623
+ offset = FFI.config("sockaddr_in6.sin6_addr.offset")
624
+ size = FFI.config("sockaddr_in6.sin6_addr.size")
625
+ addresses << sockaddr.byteslice(offset, size)
626
+ else
627
+ addresses << a[3]
628
+ end
629
+ end
630
+ end
631
+
632
+ [hostname, alternatives.uniq, family] + addresses.uniq
633
+ end
634
+
635
+
636
+ class Servent < FFI::Struct
637
+ config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
638
+
639
+ def initialize(data)
640
+ @p = FFI::MemoryPointer.new data.size
641
+ @p.write_string(data)
642
+ super(@p)
643
+ end
644
+
645
+ def to_s
646
+ @p.read_string(size)
647
+ end
648
+
649
+ end
650
+
651
+ def self.getservbyname(service, proto='tcp')
652
+ FFI::MemoryPointer.new :char, service.length + 1 do |svc|
653
+ FFI::MemoryPointer.new :char, proto.length + 1 do |prot|
654
+ svc.write_string(service + "\0")
655
+ prot.write_string(proto + "\0")
656
+ fn = Socket::Foreign.getservbyname(svc, prot)
657
+
658
+ raise SocketError, "no such service #{service}/#{proto}" if fn.nil?
659
+
660
+ s = Servent.new(fn.read_string(Servent.size))
661
+ return Socket::Foreign.ntohs(s[:s_port])
662
+ end
663
+ end
664
+ end
665
+
666
+ def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
667
+ Socket::Foreign.pack_sockaddr_in host, port, Socket::AF_UNSPEC, type, flags
668
+ end
669
+
670
+ def self.unpack_sockaddr_in(sockaddr)
671
+ host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false
672
+
673
+ return [port, address]
674
+ rescue SocketError => e
675
+ if e.message =~ /ai_family not supported/ then # HACK platform specific?
676
+ raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
677
+ else
678
+ raise e
679
+ end
680
+ end
681
+
682
+ def self.socketpair(domain, type, protocol, klass=self)
683
+ if domain.kind_of? String
684
+ if domain.prefix? "AF_" or domain.prefix? "PF_"
685
+ begin
686
+ domain = Socket::Constants.const_get(domain)
687
+ rescue NameError
688
+ raise SocketError, "unknown socket domain #{domani}"
689
+ end
690
+ else
691
+ raise SocketError, "unknown socket domain #{domani}"
692
+ end
693
+ end
694
+
695
+ type = get_socket_type(type)
696
+
697
+ FFI::MemoryPointer.new :int, 2 do |mp|
698
+ Socket::Foreign.socketpair(domain, type, protocol, mp)
699
+ fd0, fd1 = mp.read_array_of_int(2)
700
+
701
+ [ klass.from_descriptor(fd0), klass.from_descriptor(fd1) ]
702
+ end
703
+ end
704
+
705
+ class << self
706
+ alias_method :sockaddr_in, :pack_sockaddr_in
707
+ alias_method :pair, :socketpair
708
+ end
709
+
710
+ # Only define these methods if we support unix sockets
711
+ if self.const_defined?(:SockAddr_Un)
712
+ def self.pack_sockaddr_un(file)
713
+ SockAddr_Un.new(file).to_s
714
+ end
715
+
716
+ def self.unpack_sockaddr_un(addr)
717
+
718
+ if addr.length > FFI.config("sockaddr_un.sizeof")
719
+ raise TypeError, "too long sockaddr_un - #{addr.length} longer than #{FFI.config("sockaddr_un.sizeof")}"
720
+ end
721
+
722
+ struct = SockAddr_Un.new
723
+ struct.pointer.write_string(addr)
724
+
725
+ struct[:sun_path]
726
+ end
727
+
728
+ class << self
729
+ alias_method :sockaddr_un, :pack_sockaddr_un
730
+ end
731
+ end
732
+
733
+ def initialize(family, socket_type, protocol)
734
+ family = self.class.get_protocol_family family
735
+ socket_type = self.class.get_socket_type socket_type
736
+ descriptor = Socket::Foreign.socket family, socket_type, protocol
737
+
738
+ Errno.handle 'socket(2)' if descriptor < 0
739
+
740
+ IO.setup self, descriptor, nil, true
741
+ end
742
+
743
+ def bind(server_sockaddr)
744
+ err = Socket::Foreign.bind(descriptor, server_sockaddr)
745
+ Errno.handle 'bind(2)' unless err == 0
746
+ err
747
+ end
748
+
749
+ # @todo Should this be closing the descriptor? --rue
750
+ def connect(sockaddr, extra=nil)
751
+ if extra
752
+ sockaddr = Socket.pack_sockaddr_in sockaddr, extra
753
+ else
754
+ sockaddr = StringValue(sockaddr)
755
+ end
756
+
757
+ status = Socket::Foreign.connect descriptor, sockaddr
758
+
759
+ if status < 0
760
+ begin
761
+ Errno.handle "connect(2)"
762
+ rescue Errno::EISCONN
763
+ return 0
764
+ end
765
+ end
766
+
767
+ return 0
768
+ end
769
+
770
+ def connect_nonblock(sockaddr)
771
+ fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
772
+
773
+ status = Socket::Foreign.connect descriptor, StringValue(sockaddr)
774
+ if status < 0
775
+ Errno.handle "connect(2)"
776
+ end
777
+
778
+ return status
779
+ end
780
+
781
+ def self.get_protocol_family(family)
782
+ case family
783
+ when Fixnum
784
+ return family
785
+ when String
786
+ # do nothing
787
+ else
788
+ family = StringValue(family)
789
+ end
790
+
791
+ return Socket::Constants.const_get family
792
+ end
793
+
794
+ def self.get_socket_type(type)
795
+ if type.kind_of? String
796
+ if type.prefix? "SOCK_"
797
+ begin
798
+ type = Socket::Constants.const_get(type)
799
+ rescue NameError
800
+ raise SocketError, "unknown socket type #{type}"
801
+ end
802
+ else
803
+ raise SocketError, "unknown socket type #{type}"
804
+ end
805
+ elsif !type.kind_of? Integer
806
+ raise Errno::EPROTONOSUPPORT, type.inspect
807
+ end
808
+
809
+ type
810
+ end
811
+ end
812
+
813
+ class UNIXSocket < BasicSocket
814
+ include IO::TransferIO
815
+
816
+ # Coding to the lowest standard here.
817
+ def recvfrom(bytes_read, flags = 0)
818
+ # FIXME 2 is hardcoded knowledge from io.cpp
819
+ socket_recv(bytes_read, flags, 2)
820
+ end
821
+
822
+ def initialize(path)
823
+ @path = path
824
+ unix_setup
825
+ @path = "" # Client
826
+ end
827
+
828
+ def path
829
+ unless @path
830
+ sockaddr = Socket::Foreign.getsockname descriptor
831
+ _, @path = sockaddr.unpack('SZ*')
832
+ end
833
+
834
+ return @path
835
+ end
836
+
837
+ def from_descriptor(fixnum)
838
+ super
839
+ @path = nil
840
+ end
841
+
842
+ def unix_setup(server = false)
843
+ status = nil
844
+ phase = 'socket(2)'
845
+ sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
846
+
847
+ Errno.handle phase if sock < 0
848
+
849
+ IO.setup self, sock, 'r+', true
850
+
851
+ sockaddr = Socket.pack_sockaddr_un(@path)
852
+
853
+ if server then
854
+ phase = 'bind(2)'
855
+ status = Socket::Foreign.bind descriptor, sockaddr
856
+ else
857
+ phase = 'connect(2)'
858
+ status = Socket::Foreign.connect descriptor, sockaddr
859
+ end
860
+
861
+ if status < 0 then
862
+ close
863
+ Errno.handle phase
864
+ end
865
+
866
+ if server then
867
+ phase = 'listen(2)'
868
+ status = Socket::Foreign.listen descriptor, 5
869
+ if status < 0
870
+ close
871
+ Errno.handle phase
872
+ end
873
+ end
874
+
875
+ return sock
876
+ end
877
+ private :unix_setup
878
+
879
+ def addr
880
+ sockaddr = Socket::Foreign.getsockname descriptor
881
+ _, sock_path = sockaddr.unpack('SZ*')
882
+ ["AF_UNIX", sock_path]
883
+ end
884
+
885
+ def peeraddr
886
+ sockaddr = Socket::Foreign.getpeername descriptor
887
+ _, sock_path = sockaddr.unpack('SZ*')
888
+ ["AF_UNIX", sock_path]
889
+ end
890
+
891
+ def recv_io(klass=IO, mode=nil)
892
+ begin
893
+ fd = recv_fd
894
+ rescue PrimitiveFailure
895
+ raise SocketError, "file descriptor was not passed"
896
+ end
897
+
898
+ return fd unless klass
899
+
900
+ if klass < BasicSocket
901
+ klass.for_fd(fd)
902
+ else
903
+ klass.for_fd(fd, mode)
904
+ end
905
+ end
906
+
907
+ class << self
908
+ def socketpair(type=Socket::SOCK_STREAM, protocol=0)
909
+ Socket.socketpair(Socket::PF_UNIX, type, protocol, self)
910
+ end
911
+
912
+ alias_method :pair, :socketpair
913
+ end
914
+
915
+ end
916
+
917
+ class UNIXServer < UNIXSocket
918
+
919
+ include Socket::ListenAndAccept
920
+
921
+ def initialize(path)
922
+ @path = path
923
+ unix_setup(true)
924
+ end
925
+ end
926
+
927
+ class IPSocket < BasicSocket
928
+
929
+ def self.getaddress(host)
930
+ Socket::Foreign.getaddress host
931
+ end
932
+
933
+ def addr
934
+ sockaddr = Socket::Foreign.getsockname descriptor
935
+
936
+ family, port, host, ip = Socket::Foreign.getnameinfo sockaddr
937
+ [family, port.to_i, host, ip]
938
+ end
939
+
940
+ def peeraddr
941
+ sockaddr = Socket::Foreign.getpeername descriptor
942
+
943
+ family, port, host, ip = Socket::Foreign.getnameinfo sockaddr
944
+ [family, port.to_i, host, ip]
945
+ end
946
+
947
+ def recvfrom(maxlen, flags = 0)
948
+ # FIXME 1 is hardcoded knowledge from io.cpp
949
+ flags = 0 if flags.nil?
950
+ socket_recv maxlen, flags, 1
951
+ end
952
+
953
+ def recvfrom_nonblock(maxlen, flags = 0)
954
+ # Set socket to non-blocking, if we can
955
+ # Todo: Ensure this works in Windows! If not, I claim that's Fcntl's fault.
956
+ fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
957
+ flags = 0 if flags.nil?
958
+ flags |= Socket::MSG_DONTWAIT
959
+
960
+ # Wait until we have something to read
961
+ # @todo Why? ^^ --rue
962
+ IO.select([self])
963
+ return recvfrom(maxlen, flags)
964
+ end
965
+ end
966
+
967
+ class UDPSocket < IPSocket
968
+ FFI = Rubinius::FFI
969
+
970
+ def initialize(socktype = Socket::AF_INET)
971
+ @socktype = socktype
972
+ status = Socket::Foreign.socket @socktype,
973
+ Socket::SOCK_DGRAM,
974
+ Socket::IPPROTO_UDP
975
+ Errno.handle 'socket(2)' if status < 0
976
+
977
+ IO.setup self, status, nil, true
978
+ end
979
+
980
+ def bind(host, port)
981
+ @host = host.to_s if host
982
+ @port = port.to_s if port
983
+
984
+ addrinfos = Socket::Foreign.getaddrinfo(@host,
985
+ @port,
986
+ @socktype,
987
+ Socket::SOCK_DGRAM, 0,
988
+ Socket::AI_PASSIVE)
989
+
990
+ status = -1
991
+
992
+ addrinfos.each do |addrinfo|
993
+ flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
994
+
995
+ status = Socket::Foreign.bind descriptor, sockaddr
996
+
997
+ break if status >= 0
998
+ end
999
+
1000
+ if status < 0
1001
+ Errno.handle 'bind(2)'
1002
+ end
1003
+
1004
+ status
1005
+ end
1006
+
1007
+ def connect(host, port)
1008
+ sockaddr = Socket::Foreign.pack_sockaddr_in host, port, @socktype, Socket::SOCK_DGRAM, 0
1009
+
1010
+ syscall = 'connect(2)'
1011
+ status = Socket::Foreign.connect descriptor, sockaddr
1012
+
1013
+ if status < 0
1014
+ Errno.handle syscall
1015
+ end
1016
+
1017
+ 0
1018
+ end
1019
+
1020
+ def send(message, flags, *to)
1021
+ connect *to unless to.empty?
1022
+
1023
+ bytes = message.length
1024
+ bytes_sent = 0
1025
+
1026
+ FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
1027
+ buffer.write_string message
1028
+ bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
1029
+ Errno.handle 'send(2)' if bytes_sent < 0
1030
+ end
1031
+
1032
+ bytes_sent
1033
+ end
1034
+
1035
+ def inspect
1036
+ "#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
1037
+ end
1038
+
1039
+ end
1040
+
1041
+ class TCPSocket < IPSocket
1042
+ FFI = Rubinius::FFI
1043
+
1044
+ def self.gethostbyname(hostname)
1045
+ addrinfos = Socket.getaddrinfo(hostname, nil)
1046
+
1047
+ hostname = addrinfos.first[2]
1048
+ family = addrinfos.first[4]
1049
+ addresses = []
1050
+ alternatives = []
1051
+ addrinfos.each do |a|
1052
+ alternatives << a[2] unless a[2] == hostname
1053
+ addresses << a[3] if a[4] == family
1054
+ end
1055
+
1056
+ [hostname, alternatives.uniq, family] + addresses.uniq
1057
+ end
1058
+
1059
+ #
1060
+ # @todo Is it correct to ignore the to? If not, does
1061
+ # the socket need to be reconnected? --rue
1062
+ #
1063
+ def send(bytes_to_read, flags, to = nil)
1064
+ super(bytes_to_read, flags)
1065
+ end
1066
+
1067
+
1068
+ def initialize(host, port, local_host=nil, local_service=nil)
1069
+ @host = host
1070
+ @port = port
1071
+
1072
+ tcp_setup @host, @port, local_host, local_service
1073
+ end
1074
+
1075
+ def tcp_setup(remote_host, remote_service, local_host = nil,
1076
+ local_service = nil, server = false)
1077
+ status = nil
1078
+ syscall = nil
1079
+ remote_host = StringValue(remote_host) if remote_host
1080
+ if remote_service
1081
+ if remote_service.kind_of? Fixnum
1082
+ remote_service = remote_service.to_s
1083
+ else
1084
+ remote_service = StringValue(remote_service)
1085
+ end
1086
+ end
1087
+
1088
+ flags = server ? Socket::AI_PASSIVE : 0
1089
+ @remote_addrinfo = Socket::Foreign.getaddrinfo(remote_host,
1090
+ remote_service,
1091
+ Socket::AF_UNSPEC,
1092
+ Socket::SOCK_STREAM, 0,
1093
+ flags)
1094
+
1095
+ if server == false and (local_host or local_service)
1096
+ local_host = local_host.to_s if local_host
1097
+ local_service = local_service.to_s if local_service
1098
+ @local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
1099
+ local_service,
1100
+ Socket::AF_UNSPEC,
1101
+ Socket::SOCK_STREAM, 0, 0)
1102
+ end
1103
+
1104
+ sock = nil
1105
+
1106
+ @remote_addrinfo.each do |addrinfo|
1107
+ flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
1108
+
1109
+ sock = Socket::Foreign.socket family, socket_type, protocol
1110
+ syscall = 'socket(2)'
1111
+
1112
+ next if sock < 0
1113
+
1114
+ if server
1115
+ FFI::MemoryPointer.new :socklen_t do |val|
1116
+ val.write_int 1
1117
+ level = Socket::Constants::SOL_SOCKET
1118
+ optname = Socket::Constants::SO_REUSEADDR
1119
+ error = Socket::Foreign.setsockopt(sock, level,
1120
+ optname, val,
1121
+ val.total)
1122
+ # Don't check error because if this fails, we just continue
1123
+ # anyway.
1124
+ end
1125
+
1126
+ status = Socket::Foreign.bind sock, sockaddr
1127
+ syscall = 'bind(2)'
1128
+ else
1129
+ if @local_addrinfo
1130
+ # Pick a local_addrinfo for the family and type of
1131
+ # the remote side
1132
+ li = @local_addrinfo.find do |i|
1133
+ i[1] == family && i[2] == socket_type
1134
+ end
1135
+
1136
+ if li
1137
+ status = Socket::Foreign.bind sock, li[4]
1138
+ syscall = 'bind(2)'
1139
+ else
1140
+ status = 1
1141
+ end
1142
+ else
1143
+ status = 1
1144
+ end
1145
+
1146
+ if status >= 0
1147
+ status = Socket::Foreign.connect sock, sockaddr
1148
+ syscall = 'connect(2)'
1149
+ end
1150
+ end
1151
+
1152
+ if status < 0
1153
+ Socket::Foreign.close sock
1154
+ else
1155
+ break
1156
+ end
1157
+ end
1158
+
1159
+ if status < 0
1160
+ Errno.handle syscall
1161
+ end
1162
+
1163
+ if server
1164
+ err = Socket::Foreign.listen sock, 5
1165
+ unless err == 0
1166
+ Socket::Foreign.close sock
1167
+ Errno.handle syscall
1168
+ end
1169
+ end
1170
+
1171
+ # Only setup once we have found a socket we can use. Otherwise
1172
+ # because we manually close a socket fd, we can create an IO fd
1173
+ # alias condition which causes EBADF because when an IO is finalized
1174
+ # and it's fd has been closed underneith it, we close someone elses
1175
+ # fd!
1176
+ IO.setup self, sock, nil, true
1177
+ end
1178
+ private :tcp_setup
1179
+
1180
+ def from_descriptor(descriptor)
1181
+ IO.setup self, descriptor, nil, true
1182
+
1183
+ self
1184
+ end
1185
+ end
1186
+
1187
+ class TCPServer < TCPSocket
1188
+
1189
+ include Socket::ListenAndAccept
1190
+
1191
+ def initialize(host, port = nil)
1192
+ if Fixnum === host and port.nil? then
1193
+ port = host
1194
+ host = nil
1195
+ end
1196
+
1197
+ if String === host and port.nil? then
1198
+ port = Integer(host)
1199
+ host = nil
1200
+ end
1201
+
1202
+ port = StringValue port unless port.kind_of? Fixnum
1203
+
1204
+ @host = host
1205
+ @port = port
1206
+
1207
+ tcp_setup @host, @port, nil, nil, true
1208
+ end
1209
+
1210
+ end