rubysl-socket 2.0.1 → 2.1.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.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -16
  3. data/lib/rubysl/socket.rb +201 -1396
  4. data/lib/rubysl/socket/ancillary_data.rb +56 -0
  5. data/lib/rubysl/socket/bsd.rb +28 -0
  6. data/lib/rubysl/socket/error.rb +67 -0
  7. data/lib/rubysl/socket/foreign.rb +324 -0
  8. data/lib/rubysl/socket/foreign/addrinfo.rb +10 -0
  9. data/lib/rubysl/socket/foreign/hostent.rb +36 -0
  10. data/lib/rubysl/socket/foreign/ifaddrs.rb +129 -0
  11. data/lib/rubysl/socket/foreign/iovec.rb +18 -0
  12. data/lib/rubysl/socket/foreign/linger.rb +41 -0
  13. data/lib/rubysl/socket/foreign/msghdr.rb +41 -0
  14. data/lib/rubysl/socket/foreign/servent.rb +17 -0
  15. data/lib/rubysl/socket/foreign/sockaddr.rb +21 -0
  16. data/lib/rubysl/socket/foreign/sockaddr_in.rb +25 -0
  17. data/lib/rubysl/socket/foreign/sockaddr_in6.rb +25 -0
  18. data/lib/rubysl/socket/foreign/sockaddr_un.rb +29 -0
  19. data/lib/rubysl/socket/ipv6.rb +38 -0
  20. data/lib/rubysl/socket/linux.rb +16 -0
  21. data/lib/rubysl/socket/socket_options.rb +84 -0
  22. data/lib/rubysl/socket/version.rb +1 -1
  23. data/lib/socket.rb +45 -1
  24. data/lib/socket/addrinfo.rb +453 -0
  25. data/lib/socket/ancillary_data.rb +114 -0
  26. data/lib/socket/basic_socket.rb +295 -0
  27. data/lib/socket/constants.rb +41 -0
  28. data/lib/socket/ifaddr.rb +29 -0
  29. data/lib/socket/ip_socket.rb +37 -0
  30. data/lib/socket/mri.rb +928 -0
  31. data/lib/socket/option.rb +96 -0
  32. data/lib/socket/socket.rb +353 -0
  33. data/lib/socket/socket_error.rb +2 -0
  34. data/lib/socket/tcp_server.rb +78 -0
  35. data/lib/socket/tcp_socket.rb +109 -0
  36. data/lib/socket/udp_socket.rb +73 -0
  37. data/lib/socket/unix_server.rb +35 -0
  38. data/lib/socket/unix_socket.rb +78 -0
  39. data/rubysl-socket.gemspec +15 -10
  40. metadata +78 -293
  41. data/.gitignore +0 -17
  42. data/.travis.yml +0 -9
  43. data/Gemfile +0 -4
  44. data/Rakefile +0 -1
  45. data/spec/addrinfo/afamily_spec.rb +0 -5
  46. data/spec/addrinfo/bind_spec.rb +0 -5
  47. data/spec/addrinfo/canonname_spec.rb +0 -5
  48. data/spec/addrinfo/connect_from_spec.rb +0 -5
  49. data/spec/addrinfo/connect_spec.rb +0 -5
  50. data/spec/addrinfo/connect_to_spec.rb +0 -5
  51. data/spec/addrinfo/family_addrinfo_spec.rb +0 -5
  52. data/spec/addrinfo/foreach_spec.rb +0 -5
  53. data/spec/addrinfo/getaddrinfo_spec.rb +0 -5
  54. data/spec/addrinfo/getnameinfo_spec.rb +0 -5
  55. data/spec/addrinfo/inspect_sockaddr_spec.rb +0 -5
  56. data/spec/addrinfo/inspect_spec.rb +0 -5
  57. data/spec/addrinfo/ip_address_spec.rb +0 -5
  58. data/spec/addrinfo/ip_port_spec.rb +0 -5
  59. data/spec/addrinfo/ip_spec.rb +0 -9
  60. data/spec/addrinfo/ip_unpack_spec.rb +0 -5
  61. data/spec/addrinfo/ipv4_loopback_spec.rb +0 -5
  62. data/spec/addrinfo/ipv4_multicast_spec.rb +0 -5
  63. data/spec/addrinfo/ipv4_private_spec.rb +0 -5
  64. data/spec/addrinfo/ipv4_spec.rb +0 -5
  65. data/spec/addrinfo/ipv6_linklocal_spec.rb +0 -5
  66. data/spec/addrinfo/ipv6_loopback_spec.rb +0 -5
  67. data/spec/addrinfo/ipv6_mc_global_spec.rb +0 -5
  68. data/spec/addrinfo/ipv6_mc_linklocal_spec.rb +0 -5
  69. data/spec/addrinfo/ipv6_mc_nodelocal_spec.rb +0 -5
  70. data/spec/addrinfo/ipv6_mc_orglocal_spec.rb +0 -5
  71. data/spec/addrinfo/ipv6_mc_sitelocal_spec.rb +0 -5
  72. data/spec/addrinfo/ipv6_multicast_spec.rb +0 -5
  73. data/spec/addrinfo/ipv6_sitelocal_spec.rb +0 -5
  74. data/spec/addrinfo/ipv6_spec.rb +0 -5
  75. data/spec/addrinfo/ipv6_to_ipv4_spec.rb +0 -5
  76. data/spec/addrinfo/ipv6_unspecified_spec.rb +0 -5
  77. data/spec/addrinfo/ipv6_v4compat_spec.rb +0 -5
  78. data/spec/addrinfo/ipv6_v4mapped_spec.rb +0 -5
  79. data/spec/addrinfo/listen_spec.rb +0 -5
  80. data/spec/addrinfo/marshal_dump_spec.rb +0 -5
  81. data/spec/addrinfo/marshal_load_spec.rb +0 -5
  82. data/spec/addrinfo/pfamily_spec.rb +0 -5
  83. data/spec/addrinfo/protocol_spec.rb +0 -5
  84. data/spec/addrinfo/socktype_spec.rb +0 -5
  85. data/spec/addrinfo/tcp_spec.rb +0 -5
  86. data/spec/addrinfo/to_s_spec.rb +0 -5
  87. data/spec/addrinfo/to_sockaddr_spec.rb +0 -5
  88. data/spec/addrinfo/udp_spec.rb +0 -5
  89. data/spec/addrinfo/unix_path_spec.rb +0 -5
  90. data/spec/addrinfo/unix_spec.rb +0 -9
  91. data/spec/basicsocket/close_read_spec.rb +0 -42
  92. data/spec/basicsocket/close_write_spec.rb +0 -42
  93. data/spec/basicsocket/do_not_reverse_lookup_spec.rb +0 -78
  94. data/spec/basicsocket/for_fd_spec.rb +0 -37
  95. data/spec/basicsocket/getpeername_spec.rb +0 -24
  96. data/spec/basicsocket/getsockname_spec.rb +0 -27
  97. data/spec/basicsocket/getsockopt_spec.rb +0 -54
  98. data/spec/basicsocket/ioctl_spec.rb +0 -22
  99. data/spec/basicsocket/recv_nonblock_spec.rb +0 -6
  100. data/spec/basicsocket/recv_spec.rb +0 -76
  101. data/spec/basicsocket/send_spec.rb +0 -81
  102. data/spec/basicsocket/setsockopt_spec.rb +0 -333
  103. data/spec/basicsocket/shutdown_spec.rb +0 -5
  104. data/spec/constants/constants_spec.rb +0 -63
  105. data/spec/fixtures/classes.rb +0 -174
  106. data/spec/fixtures/send_io.txt +0 -1
  107. data/spec/ipsocket/addr_spec.rb +0 -72
  108. data/spec/ipsocket/getaddress_spec.rb +0 -26
  109. data/spec/ipsocket/peeraddr_spec.rb +0 -79
  110. data/spec/ipsocket/recvfrom_spec.rb +0 -64
  111. data/spec/option/int_spec.rb +0 -27
  112. data/spec/option/linger_spec.rb +0 -52
  113. data/spec/option/new_spec.rb +0 -32
  114. data/spec/shared/pack_sockaddr.rb +0 -26
  115. data/spec/shared/partially_closable_sockets.rb +0 -13
  116. data/spec/shared/recv_nonblock.rb +0 -33
  117. data/spec/shared/socketpair.rb +0 -35
  118. data/spec/socket/accept_nonblock_spec.rb +0 -27
  119. data/spec/socket/accept_spec.rb +0 -1
  120. data/spec/socket/bind_spec.rb +0 -80
  121. data/spec/socket/connect_nonblock_spec.rb +0 -62
  122. data/spec/socket/connect_spec.rb +0 -1
  123. data/spec/socket/for_fd_spec.rb +0 -29
  124. data/spec/socket/getaddrinfo_spec.rb +0 -120
  125. data/spec/socket/gethostbyaddr_spec.rb +0 -1
  126. data/spec/socket/gethostbyname_spec.rb +0 -26
  127. data/spec/socket/gethostname_spec.rb +0 -9
  128. data/spec/socket/getnameinfo_spec.rb +0 -57
  129. data/spec/socket/getservbyname_spec.rb +0 -24
  130. data/spec/socket/listen_spec.rb +0 -21
  131. data/spec/socket/new_spec.rb +0 -109
  132. data/spec/socket/pack_sockaddr_in_spec.rb +0 -6
  133. data/spec/socket/pack_sockaddr_un_spec.rb +0 -6
  134. data/spec/socket/pair_spec.rb +0 -6
  135. data/spec/socket/recvfrom_nonblock_spec.rb +0 -1
  136. data/spec/socket/recvfrom_spec.rb +0 -1
  137. data/spec/socket/sockaddr_in_spec.rb +0 -6
  138. data/spec/socket/sockaddr_un_spec.rb +0 -6
  139. data/spec/socket/socket_spec.rb +0 -37
  140. data/spec/socket/socketpair_spec.rb +0 -6
  141. data/spec/socket/sysaccept_spec.rb +0 -1
  142. data/spec/socket/unpack_sockaddr_in_spec.rb +0 -16
  143. data/spec/socket/unpack_sockaddr_un_spec.rb +0 -2
  144. data/spec/tcpserver/accept_nonblock_spec.rb +0 -30
  145. data/spec/tcpserver/accept_spec.rb +0 -60
  146. data/spec/tcpserver/gets_spec.rb +0 -17
  147. data/spec/tcpserver/listen_spec.rb +0 -1
  148. data/spec/tcpserver/new_spec.rb +0 -88
  149. data/spec/tcpserver/output_spec.rb +0 -8
  150. data/spec/tcpserver/readpartial_spec.rb +0 -8
  151. data/spec/tcpserver/sysaccept_spec.rb +0 -1
  152. data/spec/tcpsocket/gethostbyname_spec.rb +0 -62
  153. data/spec/tcpsocket/new_spec.rb +0 -5
  154. data/spec/tcpsocket/open_spec.rb +0 -5
  155. data/spec/tcpsocket/partially_closable_spec.rb +0 -20
  156. data/spec/tcpsocket/recv_nonblock_spec.rb +0 -31
  157. data/spec/tcpsocket/setsockopt_spec.rb +0 -49
  158. data/spec/tcpsocket/shared/new.rb +0 -85
  159. data/spec/udpsocket/bind_spec.rb +0 -33
  160. data/spec/udpsocket/connect_spec.rb +0 -1
  161. data/spec/udpsocket/new_spec.rb +0 -1
  162. data/spec/udpsocket/open_spec.rb +0 -12
  163. data/spec/udpsocket/recvfrom_nonblock_spec.rb +0 -1
  164. data/spec/udpsocket/send_spec.rb +0 -57
  165. data/spec/unixserver/accept_nonblock_spec.rb +0 -33
  166. data/spec/unixserver/accept_spec.rb +0 -64
  167. data/spec/unixserver/for_fd_spec.rb +0 -32
  168. data/spec/unixserver/new_spec.rb +0 -5
  169. data/spec/unixserver/open_spec.rb +0 -25
  170. data/spec/unixserver/shared/new.rb +0 -23
  171. data/spec/unixsocket/addr_spec.rb +0 -37
  172. data/spec/unixsocket/new_spec.rb +0 -5
  173. data/spec/unixsocket/open_spec.rb +0 -26
  174. data/spec/unixsocket/pair_spec.rb +0 -38
  175. data/spec/unixsocket/partially_closable_spec.rb +0 -25
  176. data/spec/unixsocket/path_spec.rb +0 -29
  177. data/spec/unixsocket/peeraddr_spec.rb +0 -29
  178. data/spec/unixsocket/recv_io_spec.rb +0 -40
  179. data/spec/unixsocket/recvfrom_spec.rb +0 -48
  180. data/spec/unixsocket/send_io_spec.rb +0 -30
  181. data/spec/unixsocket/shared/new.rb +0 -25
