rubysl-socket 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +3 -10
- data/lib/rubysl/socket.rb +1519 -1
- data/lib/rubysl/socket/version.rb +1 -1
- data/rubysl-socket.gemspec +1 -2
- data/spec/socket/unpack_sockaddr_un_spec.rb +1 -0
- metadata +14 -29
- data/lib/rubysl/socket/socket.rb +0 -1210
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3001f70f2e5573956833527ffbe91569468d1db7
|
4
|
+
data.tar.gz: 8b6cbda543492cb5ba85b95950ec3442ff589020
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5ffc798a438d07cb7d87c63cec69bab0e8a05ff5e21b7d4c97401a6dec668e1948bb780aec783a0f0302643448130549923d04ea35d4cabb0d5d2c338c81295
|
7
|
+
data.tar.gz: d94be2dc455e26657f2e73b3490cfa36e9984757bfcdd1b51241afcf5b154fe295d5c1e024d4a95a05db2226e444297381c20e3a783510ad9832281b8eb5e755
|
data/.travis.yml
CHANGED
@@ -1,14 +1,7 @@
|
|
1
1
|
language: ruby
|
2
2
|
env:
|
3
3
|
- RUBYLIB=lib
|
4
|
-
|
5
|
-
script: mspec spec
|
4
|
+
script: bundle exec mspec
|
6
5
|
rvm:
|
7
|
-
- 1.
|
8
|
-
- rbx-
|
9
|
-
matrix:
|
10
|
-
exclude:
|
11
|
-
- rvm: 1.8.7
|
12
|
-
env: RUBYLIB=lib
|
13
|
-
- rvm: rbx-1
|
14
|
-
env: RUBYLIB=
|
6
|
+
- 1.9.3
|
7
|
+
- rbx-nightly-19mode
|
data/lib/rubysl/socket.rb
CHANGED
@@ -1,2 +1,1520 @@
|
|
1
|
-
require "rubysl/socket/socket"
|
2
1
|
require "rubysl/socket/version"
|
2
|
+
require "rubysl/fcntl"
|
3
|
+
|
4
|
+
class SocketError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
# @todo Socket#accept[_nonblock]
|
8
|
+
# @todo UNIXServer#accept[_nonblock]
|
9
|
+
# @todo UDPSocket#recvfrom
|
10
|
+
|
11
|
+
class BasicSocket < IO
|
12
|
+
FFI = Rubinius::FFI
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def from_descriptor(fixnum)
|
16
|
+
sock = allocate()
|
17
|
+
sock.from_descriptor(fixnum)
|
18
|
+
return sock
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :for_fd :from_descriptor
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_descriptor(fixnum)
|
25
|
+
IO.setup self, fixnum, nil, true
|
26
|
+
return self
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.do_not_reverse_lookup=(setting)
|
30
|
+
@no_reverse_lookup = setting
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.do_not_reverse_lookup
|
34
|
+
@no_reverse_lookup = true unless defined? @no_reverse_lookup
|
35
|
+
@no_reverse_lookup
|
36
|
+
end
|
37
|
+
|
38
|
+
def do_not_reverse_lookup=(setting)
|
39
|
+
@no_reverse_lookup = setting
|
40
|
+
end
|
41
|
+
|
42
|
+
def do_not_reverse_lookup
|
43
|
+
@no_reverse_lookup
|
44
|
+
end
|
45
|
+
|
46
|
+
def getsockopt(level, optname)
|
47
|
+
data = Socket::Foreign.getsockopt(descriptor, level, optname)
|
48
|
+
|
49
|
+
sockaddr = Socket::Foreign.getsockname(descriptor)
|
50
|
+
family, = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV
|
51
|
+
Socket::Option.new(family, level, optname, data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def setsockopt(level_or_option, optname=nil, optval=nil)
|
55
|
+
level = nil
|
56
|
+
|
57
|
+
case level_or_option
|
58
|
+
when Socket::Option
|
59
|
+
if !optname.nil?
|
60
|
+
raise ArgumentError, "given 2, expected 3"
|
61
|
+
end
|
62
|
+
level = level_or_option.level
|
63
|
+
optname = level_or_option.optname
|
64
|
+
optval = level_or_option.data
|
65
|
+
else
|
66
|
+
if level_or_option.nil? or optname.nil?
|
67
|
+
nb_arg = 3 - [level_or_option, optname, optval].count(nil)
|
68
|
+
raise ArgumentError, "given #{nb_arg}, expected 3"
|
69
|
+
end
|
70
|
+
level = level_or_option
|
71
|
+
end
|
72
|
+
|
73
|
+
optval = 1 if optval == true
|
74
|
+
optval = 0 if optval == false
|
75
|
+
|
76
|
+
error = 0
|
77
|
+
|
78
|
+
sockname = Socket::Foreign.getsockname descriptor
|
79
|
+
family = Socket::Foreign.getnameinfo(sockname).first
|
80
|
+
|
81
|
+
level = level_arg(family, level)
|
82
|
+
optname = optname_arg(level, optname)
|
83
|
+
|
84
|
+
case optval
|
85
|
+
when Fixnum then
|
86
|
+
FFI::MemoryPointer.new :socklen_t do |val|
|
87
|
+
val.write_int optval
|
88
|
+
error = Socket::Foreign.setsockopt(descriptor, level,
|
89
|
+
optname, val,
|
90
|
+
val.total)
|
91
|
+
end
|
92
|
+
when String then
|
93
|
+
FFI::MemoryPointer.new optval.bytesize do |val|
|
94
|
+
val.write_string optval, optval.bytesize
|
95
|
+
error = Socket::Foreign.setsockopt(descriptor, level,
|
96
|
+
optname, val,
|
97
|
+
optval.size)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
raise TypeError, "socket option should be a String, a Fixnum, true, or false"
|
101
|
+
end
|
102
|
+
|
103
|
+
Errno.handle "Unable to set socket option" unless error == 0
|
104
|
+
|
105
|
+
return 0
|
106
|
+
end
|
107
|
+
|
108
|
+
def getsockname()
|
109
|
+
return Socket::Foreign.getsockname(descriptor)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Obtain peername information for this socket.
|
114
|
+
#
|
115
|
+
# @see Socket.getpeername
|
116
|
+
#
|
117
|
+
def getpeername()
|
118
|
+
Socket::Foreign.getpeername @descriptor
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
#
|
123
|
+
#
|
124
|
+
def send(message, flags, to = nil)
|
125
|
+
connect to if to
|
126
|
+
|
127
|
+
bytes = message.bytesize
|
128
|
+
bytes_sent = 0
|
129
|
+
|
130
|
+
FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
|
131
|
+
buffer.write_string message, bytes
|
132
|
+
bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
|
133
|
+
Errno.handle 'send(2)' if bytes_sent < 0
|
134
|
+
end
|
135
|
+
|
136
|
+
bytes_sent
|
137
|
+
end
|
138
|
+
|
139
|
+
def recvfrom(bytes_to_read, flags = 0)
|
140
|
+
# FIXME 0 is knowledge from io.cpp
|
141
|
+
return socket_recv(bytes_to_read, flags, 0)
|
142
|
+
end
|
143
|
+
|
144
|
+
def recv(bytes_to_read, flags = 0)
|
145
|
+
# FIXME 0 is knowledge from io.cpp
|
146
|
+
return socket_recv(bytes_to_read, flags, 0)
|
147
|
+
end
|
148
|
+
|
149
|
+
def close_read
|
150
|
+
ensure_open
|
151
|
+
|
152
|
+
# If we were only in readonly mode, close it all together
|
153
|
+
if @mode & ACCMODE == RDONLY
|
154
|
+
return close
|
155
|
+
end
|
156
|
+
|
157
|
+
# MRI doesn't check if shutdown worked, so we don't.
|
158
|
+
Socket::Foreign.shutdown @descriptor, 0
|
159
|
+
|
160
|
+
@mode = WRONLY
|
161
|
+
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
|
165
|
+
def close_write
|
166
|
+
ensure_open
|
167
|
+
|
168
|
+
# If we were only in writeonly mode, close it all together
|
169
|
+
if @mode & ACCMODE == WRONLY
|
170
|
+
return close
|
171
|
+
end
|
172
|
+
|
173
|
+
Socket::Foreign.shutdown @descriptor, 1
|
174
|
+
|
175
|
+
# Mark it as read only
|
176
|
+
@mode = RDONLY
|
177
|
+
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Sets socket nonblocking and reads up to given number of bytes.
|
183
|
+
#
|
184
|
+
# @todo Should EWOULDBLOCK be passed unchanged? --rue
|
185
|
+
#
|
186
|
+
def recv_nonblock(bytes_to_read, flags = 0)
|
187
|
+
fcntl Fcntl::F_SETFL, Fcntl::O_NONBLOCK
|
188
|
+
socket_recv bytes_to_read, flags, 0
|
189
|
+
rescue Errno::EWOULDBLOCK
|
190
|
+
raise Errno::EAGAIN
|
191
|
+
end
|
192
|
+
|
193
|
+
def shutdown(how = 2)
|
194
|
+
err = Socket::Foreign.shutdown @descriptor, how
|
195
|
+
Errno.handle "shutdown" unless err == 0
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def level_arg(family, level)
|
201
|
+
case level
|
202
|
+
when Symbol, String
|
203
|
+
if Socket::Constants.const_defined?(level)
|
204
|
+
Socket::Constants.const_get(level)
|
205
|
+
else
|
206
|
+
if is_ip_family?(family)
|
207
|
+
ip_level_to_int(level)
|
208
|
+
else
|
209
|
+
unknown_level_to_int(level)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
else
|
213
|
+
level
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def optname_arg(level, optname)
|
218
|
+
case optname
|
219
|
+
when Symbol, String
|
220
|
+
if Socket::Constants.const_defined?(optname)
|
221
|
+
Socket::Constants.const_get(optname)
|
222
|
+
else
|
223
|
+
case(level)
|
224
|
+
when Socket::Constants::SOL_SOCKET
|
225
|
+
constant("SO", optname)
|
226
|
+
when Socket::Constants::IPPROTO_IP
|
227
|
+
constant("IP", optname)
|
228
|
+
when Socket::Constants::IPPROTO_TCP
|
229
|
+
constant("TCP", optname)
|
230
|
+
when Socket::Constants::IPPROTO_UDP
|
231
|
+
constant("UDP", optname)
|
232
|
+
else
|
233
|
+
if Socket::Constants.const_defined?(Socket::Constants::IPPROTO_IPV6) &&
|
234
|
+
level == Socket::Constants::IPPROTO_IPV6
|
235
|
+
constant("IPV6", optname)
|
236
|
+
else
|
237
|
+
optname
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
else
|
242
|
+
optname
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def is_ip_family?(family)
|
247
|
+
family == "AF_INET" || family == "AF_INET6"
|
248
|
+
end
|
249
|
+
|
250
|
+
def ip_level_to_int(level)
|
251
|
+
prefixes = ["IPPROTO", "SOL"]
|
252
|
+
prefixes.each do |prefix|
|
253
|
+
if Socket::Constants.const_defined?("#{prefix}_#{level}")
|
254
|
+
return Socket::Constants.const_get("#{prefix}_#{level}")
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def unknown_level_to_int(level)
|
260
|
+
constant("SOL", level)
|
261
|
+
end
|
262
|
+
|
263
|
+
def constant(prefix, suffix)
|
264
|
+
if Socket::Constants.const_defined?("#{prefix}_#{suffix}")
|
265
|
+
Socket::Constants.const_get("#{prefix}_#{suffix}")
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
class Socket < BasicSocket
|
272
|
+
FFI = Rubinius::FFI
|
273
|
+
|
274
|
+
# @todo Is omitting empty-value constants reasonable? --rue
|
275
|
+
module Constants
|
276
|
+
all_valid = FFI.config_hash("socket").reject {|name, value| value.empty? }
|
277
|
+
|
278
|
+
all_valid.each {|name, value| const_set name, Integer(value) }
|
279
|
+
|
280
|
+
# MRI compat. socket is a pretty screwed up API. All the constants in Constants
|
281
|
+
# must also be directly accessible on Socket itself. This means it's not enough
|
282
|
+
# to include Constants into Socket, because Socket#const_defined? must be able
|
283
|
+
# to see constants like AF_INET6 directly on Socket, but #const_defined? doesn't
|
284
|
+
# check inherited constants. O_o
|
285
|
+
#
|
286
|
+
all_valid.each {|name, value| Socket.const_set name, Integer(value) }
|
287
|
+
|
288
|
+
|
289
|
+
afamilies = all_valid.to_a.select { |name,| name =~ /^AF_/ }
|
290
|
+
afamilies.map! {|name, value| [value.to_i, name] }
|
291
|
+
|
292
|
+
pfamilies = all_valid.to_a.select { |name,| name =~ /^PF_/ }
|
293
|
+
pfamilies.map! {|name, value| [value.to_i, name] }
|
294
|
+
|
295
|
+
AF_TO_FAMILY = Hash[*afamilies.flatten]
|
296
|
+
PF_TO_FAMILY = Hash[*pfamilies.flatten]
|
297
|
+
end
|
298
|
+
|
299
|
+
module Foreign
|
300
|
+
extend FFI::Library
|
301
|
+
|
302
|
+
class Addrinfo < FFI::Struct
|
303
|
+
config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
|
304
|
+
:ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
|
305
|
+
end
|
306
|
+
|
307
|
+
class Linger < FFI::Struct
|
308
|
+
config("rbx.platform.linger", :l_onoff, :l_linger)
|
309
|
+
end
|
310
|
+
|
311
|
+
attach_function :_bind, "bind", [:int, :pointer, :socklen_t], :int
|
312
|
+
attach_function :_connect, "connect", [:int, :pointer, :socklen_t], :int
|
313
|
+
|
314
|
+
attach_function :accept, [:int, :pointer, :pointer], :int
|
315
|
+
attach_function :close, [:int], :int
|
316
|
+
attach_function :shutdown, [:int, :int], :int
|
317
|
+
attach_function :listen, [:int, :int], :int
|
318
|
+
attach_function :socket, [:int, :int, :int], :int
|
319
|
+
attach_function :send, [:int, :pointer, :size_t, :int], :ssize_t
|
320
|
+
attach_function :recv, [:int, :pointer, :size_t, :int], :ssize_t
|
321
|
+
attach_function :recvfrom, [:int, :pointer, :size_t, :int,
|
322
|
+
:pointer, :pointer], :int
|
323
|
+
|
324
|
+
attach_function :_getsockopt,
|
325
|
+
"getsockopt", [:int, :int, :int, :pointer, :pointer], :int
|
326
|
+
attach_function :_getaddrinfo,
|
327
|
+
"getaddrinfo", [:string, :string, :pointer, :pointer], :int
|
328
|
+
|
329
|
+
attach_function :gai_strerror, [:int], :string
|
330
|
+
attach_function :setsockopt, [:int, :int, :int, :pointer, :socklen_t], :int
|
331
|
+
attach_function :freeaddrinfo, [:pointer], :void
|
332
|
+
attach_function :_getpeername, "getpeername", [:int, :pointer, :pointer], :int
|
333
|
+
attach_function :_getsockname, "getsockname", [:int, :pointer, :pointer], :int
|
334
|
+
|
335
|
+
attach_function :socketpair, [:int, :int, :int, :pointer], :int
|
336
|
+
|
337
|
+
attach_function :gethostname, [:pointer, :size_t], :int
|
338
|
+
attach_function :getservbyname, [:pointer, :pointer], :pointer
|
339
|
+
|
340
|
+
attach_function :htons, [:uint16_t], :uint16_t
|
341
|
+
attach_function :ntohs, [:uint16_t], :uint16_t
|
342
|
+
|
343
|
+
attach_function :_getnameinfo,
|
344
|
+
"getnameinfo", [:pointer, :socklen_t, :pointer, :socklen_t,
|
345
|
+
:pointer, :socklen_t, :int], :int
|
346
|
+
|
347
|
+
def self.bind(descriptor, sockaddr)
|
348
|
+
FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
|
349
|
+
sockaddr_p.write_string sockaddr, sockaddr.bytesize
|
350
|
+
|
351
|
+
_bind descriptor, sockaddr_p, sockaddr.bytesize
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def self.connect(descriptor, sockaddr)
|
356
|
+
err = 0
|
357
|
+
FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
|
358
|
+
sockaddr_p.write_string sockaddr, sockaddr.bytesize
|
359
|
+
|
360
|
+
err = _connect descriptor, sockaddr_p, sockaddr.bytesize
|
361
|
+
end
|
362
|
+
|
363
|
+
err
|
364
|
+
end
|
365
|
+
|
366
|
+
def self.getsockopt(descriptor, level, optname)
|
367
|
+
FFI::MemoryPointer.new 256 do |val| # HACK magic number
|
368
|
+
FFI::MemoryPointer.new :socklen_t do |length|
|
369
|
+
length.write_int 256 # HACK magic number
|
370
|
+
|
371
|
+
err = _getsockopt descriptor, level, optname, val, length
|
372
|
+
|
373
|
+
Errno.handle "Unable to get socket option" unless err == 0
|
374
|
+
|
375
|
+
return val.read_string(length.read_int)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def self.getaddrinfo(host, service = nil, family = nil, socktype = nil, protocol = nil, flags = nil)
|
381
|
+
hints = Addrinfo.new
|
382
|
+
hints[:ai_family] = family || 0
|
383
|
+
hints[:ai_socktype] = socktype || 0
|
384
|
+
hints[:ai_protocol] = protocol || 0
|
385
|
+
hints[:ai_flags] = flags || 0
|
386
|
+
|
387
|
+
if host && (host.empty? || host == '<any>')
|
388
|
+
host = "0.0.0.0"
|
389
|
+
elsif host == '<broadcast>'
|
390
|
+
host = '255.255.255.255'
|
391
|
+
end
|
392
|
+
|
393
|
+
res_p = FFI::MemoryPointer.new :pointer
|
394
|
+
|
395
|
+
err = _getaddrinfo host, service, hints.pointer, res_p
|
396
|
+
|
397
|
+
raise SocketError, gai_strerror(err) unless err == 0
|
398
|
+
|
399
|
+
ptr = res_p.read_pointer
|
400
|
+
|
401
|
+
return [] unless ptr
|
402
|
+
|
403
|
+
res = Addrinfo.new ptr
|
404
|
+
|
405
|
+
addrinfos = []
|
406
|
+
|
407
|
+
while true
|
408
|
+
addrinfo = []
|
409
|
+
addrinfo << res[:ai_flags]
|
410
|
+
addrinfo << res[:ai_family]
|
411
|
+
addrinfo << res[:ai_socktype]
|
412
|
+
addrinfo << res[:ai_protocol]
|
413
|
+
addrinfo << res[:ai_addr].read_string(res[:ai_addrlen])
|
414
|
+
addrinfo << res[:ai_canonname]
|
415
|
+
|
416
|
+
addrinfos << addrinfo
|
417
|
+
|
418
|
+
break unless res[:ai_next]
|
419
|
+
|
420
|
+
res = Addrinfo.new res[:ai_next]
|
421
|
+
end
|
422
|
+
|
423
|
+
return addrinfos
|
424
|
+
ensure
|
425
|
+
hints.free if hints
|
426
|
+
|
427
|
+
if res_p
|
428
|
+
ptr = res_p.read_pointer
|
429
|
+
|
430
|
+
# Be sure to feed a legit pointer to freeaddrinfo
|
431
|
+
if ptr and !ptr.null?
|
432
|
+
freeaddrinfo ptr
|
433
|
+
end
|
434
|
+
res_p.free
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def self.getaddress(host)
|
439
|
+
addrinfos = getaddrinfo(host)
|
440
|
+
unpack_sockaddr_in(addrinfos.first[4], false).first
|
441
|
+
end
|
442
|
+
|
443
|
+
def self.getnameinfo(sockaddr, flags = Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV,
|
444
|
+
reverse_lookup = !BasicSocket.do_not_reverse_lookup)
|
445
|
+
name_info = []
|
446
|
+
value = nil
|
447
|
+
|
448
|
+
FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
|
449
|
+
FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXHOST do |node|
|
450
|
+
FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXSERV do |service|
|
451
|
+
sockaddr_p.write_string sockaddr, sockaddr.bytesize
|
452
|
+
|
453
|
+
if reverse_lookup then
|
454
|
+
err = _getnameinfo(sockaddr_p, sockaddr.bytesize,
|
455
|
+
node, Socket::Constants::NI_MAXHOST, nil, 0, 0)
|
456
|
+
|
457
|
+
name_info[2] = node.read_string if err == 0
|
458
|
+
end
|
459
|
+
|
460
|
+
err = _getnameinfo(sockaddr_p, sockaddr.bytesize,
|
461
|
+
node, Socket::Constants::NI_MAXHOST,
|
462
|
+
service, Socket::Constants::NI_MAXSERV,
|
463
|
+
flags)
|
464
|
+
|
465
|
+
unless err == 0 then
|
466
|
+
raise SocketError, gai_strerror(err)
|
467
|
+
end
|
468
|
+
|
469
|
+
sa_family = SockAddr_In.new(sockaddr)[:sin_family]
|
470
|
+
|
471
|
+
name_info[0] = Socket::Constants::AF_TO_FAMILY[sa_family]
|
472
|
+
name_info[1] = service.read_string
|
473
|
+
name_info[3] = node.read_string
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
name_info[2] = name_info[3] if name_info[2].nil?
|
479
|
+
name_info
|
480
|
+
end
|
481
|
+
|
482
|
+
def self.getpeername(descriptor)
|
483
|
+
FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
|
484
|
+
FFI::MemoryPointer.new :socklen_t do |len_p|
|
485
|
+
len_p.write_int 128
|
486
|
+
|
487
|
+
err = _getpeername descriptor, sockaddr_storage_p, len_p
|
488
|
+
|
489
|
+
Errno.handle 'getpeername(2)' unless err == 0
|
490
|
+
|
491
|
+
sockaddr_storage_p.read_string len_p.read_int
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
def self.getsockname(descriptor)
|
497
|
+
FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
|
498
|
+
FFI::MemoryPointer.new :socklen_t do |len_p|
|
499
|
+
len_p.write_int 128
|
500
|
+
|
501
|
+
err = _getsockname descriptor, sockaddr_storage_p, len_p
|
502
|
+
|
503
|
+
Errno.handle 'getsockname(2)' unless err == 0
|
504
|
+
|
505
|
+
sockaddr_storage_p.read_string len_p.read_int
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def self.pack_sockaddr_in(host, port, family, type, flags)
|
511
|
+
hints = Addrinfo.new
|
512
|
+
hints[:ai_family] = family
|
513
|
+
hints[:ai_socktype] = type
|
514
|
+
hints[:ai_flags] = flags
|
515
|
+
|
516
|
+
if host && host.empty?
|
517
|
+
host = "0.0.0.0"
|
518
|
+
end
|
519
|
+
|
520
|
+
res_p = FFI::MemoryPointer.new :pointer
|
521
|
+
|
522
|
+
err = _getaddrinfo host, port.to_s, hints.pointer, res_p
|
523
|
+
|
524
|
+
raise SocketError, gai_strerror(err) unless err == 0
|
525
|
+
|
526
|
+
return [] if res_p.read_pointer.null?
|
527
|
+
|
528
|
+
res = Addrinfo.new res_p.read_pointer
|
529
|
+
|
530
|
+
return res[:ai_addr].read_string(res[:ai_addrlen])
|
531
|
+
|
532
|
+
ensure
|
533
|
+
hints.free if hints
|
534
|
+
|
535
|
+
if res_p then
|
536
|
+
ptr = res_p.read_pointer
|
537
|
+
|
538
|
+
freeaddrinfo ptr if ptr and not ptr.null?
|
539
|
+
|
540
|
+
res_p.free
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
|
545
|
+
family, port, host, ip = getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
|
546
|
+
# On some systems this doesn't fail for families other than AF_INET(6)
|
547
|
+
# so we raise manually here.
|
548
|
+
raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' unless family =~ /AF_INET/
|
549
|
+
return host, ip, port.to_i
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
module ListenAndAccept
|
554
|
+
include IO::Socketable
|
555
|
+
|
556
|
+
def listen(backlog)
|
557
|
+
backlog = Rubinius::Type.coerce_to backlog, Fixnum, :to_int
|
558
|
+
|
559
|
+
err = Socket::Foreign.listen descriptor, backlog
|
560
|
+
|
561
|
+
Errno.handle 'listen(2)' unless err == 0
|
562
|
+
|
563
|
+
err
|
564
|
+
end
|
565
|
+
|
566
|
+
def accept
|
567
|
+
return if closed?
|
568
|
+
|
569
|
+
fd = super
|
570
|
+
|
571
|
+
socket = self.class.superclass.allocate
|
572
|
+
IO.setup socket, fd, nil, true
|
573
|
+
socket.binmode
|
574
|
+
socket
|
575
|
+
end
|
576
|
+
|
577
|
+
#
|
578
|
+
# Set nonblocking and accept.
|
579
|
+
#
|
580
|
+
def accept_nonblock
|
581
|
+
return if closed?
|
582
|
+
|
583
|
+
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
584
|
+
|
585
|
+
fd = nil
|
586
|
+
sockaddr = nil
|
587
|
+
|
588
|
+
FFI::MemoryPointer.new 1024 do |sockaddr_p| # HACK from MRI
|
589
|
+
FFI::MemoryPointer.new :int do |size_p|
|
590
|
+
fd = Socket::Foreign.accept descriptor, sockaddr_p, size_p
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
Errno.handle 'accept(2)' if fd < 0
|
595
|
+
|
596
|
+
# TCPServer -> TCPSocket etc. *sigh*
|
597
|
+
socket = self.class.superclass.allocate
|
598
|
+
IO.setup socket, fd, nil, true
|
599
|
+
socket
|
600
|
+
end
|
601
|
+
|
602
|
+
end
|
603
|
+
|
604
|
+
include Socket::ListenAndAccept
|
605
|
+
|
606
|
+
class SockAddr_In < FFI::Struct
|
607
|
+
config("rbx.platform.sockaddr_in", :sin_family, :sin_port, :sin_addr, :sin_zero)
|
608
|
+
|
609
|
+
def initialize(sockaddrin)
|
610
|
+
@p = FFI::MemoryPointer.new sockaddrin.bytesize
|
611
|
+
@p.write_string(sockaddrin, sockaddrin.bytesize)
|
612
|
+
super(@p)
|
613
|
+
end
|
614
|
+
|
615
|
+
def to_s
|
616
|
+
@p.read_string(@p.total)
|
617
|
+
end
|
618
|
+
|
619
|
+
end
|
620
|
+
|
621
|
+
class Option
|
622
|
+
attr_reader :family, :level, :optname, :data
|
623
|
+
|
624
|
+
def self.bool(family, level, optname, bool)
|
625
|
+
data = [(bool ? 1 : 0)].pack('i')
|
626
|
+
new family, level, optname, data
|
627
|
+
end
|
628
|
+
|
629
|
+
def self.int(family, level, optname, integer)
|
630
|
+
new family, level, optname, [integer].pack('i')
|
631
|
+
end
|
632
|
+
|
633
|
+
def self.linger(onoff, secs)
|
634
|
+
linger = Socket::Foreign::Linger.new
|
635
|
+
|
636
|
+
case onoff
|
637
|
+
when Integer
|
638
|
+
linger[:l_onoff] = onoff
|
639
|
+
else
|
640
|
+
linger[:l_onoff] = onoff ? 1 : 0
|
641
|
+
end
|
642
|
+
linger[:l_linger] = secs
|
643
|
+
|
644
|
+
p = linger.to_ptr
|
645
|
+
data = p.read_string(p.total)
|
646
|
+
|
647
|
+
new :UNSPEC, :SOCKET, :LINGER, data
|
648
|
+
end
|
649
|
+
|
650
|
+
def initialize(family, level, optname, data)
|
651
|
+
@family = family_arg(family)
|
652
|
+
@family_name = family
|
653
|
+
@level = level_arg(@family, level)
|
654
|
+
@level_name = level
|
655
|
+
@optname = optname_arg(@level, optname)
|
656
|
+
@opt_name = optname
|
657
|
+
@data = data
|
658
|
+
end
|
659
|
+
|
660
|
+
def unpack(template)
|
661
|
+
@data.unpack template
|
662
|
+
end
|
663
|
+
|
664
|
+
def inspect
|
665
|
+
"#<#{self.class}: #@family_name #@level_name #@opt_name #{@data.inspect}>"
|
666
|
+
end
|
667
|
+
|
668
|
+
def bool
|
669
|
+
unless @data.length == Rubinius::FFI.type_size(:int)
|
670
|
+
raise TypeError, "size differ. expected as sizeof(int)=" +
|
671
|
+
"#{Rubinius::FFI.type_size(:int)} but #{@data.length}"
|
672
|
+
end
|
673
|
+
|
674
|
+
i = @data.unpack('i').first
|
675
|
+
i == 0 ? false : true
|
676
|
+
end
|
677
|
+
|
678
|
+
def int
|
679
|
+
unless @data.length == Rubinius::FFI.type_size(:int)
|
680
|
+
raise TypeError, "size differ. expected as sizeof(int)=" +
|
681
|
+
"#{Rubinius::FFI.type_size(:int)} but #{@data.length}"
|
682
|
+
end
|
683
|
+
@data.unpack('i').first
|
684
|
+
end
|
685
|
+
|
686
|
+
def linger
|
687
|
+
if @level != Socket::SOL_SOCKET || @optname != Socket::SO_LINGER
|
688
|
+
raise TypeError, "linger socket option expected"
|
689
|
+
end
|
690
|
+
if @data.bytesize != FFI.config("linger.sizeof")
|
691
|
+
raise TypeError, "size differ. expected as sizeof(struct linger)=" +
|
692
|
+
"#{FFI.config("linger.sizeof")} but #{@data.length}"
|
693
|
+
end
|
694
|
+
|
695
|
+
linger = Socket::Foreign::Linger.new
|
696
|
+
linger.to_ptr.write_string @data, @data.bytesize
|
697
|
+
|
698
|
+
onoff = nil
|
699
|
+
case linger[:l_onoff]
|
700
|
+
when 0 then onoff = false
|
701
|
+
when 1 then onoff = true
|
702
|
+
else onoff = linger[:l_onoff].to_i
|
703
|
+
end
|
704
|
+
|
705
|
+
[onoff, linger[:l_linger].to_i]
|
706
|
+
end
|
707
|
+
|
708
|
+
alias :to_s :data
|
709
|
+
|
710
|
+
|
711
|
+
private
|
712
|
+
|
713
|
+
def family_arg(family)
|
714
|
+
case family
|
715
|
+
when Symbol, String
|
716
|
+
f = family.to_s
|
717
|
+
if f[0..2] != 'AF_'
|
718
|
+
f = 'AF_' + f
|
719
|
+
end
|
720
|
+
Socket.const_get f
|
721
|
+
when Integer
|
722
|
+
family
|
723
|
+
else
|
724
|
+
raise SocketError, "unknown socket domain: #{family}"
|
725
|
+
end
|
726
|
+
rescue NameError
|
727
|
+
raise SocketError, "unknown socket domain: #{family}"
|
728
|
+
end
|
729
|
+
|
730
|
+
def level_arg(family, level)
|
731
|
+
case level
|
732
|
+
when Symbol, String
|
733
|
+
if Socket::Constants.const_defined?(level)
|
734
|
+
Socket::Constants.const_get(level)
|
735
|
+
else
|
736
|
+
if is_ip_family?(family)
|
737
|
+
ip_level_to_int(level)
|
738
|
+
else
|
739
|
+
unknown_level_to_int(level)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
when Integer
|
743
|
+
level
|
744
|
+
else
|
745
|
+
raise SocketError, "unknown protocol level: #{level}"
|
746
|
+
end
|
747
|
+
rescue NameError
|
748
|
+
raise SocketError, "unknown protocol level: #{level}"
|
749
|
+
end
|
750
|
+
|
751
|
+
def optname_arg(level, optname)
|
752
|
+
case optname
|
753
|
+
when Symbol, String
|
754
|
+
if Socket::Constants.const_defined?(optname)
|
755
|
+
Socket::Constants.const_get(optname)
|
756
|
+
else
|
757
|
+
case(level)
|
758
|
+
when Socket::Constants::SOL_SOCKET
|
759
|
+
constant("SO", optname)
|
760
|
+
when Socket::Constants::IPPROTO_IP
|
761
|
+
constant("IP", optname)
|
762
|
+
when Socket::Constants::IPPROTO_TCP
|
763
|
+
constant("TCP", optname)
|
764
|
+
when Socket::Constants::IPPROTO_UDP
|
765
|
+
constant("UDP", optname)
|
766
|
+
else
|
767
|
+
if Socket::Constants.const_defined?(Socket::Constants::IPPROTO_IPV6) &&
|
768
|
+
level == Socket::Constants::IPPROTO_IPV6
|
769
|
+
constant("IPV6", optname)
|
770
|
+
else
|
771
|
+
optname
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
else
|
776
|
+
optname
|
777
|
+
end
|
778
|
+
rescue NameError
|
779
|
+
raise SocketError, "unknown socket level option name: #{optname}"
|
780
|
+
end
|
781
|
+
|
782
|
+
def is_ip_family?(family)
|
783
|
+
[Socket::AF_INET, Socket::AF_INET6].include? family
|
784
|
+
end
|
785
|
+
|
786
|
+
def ip_level_to_int(level)
|
787
|
+
prefixes = ["IPPROTO", "SOL"]
|
788
|
+
prefixes.each do |prefix|
|
789
|
+
if Socket::Constants.const_defined?("#{prefix}_#{level}")
|
790
|
+
return Socket::Constants.const_get("#{prefix}_#{level}")
|
791
|
+
end
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
def unknown_level_to_int(level)
|
796
|
+
constant("SOL", level)
|
797
|
+
end
|
798
|
+
|
799
|
+
def constant(prefix, suffix)
|
800
|
+
#if Socket::Constants.const_defined?("#{prefix}_#{suffix}")
|
801
|
+
Socket::Constants.const_get("#{prefix}_#{suffix}")
|
802
|
+
#end
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
# If we have the details to support unix sockets, do so.
|
807
|
+
if FFI.config("sockaddr_un.sun_family.offset") and Socket::Constants.const_defined?(:AF_UNIX)
|
808
|
+
class SockAddr_Un < FFI::Struct
|
809
|
+
config("rbx.platform.sockaddr_un", :sun_family, :sun_path)
|
810
|
+
|
811
|
+
def initialize(filename = nil)
|
812
|
+
maxfnsize = self.size - (FFI.config("sockaddr_un.sun_family.size") + 1)
|
813
|
+
|
814
|
+
if filename and filename.length > maxfnsize
|
815
|
+
raise ArgumentError, "too long unix socket path (max: #{maxfnsize}bytes)"
|
816
|
+
end
|
817
|
+
@p = FFI::MemoryPointer.new self.size
|
818
|
+
if filename
|
819
|
+
@p.write_string( [Socket::AF_UNIX].pack("s") + filename )
|
820
|
+
end
|
821
|
+
super @p
|
822
|
+
end
|
823
|
+
|
824
|
+
def to_s
|
825
|
+
@p.read_string self.size
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
def self.getaddrinfo(host, service, family = 0, socktype = 0,
|
831
|
+
protocol = 0, flags = 0)
|
832
|
+
if service
|
833
|
+
if service.kind_of? Fixnum
|
834
|
+
service = service.to_s
|
835
|
+
else
|
836
|
+
service = StringValue(service)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
|
841
|
+
protocol, flags)
|
842
|
+
|
843
|
+
addrinfos.map do |ai|
|
844
|
+
addrinfo = []
|
845
|
+
addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]
|
846
|
+
|
847
|
+
sockaddr = Foreign.unpack_sockaddr_in ai[4], !BasicSocket.do_not_reverse_lookup
|
848
|
+
|
849
|
+
addrinfo << sockaddr.pop # port
|
850
|
+
addrinfo.concat sockaddr # hosts
|
851
|
+
addrinfo << ai[1]
|
852
|
+
addrinfo << ai[2]
|
853
|
+
addrinfo << ai[3]
|
854
|
+
addrinfo
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
def self.getnameinfo(sockaddr, flags = 0)
|
859
|
+
port = nil
|
860
|
+
host = nil
|
861
|
+
family = Socket::AF_UNSPEC
|
862
|
+
if sockaddr.is_a?(Array)
|
863
|
+
if sockaddr.size == 3
|
864
|
+
af = sockaddr[0]
|
865
|
+
port = sockaddr[1]
|
866
|
+
host = sockaddr[2]
|
867
|
+
elsif sockaddr.size == 4
|
868
|
+
af = sockaddr[0]
|
869
|
+
port = sockaddr[1]
|
870
|
+
host = sockaddr[3] || sockaddr[2]
|
871
|
+
else
|
872
|
+
raise ArgumentError, "array size should be 3 or 4, #{sockaddr.size} given"
|
873
|
+
end
|
874
|
+
|
875
|
+
if family == "AF_INET"
|
876
|
+
family = Socket::AF_INET
|
877
|
+
elsif family == "AF_INET6"
|
878
|
+
family = Socket::AF_INET6
|
879
|
+
end
|
880
|
+
sockaddr = Socket::Foreign.pack_sockaddr_in(host, port, family, Socket::SOCK_DGRAM, 0)
|
881
|
+
end
|
882
|
+
|
883
|
+
family, port, host, ip = Socket::Foreign.getnameinfo(sockaddr, flags)
|
884
|
+
[host, port]
|
885
|
+
end
|
886
|
+
|
887
|
+
def self.gethostname
|
888
|
+
FFI::MemoryPointer.new :char, 1024 do |mp| #magic number 1024 comes from MRI
|
889
|
+
Socket::Foreign.gethostname(mp, 1024) # same here
|
890
|
+
return mp.read_string
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
def self.gethostbyname(hostname)
|
895
|
+
addrinfos = Socket.getaddrinfo(hostname, nil)
|
896
|
+
|
897
|
+
hostname = addrinfos.first[2]
|
898
|
+
family = addrinfos.first[4]
|
899
|
+
addresses = []
|
900
|
+
alternatives = []
|
901
|
+
addrinfos.each do |a|
|
902
|
+
alternatives << a[2] unless a[2] == hostname
|
903
|
+
# transform addresses to packed strings
|
904
|
+
if a[4] == family
|
905
|
+
sockaddr = Socket.sockaddr_in(1, a[3])
|
906
|
+
if family == AF_INET
|
907
|
+
# IPv4 address
|
908
|
+
offset = FFI.config("sockaddr_in.sin_addr.offset")
|
909
|
+
size = FFI.config("sockaddr_in.sin_addr.size")
|
910
|
+
addresses << sockaddr.byteslice(offset, size)
|
911
|
+
elsif family == AF_INET6
|
912
|
+
# Ipv6 address
|
913
|
+
offset = FFI.config("sockaddr_in6.sin6_addr.offset")
|
914
|
+
size = FFI.config("sockaddr_in6.sin6_addr.size")
|
915
|
+
addresses << sockaddr.byteslice(offset, size)
|
916
|
+
else
|
917
|
+
addresses << a[3]
|
918
|
+
end
|
919
|
+
end
|
920
|
+
end
|
921
|
+
|
922
|
+
[hostname, alternatives.uniq, family] + addresses.uniq
|
923
|
+
end
|
924
|
+
|
925
|
+
|
926
|
+
class Servent < FFI::Struct
|
927
|
+
config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
|
928
|
+
|
929
|
+
def initialize(data)
|
930
|
+
@p = FFI::MemoryPointer.new data.bytesize
|
931
|
+
@p.write_string(data, data.bytesize)
|
932
|
+
super(@p)
|
933
|
+
end
|
934
|
+
|
935
|
+
def to_s
|
936
|
+
@p.read_string(size)
|
937
|
+
end
|
938
|
+
|
939
|
+
end
|
940
|
+
|
941
|
+
def self.getservbyname(service, proto='tcp')
|
942
|
+
FFI::MemoryPointer.new :char, service.length + 1 do |svc|
|
943
|
+
FFI::MemoryPointer.new :char, proto.length + 1 do |prot|
|
944
|
+
svc.write_string(service + "\0")
|
945
|
+
prot.write_string(proto + "\0")
|
946
|
+
fn = Socket::Foreign.getservbyname(svc, prot)
|
947
|
+
|
948
|
+
raise SocketError, "no such service #{service}/#{proto}" if fn.nil?
|
949
|
+
|
950
|
+
s = Servent.new(fn.read_string(Servent.size))
|
951
|
+
return Socket::Foreign.ntohs(s[:s_port])
|
952
|
+
end
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
|
957
|
+
Socket::Foreign.pack_sockaddr_in host, port, Socket::AF_UNSPEC, type, flags
|
958
|
+
end
|
959
|
+
|
960
|
+
def self.unpack_sockaddr_in(sockaddr)
|
961
|
+
host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false
|
962
|
+
|
963
|
+
return [port, address]
|
964
|
+
rescue SocketError => e
|
965
|
+
if e.message =~ /ai_family not supported/ then # HACK platform specific?
|
966
|
+
raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
|
967
|
+
else
|
968
|
+
raise e
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
def self.socketpair(domain, type, protocol, klass=self)
|
973
|
+
if domain.kind_of? String
|
974
|
+
if domain.prefix? "AF_" or domain.prefix? "PF_"
|
975
|
+
begin
|
976
|
+
domain = Socket::Constants.const_get(domain)
|
977
|
+
rescue NameError
|
978
|
+
raise SocketError, "unknown socket domain #{domani}"
|
979
|
+
end
|
980
|
+
else
|
981
|
+
raise SocketError, "unknown socket domain #{domani}"
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
type = get_socket_type(type)
|
986
|
+
|
987
|
+
FFI::MemoryPointer.new :int, 2 do |mp|
|
988
|
+
Socket::Foreign.socketpair(domain, type, protocol, mp)
|
989
|
+
fd0, fd1 = mp.read_array_of_int(2)
|
990
|
+
|
991
|
+
[ klass.from_descriptor(fd0), klass.from_descriptor(fd1) ]
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
class << self
|
996
|
+
alias_method :sockaddr_in, :pack_sockaddr_in
|
997
|
+
alias_method :pair, :socketpair
|
998
|
+
end
|
999
|
+
|
1000
|
+
# Only define these methods if we support unix sockets
|
1001
|
+
if self.const_defined?(:SockAddr_Un)
|
1002
|
+
def self.pack_sockaddr_un(file)
|
1003
|
+
SockAddr_Un.new(file).to_s
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def self.unpack_sockaddr_un(addr)
|
1007
|
+
|
1008
|
+
if addr.bytesize > FFI.config("sockaddr_un.sizeof")
|
1009
|
+
raise TypeError, "too long sockaddr_un - #{addr.bytesize} longer than #{FFI.config("sockaddr_un.sizeof")}"
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
struct = SockAddr_Un.new
|
1013
|
+
struct.pointer.write_string(addr, addr.bytesize)
|
1014
|
+
|
1015
|
+
struct[:sun_path]
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
class << self
|
1019
|
+
alias_method :sockaddr_un, :pack_sockaddr_un
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
def initialize(family, socket_type, protocol=0)
|
1024
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1025
|
+
family = self.class.get_protocol_family(family)
|
1026
|
+
socket_type = self.class.get_socket_type(socket_type)
|
1027
|
+
descriptor = Socket::Foreign.socket family, socket_type, protocol
|
1028
|
+
|
1029
|
+
Errno.handle 'socket(2)' if descriptor < 0
|
1030
|
+
|
1031
|
+
IO.setup self, descriptor, nil, true
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def bind(server_sockaddr)
|
1035
|
+
err = Socket::Foreign.bind(descriptor, server_sockaddr)
|
1036
|
+
Errno.handle 'bind(2)' unless err == 0
|
1037
|
+
err
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
# @todo Should this be closing the descriptor? --rue
|
1041
|
+
def connect(sockaddr, extra=nil)
|
1042
|
+
if extra
|
1043
|
+
sockaddr = Socket.pack_sockaddr_in sockaddr, extra
|
1044
|
+
else
|
1045
|
+
sockaddr = StringValue(sockaddr)
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
status = Socket::Foreign.connect descriptor, sockaddr
|
1049
|
+
|
1050
|
+
if status < 0
|
1051
|
+
begin
|
1052
|
+
Errno.handle "connect(2)"
|
1053
|
+
rescue Errno::EISCONN
|
1054
|
+
return 0
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
return 0
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def connect_nonblock(sockaddr)
|
1062
|
+
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
1063
|
+
|
1064
|
+
status = Socket::Foreign.connect descriptor, StringValue(sockaddr)
|
1065
|
+
if status < 0
|
1066
|
+
Errno.handle "connect(2)"
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
return status
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
def self.get_protocol_family(family)
|
1073
|
+
case family
|
1074
|
+
when Fixnum
|
1075
|
+
return family
|
1076
|
+
when String
|
1077
|
+
# do nothing
|
1078
|
+
when Symbol
|
1079
|
+
family = family.to_s
|
1080
|
+
else
|
1081
|
+
family = StringValue(family)
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
family = "PF_#{family}" unless family[0, 3] == "PF_"
|
1085
|
+
Socket::Constants.const_get family
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
def self.get_socket_type(type)
|
1089
|
+
if type.kind_of? String
|
1090
|
+
if type.prefix? "SOCK_"
|
1091
|
+
begin
|
1092
|
+
type = Socket::Constants.const_get(type)
|
1093
|
+
rescue NameError
|
1094
|
+
raise SocketError, "unknown socket type #{type}"
|
1095
|
+
end
|
1096
|
+
else
|
1097
|
+
raise SocketError, "unknown socket type #{type}"
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
if type.kind_of? Symbol
|
1102
|
+
begin
|
1103
|
+
type = Socket::Constants.const_get("SOCK_#{type}")
|
1104
|
+
rescue NameError
|
1105
|
+
raise SocketError, "unknown socket type #{type}"
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
type
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
class UNIXSocket < BasicSocket
|
1114
|
+
include IO::TransferIO
|
1115
|
+
|
1116
|
+
# Coding to the lowest standard here.
|
1117
|
+
def recvfrom(bytes_read, flags = 0)
|
1118
|
+
# FIXME 2 is hardcoded knowledge from io.cpp
|
1119
|
+
socket_recv(bytes_read, flags, 2)
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def initialize(path)
|
1123
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1124
|
+
@path = path
|
1125
|
+
unix_setup
|
1126
|
+
@path = "" # Client
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
def path
|
1130
|
+
unless @path
|
1131
|
+
sockaddr = Socket::Foreign.getsockname descriptor
|
1132
|
+
_, @path = sockaddr.unpack('SZ*')
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
return @path
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
def from_descriptor(fixnum)
|
1139
|
+
super
|
1140
|
+
@path = nil
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def unix_setup(server = false)
|
1144
|
+
status = nil
|
1145
|
+
phase = 'socket(2)'
|
1146
|
+
sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
|
1147
|
+
|
1148
|
+
Errno.handle phase if sock < 0
|
1149
|
+
|
1150
|
+
IO.setup self, sock, 'r+', true
|
1151
|
+
|
1152
|
+
sockaddr = Socket.pack_sockaddr_un(@path)
|
1153
|
+
|
1154
|
+
if server then
|
1155
|
+
phase = 'bind(2)'
|
1156
|
+
status = Socket::Foreign.bind descriptor, sockaddr
|
1157
|
+
else
|
1158
|
+
phase = 'connect(2)'
|
1159
|
+
status = Socket::Foreign.connect descriptor, sockaddr
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
if status < 0 then
|
1163
|
+
close
|
1164
|
+
Errno.handle phase
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
if server then
|
1168
|
+
phase = 'listen(2)'
|
1169
|
+
status = Socket::Foreign.listen descriptor, 5
|
1170
|
+
if status < 0
|
1171
|
+
close
|
1172
|
+
Errno.handle phase
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
return sock
|
1177
|
+
end
|
1178
|
+
private :unix_setup
|
1179
|
+
|
1180
|
+
def addr
|
1181
|
+
sockaddr = Socket::Foreign.getsockname descriptor
|
1182
|
+
_, sock_path = sockaddr.unpack('SZ*')
|
1183
|
+
["AF_UNIX", sock_path]
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
def peeraddr
|
1187
|
+
sockaddr = Socket::Foreign.getpeername descriptor
|
1188
|
+
_, sock_path = sockaddr.unpack('SZ*')
|
1189
|
+
["AF_UNIX", sock_path]
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
def recv_io(klass=IO, mode=nil)
|
1193
|
+
begin
|
1194
|
+
fd = recv_fd
|
1195
|
+
rescue PrimitiveFailure
|
1196
|
+
raise SocketError, "file descriptor was not passed"
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
return fd unless klass
|
1200
|
+
|
1201
|
+
if klass < BasicSocket
|
1202
|
+
klass.for_fd(fd)
|
1203
|
+
else
|
1204
|
+
klass.for_fd(fd, mode)
|
1205
|
+
end
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
class << self
|
1209
|
+
def socketpair(type=Socket::SOCK_STREAM, protocol=0)
|
1210
|
+
Socket.socketpair(Socket::PF_UNIX, type, protocol, self)
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
alias_method :pair, :socketpair
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
class UNIXServer < UNIXSocket
|
1219
|
+
|
1220
|
+
include Socket::ListenAndAccept
|
1221
|
+
|
1222
|
+
def initialize(path)
|
1223
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1224
|
+
@path = path
|
1225
|
+
unix_setup(true)
|
1226
|
+
end
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
class IPSocket < BasicSocket
|
1230
|
+
|
1231
|
+
def self.getaddress(host)
|
1232
|
+
Socket::Foreign.getaddress host
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
def addr(reverse_lookup=nil)
|
1236
|
+
sockaddr = Socket::Foreign.getsockname descriptor
|
1237
|
+
|
1238
|
+
reverse_lookup = !do_not_reverse_lookup if reverse_lookup.nil?
|
1239
|
+
|
1240
|
+
family, port, host, ip = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
|
1241
|
+
[family, port.to_i, host, ip]
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
def peeraddr(reverse_lookup=nil)
|
1245
|
+
sockaddr = Socket::Foreign.getpeername descriptor
|
1246
|
+
|
1247
|
+
reverse_lookup = !do_not_reverse_lookup if reverse_lookup.nil?
|
1248
|
+
|
1249
|
+
family, port, host, ip = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
|
1250
|
+
[family, port.to_i, host, ip]
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
def recvfrom(maxlen, flags = 0)
|
1254
|
+
# FIXME 1 is hardcoded knowledge from io.cpp
|
1255
|
+
flags = 0 if flags.nil?
|
1256
|
+
socket_recv maxlen, flags, 1
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
def recvfrom_nonblock(maxlen, flags = 0)
|
1260
|
+
# Set socket to non-blocking, if we can
|
1261
|
+
# Todo: Ensure this works in Windows! If not, I claim that's Fcntl's fault.
|
1262
|
+
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
1263
|
+
flags = 0 if flags.nil?
|
1264
|
+
flags |= Socket::MSG_DONTWAIT
|
1265
|
+
|
1266
|
+
# Wait until we have something to read
|
1267
|
+
# @todo Why? ^^ --rue
|
1268
|
+
IO.select([self])
|
1269
|
+
return recvfrom(maxlen, flags)
|
1270
|
+
end
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
class UDPSocket < IPSocket
|
1274
|
+
FFI = Rubinius::FFI
|
1275
|
+
|
1276
|
+
def initialize(socktype = Socket::AF_INET)
|
1277
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1278
|
+
@socktype = socktype
|
1279
|
+
status = Socket::Foreign.socket @socktype,
|
1280
|
+
Socket::SOCK_DGRAM,
|
1281
|
+
Socket::IPPROTO_UDP
|
1282
|
+
Errno.handle 'socket(2)' if status < 0
|
1283
|
+
|
1284
|
+
IO.setup self, status, nil, true
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
def bind(host, port)
|
1288
|
+
@host = host.to_s if host
|
1289
|
+
@port = port.to_s if port
|
1290
|
+
|
1291
|
+
addrinfos = Socket::Foreign.getaddrinfo(@host,
|
1292
|
+
@port,
|
1293
|
+
@socktype,
|
1294
|
+
Socket::SOCK_DGRAM, 0,
|
1295
|
+
Socket::AI_PASSIVE)
|
1296
|
+
|
1297
|
+
status = -1
|
1298
|
+
|
1299
|
+
addrinfos.each do |addrinfo|
|
1300
|
+
flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
|
1301
|
+
|
1302
|
+
status = Socket::Foreign.bind descriptor, sockaddr
|
1303
|
+
|
1304
|
+
break if status >= 0
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
if status < 0
|
1308
|
+
Errno.handle 'bind(2)'
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
status
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
def connect(host, port)
|
1315
|
+
sockaddr = Socket::Foreign.pack_sockaddr_in host, port, @socktype, Socket::SOCK_DGRAM, 0
|
1316
|
+
|
1317
|
+
syscall = 'connect(2)'
|
1318
|
+
status = Socket::Foreign.connect descriptor, sockaddr
|
1319
|
+
|
1320
|
+
if status < 0
|
1321
|
+
Errno.handle syscall
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
0
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
def send(message, flags, *to)
|
1328
|
+
connect *to unless to.empty?
|
1329
|
+
|
1330
|
+
bytes = message.bytesize
|
1331
|
+
bytes_sent = 0
|
1332
|
+
|
1333
|
+
FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
|
1334
|
+
buffer.write_string message, bytes
|
1335
|
+
bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
|
1336
|
+
Errno.handle 'send(2)' if bytes_sent < 0
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
bytes_sent
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
def inspect
|
1343
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
class TCPSocket < IPSocket
|
1349
|
+
FFI = Rubinius::FFI
|
1350
|
+
|
1351
|
+
def self.gethostbyname(hostname)
|
1352
|
+
addrinfos = Socket.getaddrinfo(hostname, nil)
|
1353
|
+
|
1354
|
+
hostname = addrinfos.first[2]
|
1355
|
+
family = addrinfos.first[4]
|
1356
|
+
addresses = []
|
1357
|
+
alternatives = []
|
1358
|
+
addrinfos.each do |a|
|
1359
|
+
alternatives << a[2] unless a[2] == hostname
|
1360
|
+
addresses << a[3] if a[4] == family
|
1361
|
+
end
|
1362
|
+
|
1363
|
+
[hostname, alternatives.uniq, family] + addresses.uniq
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
#
|
1367
|
+
# @todo Is it correct to ignore the to? If not, does
|
1368
|
+
# the socket need to be reconnected? --rue
|
1369
|
+
#
|
1370
|
+
def send(bytes_to_read, flags, to = nil)
|
1371
|
+
super(bytes_to_read, flags)
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
|
1375
|
+
def initialize(host, port, local_host=nil, local_service=nil)
|
1376
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1377
|
+
@host = host
|
1378
|
+
@port = port
|
1379
|
+
|
1380
|
+
tcp_setup @host, @port, local_host, local_service
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
def tcp_setup(remote_host, remote_service, local_host = nil,
|
1384
|
+
local_service = nil, server = false)
|
1385
|
+
status = nil
|
1386
|
+
syscall = nil
|
1387
|
+
remote_host = StringValue(remote_host) if remote_host
|
1388
|
+
if remote_service
|
1389
|
+
if remote_service.kind_of? Fixnum
|
1390
|
+
remote_service = remote_service.to_s
|
1391
|
+
else
|
1392
|
+
remote_service = StringValue(remote_service)
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
flags = server ? Socket::AI_PASSIVE : 0
|
1397
|
+
@remote_addrinfo = Socket::Foreign.getaddrinfo(remote_host,
|
1398
|
+
remote_service,
|
1399
|
+
Socket::AF_UNSPEC,
|
1400
|
+
Socket::SOCK_STREAM, 0,
|
1401
|
+
flags)
|
1402
|
+
|
1403
|
+
if server == false and (local_host or local_service)
|
1404
|
+
local_host = local_host.to_s if local_host
|
1405
|
+
local_service = local_service.to_s if local_service
|
1406
|
+
@local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
|
1407
|
+
local_service,
|
1408
|
+
Socket::AF_UNSPEC,
|
1409
|
+
Socket::SOCK_STREAM, 0, 0)
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
sock = nil
|
1413
|
+
|
1414
|
+
@remote_addrinfo.each do |addrinfo|
|
1415
|
+
flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
|
1416
|
+
|
1417
|
+
sock = Socket::Foreign.socket family, socket_type, protocol
|
1418
|
+
syscall = 'socket(2)'
|
1419
|
+
|
1420
|
+
next if sock < 0
|
1421
|
+
|
1422
|
+
if server
|
1423
|
+
FFI::MemoryPointer.new :socklen_t do |val|
|
1424
|
+
val.write_int 1
|
1425
|
+
level = Socket::Constants::SOL_SOCKET
|
1426
|
+
optname = Socket::Constants::SO_REUSEADDR
|
1427
|
+
error = Socket::Foreign.setsockopt(sock, level,
|
1428
|
+
optname, val,
|
1429
|
+
val.total)
|
1430
|
+
# Don't check error because if this fails, we just continue
|
1431
|
+
# anyway.
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
status = Socket::Foreign.bind sock, sockaddr
|
1435
|
+
syscall = 'bind(2)'
|
1436
|
+
else
|
1437
|
+
if @local_addrinfo
|
1438
|
+
# Pick a local_addrinfo for the family and type of
|
1439
|
+
# the remote side
|
1440
|
+
li = @local_addrinfo.find do |i|
|
1441
|
+
i[1] == family && i[2] == socket_type
|
1442
|
+
end
|
1443
|
+
|
1444
|
+
if li
|
1445
|
+
status = Socket::Foreign.bind sock, li[4]
|
1446
|
+
syscall = 'bind(2)'
|
1447
|
+
else
|
1448
|
+
status = 1
|
1449
|
+
end
|
1450
|
+
else
|
1451
|
+
status = 1
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
if status >= 0
|
1455
|
+
status = Socket::Foreign.connect sock, sockaddr
|
1456
|
+
syscall = 'connect(2)'
|
1457
|
+
end
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
if status < 0
|
1461
|
+
Socket::Foreign.close sock
|
1462
|
+
else
|
1463
|
+
break
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
if status < 0
|
1468
|
+
Errno.handle syscall
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
if server
|
1472
|
+
err = Socket::Foreign.listen sock, 5
|
1473
|
+
unless err == 0
|
1474
|
+
Socket::Foreign.close sock
|
1475
|
+
Errno.handle syscall
|
1476
|
+
end
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
# Only setup once we have found a socket we can use. Otherwise
|
1480
|
+
# because we manually close a socket fd, we can create an IO fd
|
1481
|
+
# alias condition which causes EBADF because when an IO is finalized
|
1482
|
+
# and it's fd has been closed underneith it, we close someone elses
|
1483
|
+
# fd!
|
1484
|
+
IO.setup self, sock, nil, true
|
1485
|
+
end
|
1486
|
+
private :tcp_setup
|
1487
|
+
|
1488
|
+
def from_descriptor(descriptor)
|
1489
|
+
IO.setup self, descriptor, nil, true
|
1490
|
+
|
1491
|
+
self
|
1492
|
+
end
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
class TCPServer < TCPSocket
|
1496
|
+
|
1497
|
+
include Socket::ListenAndAccept
|
1498
|
+
|
1499
|
+
def initialize(host, port = nil)
|
1500
|
+
@no_reverse_lookup = self.class.do_not_reverse_lookup
|
1501
|
+
|
1502
|
+
if Fixnum === host and port.nil? then
|
1503
|
+
port = host
|
1504
|
+
host = nil
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
if String === host and port.nil? then
|
1508
|
+
port = Integer(host)
|
1509
|
+
host = nil
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
port = StringValue port unless port.kind_of? Fixnum
|
1513
|
+
|
1514
|
+
@host = host
|
1515
|
+
@port = port
|
1516
|
+
|
1517
|
+
tcp_setup @host, @port, nil, nil, true
|
1518
|
+
end
|
1519
|
+
|
1520
|
+
end
|