@@ -0,0 +1,29 @@
1
+ class Socket < BasicSocket
2
+ class Ifaddr < Data
3
+ attr_reader :addr, :broadaddr, :dstaddr, :flags, :ifindex, :name, :netmask
4
+
5
+ def initialize(addr: nil, broadaddr: nil, dstaddr: nil, flags: nil, ifindex: nil, name: nil, netmask: nil)
6
+ @addr = addr
7
+ @broadaddr = broadaddr
8
+ @dstaddr = dstaddr
9
+ @flags = flags
10
+ @ifindex = ifindex
11
+ @name = name
12
+ @netmask = netmask
13
+ end
14
+
15
+ def inspect
16
+ out = "#<Socket::Ifaddr #{name}"
17
+
18
+ if addr
19
+ out << " #{addr.inspect_sockaddr}"
20
+ end
21
+
22
+ if netmask
23
+ out << " netmask=#{netmask.inspect_sockaddr}"
24
+ end
25
+
26
+ out + '>'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ class IPSocket < BasicSocket
2
+ undef_method :getpeereid
3
+
4
+ def self.getaddress(host)
5
+ RubySL::Socket::Foreign.getaddress(host)
6
+ end
7
+
8
+ def addr(reverse_lookup = nil)
9
+ RubySL::Socket.address_info(:getsockname, self, reverse_lookup)
10
+ end
11
+
12
+ def peeraddr(reverse_lookup=nil)
13
+ RubySL::Socket.address_info(:getpeername, self, reverse_lookup)
14
+ end
15
+
16
+ def recvfrom(maxlen, flags = 0)
17
+ flags = 0 if flags.nil?
18
+
19
+ message, addr = recvmsg(maxlen, flags)
20
+
21
+ aname = RubySL::Socket.address_family_name(addr.afamily)
22
+ hostname = addr.ip_address
23
+
24
+ # We're re-using recvmsg which doesn't return the reverse hostname, thus
25
+ # we'll do an extra lookup in case this is needed.
26
+ unless do_not_reverse_lookup
27
+ addrinfos = Socket.getaddrinfo(addr.ip_address, nil, addr.afamily,
28
+ addr.socktype, addr.protocol, 0, true)
29
+
30
+ unless addrinfos.empty?
31
+ hostname = addrinfos[0][2]
32
+ end
33
+ end
34
+
35
+ return message, [aname, addr.ip_port, hostname, addr.ip_address]
36
+ end
37
+ end
@@ -0,0 +1,928 @@
1
+ # Everything below is copied from the MRI codebase. The Ruby license is
2
+ # attached below.
3
+ #
4
+ # Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
5
+ # You can redistribute it and/or modify it under either the terms of the
6
+ # 2-clause BSDL (see the file BSDL), or the conditions below:
7
+ #
8
+ # 1. You may make and give away verbatim copies of the source form of the
9
+ # software without restriction, provided that you duplicate all of the
10
+ # original copyright notices and associated disclaimers.
11
+ #
12
+ # 2. You may modify your copy of the software in any way, provided that
13
+ # you do at least ONE of the following:
14
+ #
15
+ # a) place your modifications in the Public Domain or otherwise
16
+ # make them Freely Available, such as by posting said
17
+ # modifications to Usenet or an equivalent medium, or by allowing
18
+ # the author to include your modifications in the software.
19
+ #
20
+ # b) use the modified software only within your corporation or
21
+ # organization.
22
+ #
23
+ # c) give non-standard binaries non-standard names, with
24
+ # instructions on where to get the original software distribution.
25
+ #
26
+ # d) make other distribution arrangements with the author.
27
+ #
28
+ # 3. You may distribute the software in object code or binary form,
29
+ # provided that you do at least ONE of the following:
30
+ #
31
+ # a) distribute the binaries and library files of the software,
32
+ # together with instructions (in the manual page or equivalent)
33
+ # on where to get the original distribution.
34
+ #
35
+ # b) accompany the distribution with the machine-readable source of
36
+ # the software.
37
+ #
38
+ # c) give non-standard binaries non-standard names, with
39
+ # instructions on where to get the original software distribution.
40
+ #
41
+ # d) make other distribution arrangements with the author.
42
+ #
43
+ # 4. You may modify and include the part of the software into any other
44
+ # software (possibly commercial). But some files in the distribution
45
+ # are not written by the author, so that they are not under these terms.
46
+ #
47
+ # For the list of those files and their copying conditions, see the
48
+ # file LEGAL.
49
+ #
50
+ # 5. The scripts and library files supplied as input to or produced as
51
+ # output from the software do not automatically fall under the
52
+ # copyright of the software, but belong to whomever generated them,
53
+ # and may be sold commercially, and may be aggregated with this
54
+ # software.
55
+ #
56
+ # 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
57
+ # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59
+ # PURPOSE.
60
+
61
+ class Addrinfo
62
+ # creates an Addrinfo object from the arguments.
63
+ #
64
+ # The arguments are interpreted as similar to self.
65
+ #
66
+ # Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80)
67
+ # #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)>
68
+ #
69
+ # Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2")
70
+ # #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM>
71
+ #
72
+ def family_addrinfo(*args)
73
+ if args.empty?
74
+ raise ArgumentError, "no address specified"
75
+ elsif Addrinfo === args.first
76
+ raise ArgumentError, "too many arguments" if args.length != 1
77
+ addrinfo = args.first
78
+ if (self.pfamily != addrinfo.pfamily) ||
79
+ (self.socktype != addrinfo.socktype)
80
+ raise ArgumentError, "Addrinfo type mismatch"
81
+ end
82
+ addrinfo
83
+ elsif self.ip?
84
+ raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2
85
+ host, port = args
86
+ Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0]
87
+ elsif self.unix?
88
+ raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1
89
+ path, = args
90
+ Addrinfo.unix(path)
91
+ else
92
+ raise ArgumentError, "unexpected family"
93
+ end
94
+ end
95
+
96
+ # creates a new Socket connected to the address of +local_addrinfo+.
97
+ #
98
+ # If _local_addrinfo_ is nil, the address of the socket is not bound.
99
+ #
100
+ # The _timeout_ specify the seconds for timeout.
101
+ # Errno::ETIMEDOUT is raised when timeout occur.
102
+ #
103
+ # If a block is given the created socket is yielded for each address.
104
+ #
105
+ def connect_internal(local_addrinfo, timeout=nil) # :yields: socket
106
+ sock = Socket.new(self.pfamily, self.socktype, self.protocol)
107
+ begin
108
+ sock.ipv6only! if self.ipv6?
109
+ sock.bind local_addrinfo if local_addrinfo
110
+ if timeout
111
+ begin
112
+ sock.connect_nonblock(self)
113
+ rescue IO::WaitWritable
114
+ if !IO.select(nil, [sock], nil, timeout)
115
+ raise Errno::ETIMEDOUT, 'user specified timeout'
116
+ end
117
+ begin
118
+ sock.connect_nonblock(self) # check connection failure
119
+ rescue Errno::EISCONN
120
+ end
121
+ end
122
+ else
123
+ sock.connect(self)
124
+ end
125
+ rescue Exception
126
+ sock.close
127
+ raise
128
+ end
129
+ if block_given?
130
+ begin
131
+ yield sock
132
+ ensure
133
+ sock.close if !sock.closed?
134
+ end
135
+ else
136
+ sock
137
+ end
138
+ end
139
+ private :connect_internal
140
+
141
+ # :call-seq:
142
+ # addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... }
143
+ # addrinfo.connect_from([local_addr_args], [opts])
144
+ #
145
+ # creates a socket connected to the address of self.
146
+ #
147
+ # If one or more arguments given as _local_addr_args_,
148
+ # it is used as the local address of the socket.
149
+ # _local_addr_args_ is given for family_addrinfo to obtain actual address.
150
+ #
151
+ # If _local_addr_args_ is not given, the local address of the socket is not bound.
152
+ #
153
+ # The optional last argument _opts_ is options represented by a hash.
154
+ # _opts_ may have following options:
155
+ #
156
+ # [:timeout] specify the timeout in seconds.
157
+ #
158
+ # If a block is given, it is called with the socket and the value of the block is returned.
159
+ # The socket is returned otherwise.
160
+ #
161
+ # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s|
162
+ # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
163
+ # puts s.read
164
+ # }
165
+ #
166
+ # # Addrinfo object can be taken for the argument.
167
+ # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s|
168
+ # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
169
+ # puts s.read
170
+ # }
171
+ #
172
+ def connect_from(*args, &block)
173
+ opts = Hash === args.last ? args.pop : {}
174
+ local_addr_args = args
175
+ connect_internal(family_addrinfo(*local_addr_args), opts[:timeout], &block)
176
+ end
177
+
178
+ # :call-seq:
179
+ # addrinfo.connect([opts]) {|socket| ... }
180
+ # addrinfo.connect([opts])
181
+ #
182
+ # creates a socket connected to the address of self.
183
+ #
184
+ # The optional argument _opts_ is options represented by a hash.
185
+ # _opts_ may have following options:
186
+ #
187
+ # [:timeout] specify the timeout in seconds.
188
+ #
189
+ # If a block is given, it is called with the socket and the value of the block is returned.
190
+ # The socket is returned otherwise.
191
+ #
192
+ # Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s|
193
+ # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
194
+ # puts s.read
195
+ # }
196
+ #
197
+ def connect(opts={}, &block)
198
+ connect_internal(nil, opts[:timeout], &block)
199
+ end
200
+
201
+ # :call-seq:
202
+ # addrinfo.connect_to([remote_addr_args], [opts]) {|socket| ... }
203
+ # addrinfo.connect_to([remote_addr_args], [opts])
204
+ #
205
+ # creates a socket connected to _remote_addr_args_ and bound to self.
206
+ #
207
+ # The optional last argument _opts_ is options represented by a hash.
208
+ # _opts_ may have following options:
209
+ #
210
+ # [:timeout] specify the timeout in seconds.
211
+ #
212
+ # If a block is given, it is called with the socket and the value of the block is returned.
213
+ # The socket is returned otherwise.
214
+ #
215
+ # Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s|
216
+ # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
217
+ # puts s.read
218
+ # }
219
+ #
220
+ def connect_to(*args, &block)
221
+ opts = Hash === args.last ? args.pop : {}
222
+ remote_addr_args = args
223
+ remote_addrinfo = family_addrinfo(*remote_addr_args)
224
+ remote_addrinfo.send(:connect_internal, self, opts[:timeout], &block)
225
+ end
226
+
227
+ # creates a socket bound to self.
228
+ #
229
+ # If a block is given, it is called with the socket and the value of the block is returned.
230
+ # The socket is returned otherwise.
231
+ #
232
+ # Addrinfo.udp("0.0.0.0", 9981).bind {|s|
233
+ # s.local_address.connect {|s| s.send "hello", 0 }
234
+ # p s.recv(10) #=> "hello"
235
+ # }
236
+ #
237
+ def bind
238
+ sock = Socket.new(self.pfamily, self.socktype, self.protocol)
239
+ begin
240
+ sock.ipv6only! if self.ipv6?
241
+ sock.setsockopt(:SOCKET, :REUSEADDR, 1)
242
+ sock.bind(self)
243
+ rescue Exception
244
+ sock.close
245
+ raise
246
+ end
247
+ if block_given?
248
+ begin
249
+ yield sock
250
+ ensure
251
+ sock.close if !sock.closed?
252
+ end
253
+ else
254
+ sock
255
+ end
256
+ end
257
+
258
+ # creates a listening socket bound to self.
259
+ def listen(backlog=Socket::SOMAXCONN)
260
+ sock = Socket.new(self.pfamily, self.socktype, self.protocol)
261
+ begin
262
+ sock.ipv6only! if self.ipv6?
263
+ sock.setsockopt(:SOCKET, :REUSEADDR, 1)
264
+ sock.bind(self)
265
+ sock.listen(backlog)
266
+ rescue Exception
267
+ sock.close
268
+ raise
269
+ end
270
+ if block_given?
271
+ begin
272
+ yield sock
273
+ ensure
274
+ sock.close if !sock.closed?
275
+ end
276
+ else
277
+ sock
278
+ end
279
+ end
280
+
281
+ # iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
282
+ #
283
+ # Addrinfo.foreach(nil, 80) {|x| p x }
284
+ # #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)>
285
+ # # #<Addrinfo: 127.0.0.1:80 UDP (:80)>
286
+ # # #<Addrinfo: [::1]:80 TCP (:80)>
287
+ # # #<Addrinfo: [::1]:80 UDP (:80)>
288
+ #
289
+ def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
290
+ Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
291
+ end
292
+ end
293
+
294
+ class BasicSocket < IO
295
+ # Returns an address of the socket suitable for connect in the local machine.
296
+ #
297
+ # This method returns _self_.local_address, except following condition.
298
+ #
299
+ # - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).
300
+ # - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).
301
+ #
302
+ # If the local address is not suitable for connect, SocketError is raised.
303
+ # IPv4 and IPv6 address which port is 0 is not suitable for connect.
304
+ # Unix domain socket which has no path is not suitable for connect.
305
+ #
306
+ # Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
307
+ # p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
308
+ # serv.connect_address.connect {|c|
309
+ # s, _ = serv.accept
310
+ # p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
311
+ # }
312
+ # }
313
+ #
314
+ def connect_address
315
+ addr = local_address
316
+ afamily = addr.afamily
317
+ if afamily == Socket::AF_INET
318
+ raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
319
+ if addr.ip_address == "0.0.0.0"
320
+ addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
321
+ end
322
+ elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
323
+ raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
324
+ if addr.ip_address == "::"
325
+ addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
326
+ elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
327
+ addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
328
+ elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address.
329
+ addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
330
+ end
331
+ elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
332
+ raise SocketError, "unbound Unix socket" if addr.unix_path == ""
333
+ end
334
+ addr
335
+ end
336
+ end
337
+
338
+ class Socket < BasicSocket
339
+ # enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
340
+ def ipv6only!
341
+ if defined? Socket::IPV6_V6ONLY
342
+ self.setsockopt(:IPV6, :V6ONLY, 1)
343
+ end
344
+ end
345
+
346
+ # :call-seq:
347
+ # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
348
+ # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
349
+ #
350
+ # creates a new socket object connected to host:port using TCP/IP.
351
+ #
352
+ # If local_host:local_port is given,
353
+ # the socket is bound to it.
354
+ #
355
+ # The optional last argument _opts_ is options represented by a hash.
356
+ # _opts_ may have following options:
357
+ #
358
+ # [:connect_timeout] specify the timeout in seconds.
359
+ #
360
+ # If a block is given, the block is called with the socket.
361
+ # The value of the block is returned.
362
+ # The socket is closed when this method returns.
363
+ #
364
+ # If no block is given, the socket is returned.
365
+ #
366
+ # Socket.tcp("www.ruby-lang.org", 80) {|sock|
367
+ # sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
368
+ # sock.close_write
369
+ # puts sock.read
370
+ # }
371
+ #
372
+ def self.tcp(host, port, *rest) # :yield: socket
373
+ opts = Hash === rest.last ? rest.pop : {}
374
+ raise ArgumentError, "wrong number of arguments (#{rest.length} for 2)" if 2 < rest.length
375
+ local_host, local_port = rest
376
+ last_error = nil
377
+ ret = nil
378
+
379
+ connect_timeout = opts[:connect_timeout]
380
+
381
+ local_addr_list = nil
382
+ if local_host != nil || local_port != nil
383
+ local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
384
+ end
385
+
386
+ Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
387
+ if local_addr_list
388
+ local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
389
+ next if !local_addr
390
+ else
391
+ local_addr = nil
392
+ end
393
+ begin
394
+ sock = local_addr ?
395
+ ai.connect_from(local_addr, :timeout => connect_timeout) :
396
+ ai.connect(:timeout => connect_timeout)
397
+ rescue SystemCallError
398
+ last_error = $!
399
+ next
400
+ end
401
+ ret = sock
402
+ break
403
+ }
404
+ if !ret
405
+ if last_error
406
+ raise last_error
407
+ else
408
+ raise SocketError, "no appropriate local address"
409
+ end
410
+ end
411
+ if block_given?
412
+ begin
413
+ yield ret
414
+ ensure
415
+ ret.close if !ret.closed?
416
+ end
417
+ else
418
+ ret
419
+ end
420
+ end
421
+
422
+ # :stopdoc:
423
+ def self.ip_sockets_port0(ai_list, reuseaddr)
424
+ sockets = []
425
+ begin
426
+ sockets.clear
427
+ port = nil
428
+ ai_list.each {|ai|
429
+ begin
430
+ s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
431
+ rescue SystemCallError
432
+ next
433
+ end
434
+ sockets << s
435
+ s.ipv6only! if ai.ipv6?
436
+ if reuseaddr
437
+ s.setsockopt(:SOCKET, :REUSEADDR, 1)
438
+ end
439
+ if !port
440
+ s.bind(ai)
441
+ port = s.local_address.ip_port
442
+ else
443
+ s.bind(ai.family_addrinfo(ai.ip_address, port))
444
+ end
445
+ }
446
+ rescue Errno::EADDRINUSE
447
+ sockets.each {|s| s.close }
448
+ retry
449
+ rescue Exception
450
+ sockets.each {|s| s.close }
451
+ raise
452
+ end
453
+ sockets
454
+ end
455
+ class << self
456
+ private :ip_sockets_port0
457
+ end
458
+
459
+ def self.tcp_server_sockets_port0(host)
460
+ ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
461
+ sockets = ip_sockets_port0(ai_list, true)
462
+ begin
463
+ sockets.each {|s|
464
+ s.listen(Socket::SOMAXCONN)
465
+ }
466
+ rescue Exception
467
+ sockets.each {|s| s.close }
468
+ raise
469
+ end
470
+ sockets
471
+ end
472
+ class << self
473
+ private :tcp_server_sockets_port0
474
+ end
475
+ # :startdoc:
476
+
477
+ # creates TCP/IP server sockets for _host_ and _port_.
478
+ # _host_ is optional.
479
+ #
480
+ # If no block given,
481
+ # it returns an array of listening sockets.
482
+ #
483
+ # If a block is given, the block is called with the sockets.
484
+ # The value of the block is returned.
485
+ # The socket is closed when this method returns.
486
+ #
487
+ # If _port_ is 0, actual port number is chosen dynamically.
488
+ # However all sockets in the result has same port number.
489
+ #
490
+ # # tcp_server_sockets returns two sockets.
491
+ # sockets = Socket.tcp_server_sockets(1296)
492
+ # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
493
+ #
494
+ # # The sockets contains IPv6 and IPv4 sockets.
495
+ # sockets.each {|s| p s.local_address }
496
+ # #=> #<Addrinfo: [::]:1296 TCP>
497
+ # # #<Addrinfo: 0.0.0.0:1296 TCP>
498
+ #
499
+ # # IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
500
+ # sockets = Socket.tcp_server_sockets(0)
501
+ # sockets.each {|s| p s.local_address }
502
+ # #=> #<Addrinfo: [::]:53114 TCP>
503
+ # # #<Addrinfo: 0.0.0.0:53114 TCP>
504
+ #
505
+ # # The block is called with the sockets.
506
+ # Socket.tcp_server_sockets(0) {|sockets|
507
+ # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
508
+ # }
509
+ #
510
+ def self.tcp_server_sockets(host=nil, port)
511
+ if port == 0
512
+ sockets = tcp_server_sockets_port0(host)
513
+ else
514
+ last_error = nil
515
+ sockets = []
516
+ begin
517
+ Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
518
+ begin
519
+ s = ai.listen
520
+ rescue SystemCallError
521
+ last_error = $!
522
+ next
523
+ end
524
+ sockets << s
525
+ }
526
+ if sockets.empty?
527
+ raise last_error
528
+ end
529
+ rescue Exception
530
+ sockets.each {|s| s.close }
531
+ raise
532
+ end
533
+ end
534
+ if block_given?
535
+ begin
536
+ yield sockets
537
+ ensure
538
+ sockets.each {|s| s.close if !s.closed? }
539
+ end
540
+ else
541
+ sockets
542
+ end
543
+ end
544
+
545
+ # yield socket and client address for each a connection accepted via given sockets.
546
+ #
547
+ # The arguments are a list of sockets.
548
+ # The individual argument should be a socket or an array of sockets.
549
+ #
550
+ # This method yields the block sequentially.
551
+ # It means that the next connection is not accepted until the block returns.
552
+ # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
553
+ #
554
+ def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
555
+ sockets.flatten!(1)
556
+ if sockets.empty?
557
+ raise ArgumentError, "no sockets"
558
+ end
559
+ loop {
560
+ readable, _, _ = IO.select(sockets)
561
+ readable.each {|r|
562
+ begin
563
+ sock, addr = r.accept_nonblock
564
+ rescue IO::WaitReadable
565
+ next
566
+ end
567
+ yield sock, addr
568
+ }
569
+ }
570
+ end
571
+
572
+ # creates a TCP/IP server on _port_ and calls the block for each connection accepted.
573
+ # The block is called with a socket and a client_address as an Addrinfo object.
574
+ #
575
+ # If _host_ is specified, it is used with _port_ to determine the server addresses.
576
+ #
577
+ # The socket is *not* closed when the block returns.
578
+ # So application should close it explicitly.
579
+ #
580
+ # This method calls the block sequentially.
581
+ # It means that the next connection is not accepted until the block returns.
582
+ # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
583
+ #
584
+ # Note that Addrinfo.getaddrinfo is used to determine the server socket addresses.
585
+ # When Addrinfo.getaddrinfo returns two or more addresses,
586
+ # IPv4 and IPv6 address for example,
587
+ # all of them are used.
588
+ # Socket.tcp_server_loop succeeds if one socket can be used at least.
589
+ #
590
+ # # Sequential echo server.
591
+ # # It services only one client at a time.
592
+ # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
593
+ # begin
594
+ # IO.copy_stream(sock, sock)
595
+ # ensure
596
+ # sock.close
597
+ # end
598
+ # }
599
+ #
600
+ # # Threaded echo server
601
+ # # It services multiple clients at a time.
602
+ # # Note that it may accept connections too much.
603
+ # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
604
+ # Thread.new {
605
+ # begin
606
+ # IO.copy_stream(sock, sock)
607
+ # ensure
608
+ # sock.close
609
+ # end
610
+ # }
611
+ # }
612
+ #
613
+ def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
614
+ tcp_server_sockets(host, port) {|sockets|
615
+ accept_loop(sockets, &b)
616
+ }
617
+ end
618
+
619
+ # :call-seq:
620
+ # Socket.udp_server_sockets([host, ] port)
621
+ #
622
+ # Creates UDP/IP sockets for a UDP server.
623
+ #
624
+ # If no block given, it returns an array of sockets.
625
+ #
626
+ # If a block is given, the block is called with the sockets.
627
+ # The value of the block is returned.
628
+ # The sockets are closed when this method returns.
629
+ #
630
+ # If _port_ is zero, some port is chosen.
631
+ # But the chosen port is used for the all sockets.
632
+ #
633
+ # # UDP/IP echo server
634
+ # Socket.udp_server_sockets(0) {|sockets|
635
+ # p sockets.first.local_address.ip_port #=> 32963
636
+ # Socket.udp_server_loop_on(sockets) {|msg, msg_src|
637
+ # msg_src.reply msg
638
+ # }
639
+ # }
640
+ #
641
+ def self.udp_server_sockets(host=nil, port)
642
+ last_error = nil
643
+ sockets = []
644
+
645
+ ipv6_recvpktinfo = nil
646
+ if defined? Socket::AncillaryData
647
+ if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
648
+ ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
649
+ elsif defined? Socket::IPV6_PKTINFO # RFC 2292
650
+ ipv6_recvpktinfo = Socket::IPV6_PKTINFO
651
+ end
652
+ end
653
+
654
+ local_addrs = Socket.ip_address_list
655
+
656
+ ip_list = []
657
+ Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
658
+ if ai.ipv4? && ai.ip_address == "0.0.0.0"
659
+ local_addrs.each {|a|
660
+ next if !a.ipv4?
661
+ ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
662
+ }
663
+ elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
664
+ local_addrs.each {|a|
665
+ next if !a.ipv6?
666
+ ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
667
+ }
668
+ else
669
+ ip_list << ai
670
+ end
671
+ }
672
+
673
+ if port == 0
674
+ sockets = ip_sockets_port0(ip_list, false)
675
+ else
676
+ ip_list.each {|ip|
677
+ ai = Addrinfo.udp(ip.ip_address, port)
678
+ begin
679
+ s = ai.bind
680
+ rescue SystemCallError
681
+ last_error = $!
682
+ next
683
+ end
684
+ sockets << s
685
+ }
686
+ if sockets.empty?
687
+ raise last_error
688
+ end
689
+ end
690
+
691
+ sockets.each {|s|
692
+ ai = s.local_address
693
+ if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
694
+ s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
695
+ end
696
+ }
697
+
698
+ if block_given?
699
+ begin
700
+ yield sockets
701
+ ensure
702
+ sockets.each {|s| s.close if !s.closed? } if sockets
703
+ end
704
+ else
705
+ sockets
706
+ end
707
+ end
708
+
709
+ # :call-seq:
710
+ # Socket.udp_server_recv(sockets) {|msg, msg_src| ... }
711
+ #
712
+ # Receive UDP/IP packets from the given _sockets_.
713
+ # For each packet received, the block is called.
714
+ #
715
+ # The block receives _msg_ and _msg_src_.
716
+ # _msg_ is a string which is the payload of the received packet.
717
+ # _msg_src_ is a Socket::UDPSource object which is used for reply.
718
+ #
719
+ # Socket.udp_server_loop can be implemented using this method as follows.
720
+ #
721
+ # udp_server_sockets(host, port) {|sockets|
722
+ # loop {
723
+ # readable, _, _ = IO.select(sockets)
724
+ # udp_server_recv(readable) {|msg, msg_src| ... }
725
+ # }
726
+ # }
727
+ #
728
+ def self.udp_server_recv(sockets)
729
+ sockets.each {|r|
730
+ begin
731
+ msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock
732
+ rescue IO::WaitReadable
733
+ next
734
+ end
735
+ ai = r.local_address
736
+ if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
737
+ ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
738
+ yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
739
+ r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
740
+ }
741
+ else
742
+ yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
743
+ r.send reply_msg, 0, sender_addrinfo
744
+ }
745
+ end
746
+ }
747
+ end
748
+
749
+ # :call-seq:
750
+ # Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... }
751
+ #
752
+ # Run UDP/IP server loop on the given sockets.
753
+ #
754
+ # The return value of Socket.udp_server_sockets is appropriate for the argument.
755
+ #
756
+ # It calls the block for each message received.
757
+ #
758
+ def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
759
+ loop {
760
+ readable, _, _ = IO.select(sockets)
761
+ udp_server_recv(readable, &b)
762
+ }
763
+ end
764
+
765
+ # :call-seq:
766
+ # Socket.udp_server_loop(port) {|msg, msg_src| ... }
767
+ # Socket.udp_server_loop(host, port) {|msg, msg_src| ... }
768
+ #
769
+ # creates a UDP/IP server on _port_ and calls the block for each message arrived.
770
+ # The block is called with the message and its source information.
771
+ #
772
+ # This method allocates sockets internally using _port_.
773
+ # If _host_ is specified, it is used conjunction with _port_ to determine the server addresses.
774
+ #
775
+ # The _msg_ is a string.
776
+ #
777
+ # The _msg_src_ is a Socket::UDPSource object.
778
+ # It is used for reply.
779
+ #
780
+ # # UDP/IP echo server.
781
+ # Socket.udp_server_loop(9261) {|msg, msg_src|
782
+ # msg_src.reply msg
783
+ # }
784
+ #
785
+ def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
786
+ udp_server_sockets(host, port) {|sockets|
787
+ udp_server_loop_on(sockets, &b)
788
+ }
789
+ end
790
+
791
+ # UDP/IP address information used by Socket.udp_server_loop.
792
+ class UDPSource
793
+ # +remote_address+ is an Addrinfo object.
794
+ #
795
+ # +local_address+ is an Addrinfo object.
796
+ #
797
+ # +reply_proc+ is a Proc used to send reply back to the source.
798
+ def initialize(remote_address, local_address, &reply_proc)
799
+ @remote_address = remote_address
800
+ @local_address = local_address
801
+ @reply_proc = reply_proc
802
+ end
803
+
804
+ # Address of the source
805
+ attr_reader :remote_address
806
+
807
+ # Local address
808
+ attr_reader :local_address
809
+
810
+ def inspect # :nodoc:
811
+ "\#<#{self.class}: #{@remote_address.inspect_sockaddr} to #{@local_address.inspect_sockaddr}>"
812
+ end
813
+
814
+ # Sends the String +msg+ to the source
815
+ def reply(msg)
816
+ @reply_proc.call msg
817
+ end
818
+ end
819
+
820
+ # creates a new socket connected to path using UNIX socket socket.
821
+ #
822
+ # If a block is given, the block is called with the socket.
823
+ # The value of the block is returned.
824
+ # The socket is closed when this method returns.
825
+ #
826
+ # If no block is given, the socket is returned.
827
+ #
828
+ # # talk to /tmp/sock socket.
829
+ # Socket.unix("/tmp/sock") {|sock|
830
+ # t = Thread.new { IO.copy_stream(sock, STDOUT) }
831
+ # IO.copy_stream(STDIN, sock)
832
+ # t.join
833
+ # }
834
+ #
835
+ def self.unix(path) # :yield: socket
836
+ addr = Addrinfo.unix(path)
837
+ sock = addr.connect
838
+ if block_given?
839
+ begin
840
+ yield sock
841
+ ensure
842
+ sock.close if !sock.closed?
843
+ end
844
+ else
845
+ sock
846
+ end
847
+ end
848
+
849
+ # creates a UNIX server socket on _path_
850
+ #
851
+ # If no block given, it returns a listening socket.
852
+ #
853
+ # If a block is given, it is called with the socket and the block value is returned.
854
+ # When the block exits, the socket is closed and the socket file is removed.
855
+ #
856
+ # socket = Socket.unix_server_socket("/tmp/s")
857
+ # p socket #=> #<Socket:fd 3>
858
+ # p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
859
+ #
860
+ # Socket.unix_server_socket("/tmp/sock") {|s|
861
+ # p s #=> #<Socket:fd 3>
862
+ # p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
863
+ # }
864
+ #
865
+ def self.unix_server_socket(path)
866
+ if !unix_socket_abstract_name?(path)
867
+ begin
868
+ st = File.lstat(path)
869
+ rescue Errno::ENOENT
870
+ end
871
+ if st && st.socket? && st.owned?
872
+ File.unlink path
873
+ end
874
+ end
875
+ s = Addrinfo.unix(path).listen
876
+ if block_given?
877
+ begin
878
+ yield s
879
+ ensure
880
+ s.close if !s.closed?
881
+ if !unix_socket_abstract_name?(path)
882
+ File.unlink path
883
+ end
884
+ end
885
+ else
886
+ s
887
+ end
888
+ end
889
+
890
+ class << self
891
+ private
892
+
893
+ def unix_socket_abstract_name?(path)
894
+ /linux/ =~ RUBY_PLATFORM && /\A(\0|\z)/ =~ path
895
+ end
896
+ end
897
+
898
+ # creates a UNIX socket server on _path_.
899
+ # It calls the block for each socket accepted.
900
+ #
901
+ # If _host_ is specified, it is used with _port_ to determine the server ports.
902
+ #
903
+ # The socket is *not* closed when the block returns.
904
+ # So application should close it.
905
+ #
906
+ # This method deletes the socket file pointed by _path_ at first if
907
+ # the file is a socket file and it is owned by the user of the application.
908
+ # This is safe only if the directory of _path_ is not changed by a malicious user.
909
+ # So don't use /tmp/malicious-users-directory/socket.
910
+ # Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit.
911
+ #
912
+ # # Sequential echo server.
913
+ # # It services only one client at a time.
914
+ # Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
915
+ # begin
916
+ # IO.copy_stream(sock, sock)
917
+ # ensure
918
+ # sock.close
919
+ # end
920
+ # }
921
+ #
922
+ def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
923
+ unix_server_socket(path) {|serv|
924
+ accept_loop(serv, &b)
925
+ }
926
+ end
927
+
928
+ end