PageRenderTime 5ms CodeModel.GetById 79ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 1ms

/ext/socket/lib/socket.rb

https://github.com/dream-hunter/ruby
Ruby | 761 lines | 400 code | 36 blank | 325 comment | 93 complexity | fee93189f4ee7e51ee4757e77b19df82 MD5 | raw file
  1require 'socket.so'
  2
  3class Addrinfo
  4  # creates an Addrinfo object from the arguments.
  5  #
  6  # The arguments are interpreted as similar to self.
  7  #
  8  #   Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80)
  9  #   #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)>
 10  #
 11  #   Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2")
 12  #   #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM>
 13  #
 14  def family_addrinfo(*args)
 15    if args.empty?
 16      raise ArgumentError, "no address specified"
 17    elsif Addrinfo === args.first
 18      raise ArgumentError, "too many arguments" if args.length != 1
 19    elsif self.ip?
 20      raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2
 21      host, port = args
 22      Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0]
 23    elsif self.unix?
 24      raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1
 25      path, = args
 26      Addrinfo.unix(path)
 27    else
 28      raise ArgumentError, "unexpected family"
 29    end
 30  end
 31
 32  # creates a new Socket connected to the address of +local_addrinfo+.
 33  #
 34  # If no arguments are given, the address of the socket is not bound.
 35  #
 36  # If a block is given the created socket is yielded for each address.
 37  #
 38  def connect_internal(local_addrinfo) # :yields: socket
 39    sock = Socket.new(self.pfamily, self.socktype, self.protocol)
 40    begin
 41      sock.ipv6only! if self.ipv6?
 42      sock.bind local_addrinfo if local_addrinfo
 43      sock.connect(self)
 44      if block_given?
 45        yield sock
 46      else
 47        sock
 48      end
 49    ensure
 50      sock.close if !sock.closed? && (block_given? || $!)
 51    end
 52  end
 53  private :connect_internal
 54
 55  # creates a socket connected to the address of self.
 56  #
 57  # If one or more arguments given as _local_addr_args_,
 58  # it is used as the local address of the socket.
 59  # _local_addr_args_ is given for family_addrinfo to obtain actual address.
 60  #
 61  # If no arguments given, the local address of the socket is not bound.
 62  #
 63  # If a block is given, it is called with the socket and the value of the block is returned.
 64  # The socket is returned otherwise.
 65  #
 66  #   Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s|
 67  #     s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
 68  #     puts s.read
 69  #   }
 70  #
 71  #   # Addrinfo object can be taken for the argument.
 72  #   Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s|
 73  #     s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
 74  #     puts s.read
 75  #   }
 76  #
 77  def connect_from(*local_addr_args, &block)
 78    connect_internal(family_addrinfo(*local_addr_args), &block)
 79  end
 80
 81  # creates a socket connected to the address of self.
 82  #
 83  # If a block is given, it is called with the socket and the value of the block is returned.
 84  # The socket is returned otherwise.
 85  #
 86  #   Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s|
 87  #     s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
 88  #     puts s.read
 89  #   }
 90  #
 91  def connect(&block)
 92    connect_internal(nil, &block)
 93  end
 94
 95  # creates a socket connected to _remote_addr_args_ and bound to self.
 96  #
 97  # If a block is given, it is called with the socket and the value of the block is returned.
 98  # The socket is returned otherwise.
 99  #
100  #   Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s|
101  #     s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
102  #     puts s.read
103  #   }
104  #
105  def connect_to(*remote_addr_args, &block)
106    remote_addrinfo = family_addrinfo(*remote_addr_args)
107    remote_addrinfo.send(:connect_internal, self, &block)
108  end
109
110  # creates a socket bound to self.
111  #
112  # If a block is given, it is called with the socket and the value of the block is returned.
113  # The socket is returned otherwise.
114  #
115  #   Addrinfo.udp("0.0.0.0", 9981).bind {|s|
116  #     s.local_address.connect {|s| s.send "hello", 0 }
117  #     p s.recv(10) #=> "hello"
118  #   }
119  #
120  def bind
121    sock = Socket.new(self.pfamily, self.socktype, self.protocol)
122    begin
123      sock.ipv6only! if self.ipv6?
124      sock.setsockopt(:SOCKET, :REUSEADDR, 1)
125      sock.bind(self)
126      if block_given?
127        yield sock
128      else
129        sock
130      end
131    ensure
132      sock.close if !sock.closed? && (block_given? || $!)
133    end
134  end
135
136  # creates a listening socket bound to self.
137  def listen(backlog=5)
138    sock = Socket.new(self.pfamily, self.socktype, self.protocol)
139    begin
140      sock.ipv6only! if self.ipv6?
141      sock.setsockopt(:SOCKET, :REUSEADDR, 1)
142      sock.bind(self)
143      sock.listen(backlog)
144      if block_given?
145        yield sock
146      else
147        sock
148      end
149    ensure
150      sock.close if !sock.closed? && (block_given? || $!)
151    end
152  end
153
154  # iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
155  #
156  #   Addrinfo.foreach(nil, 80) {|x| p x }
157  #   #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)>
158  #   #   #<Addrinfo: 127.0.0.1:80 UDP (:80)>
159  #   #   #<Addrinfo: [::1]:80 TCP (:80)>
160  #   #   #<Addrinfo: [::1]:80 UDP (:80)>
161  #
162  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
163    Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
164  end
165end
166
167class BasicSocket < IO
168  # Returns an address of the socket suitable for connect in the local machine.
169  #
170  # This method returns _self_.local_address, except following condition.
171  #
172  # - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).
173  # - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).
174  #
175  # If the local address is not suitable for connect, SocketError is raised.
176  # IPv4 and IPv6 address which port is 0 is not suitable for connect.
177  # Unix domain socket which has no path is not suitable for connect.
178  #
179  #   Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
180  #     p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
181  #     serv.connect_address.connect {|c|
182  #       s, _ = serv.accept
183  #       p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
184  #     }
185  #   }
186  #
187  def connect_address
188    addr = local_address
189    afamily = addr.afamily
190    if afamily == Socket::AF_INET
191      raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
192      if addr.ip_address == "0.0.0.0"
193        addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
194      end
195    elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
196      raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
197      if addr.ip_address == "::"
198        addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
199      elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
200        addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
201      elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address.
202        addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
203      end
204    elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
205      raise SocketError, "unbound Unix socket" if addr.unix_path == ""
206    end
207    addr
208  end
209end
210
211class Socket < BasicSocket
212  # enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
213  def ipv6only!
214    if defined? Socket::IPV6_V6ONLY
215      self.setsockopt(:IPV6, :V6ONLY, 1)
216    end
217  end
218
219  # creates a new socket object connected to host:port using TCP/IP.
220  #
221  # If local_host:local_port is given,
222  # the socket is bound to it.
223  #
224  # If a block is given, the block is called with the socket.
225  # The value of the block is returned.
226  # The socket is closed when this method returns.
227  #
228  # If no block is given, the socket is returned.
229  #
230  #   Socket.tcp("www.ruby-lang.org", 80) {|sock|
231  #     sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
232  #     sock.close_write
233  #     puts sock.read
234  #   }
235  #
236  def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
237    last_error = nil
238    ret = nil
239
240    local_addr_list = nil
241    if local_host != nil || local_port != nil
242      local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
243    end
244
245    Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
246      if local_addr_list
247        local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
248        next if !local_addr
249      else
250        local_addr = nil
251      end
252      begin
253        sock = local_addr ? ai.connect_from(local_addr) : ai.connect
254      rescue SystemCallError
255        last_error = $!
256        next
257      end
258      ret = sock
259      break
260    }
261    if !ret
262      if last_error
263        raise last_error
264      else
265        raise SocketError, "no appropriate local address"
266      end
267    end
268    if block_given?
269      begin
270        yield ret
271      ensure
272        ret.close if !ret.closed?
273      end
274    else
275      ret
276    end
277  end
278
279  # :stopdoc:
280  def self.ip_sockets_port0(ai_list, reuseaddr)
281    begin
282      sockets = []
283      port = nil
284      ai_list.each {|ai|
285        begin
286          s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
287        rescue SystemCallError
288          next
289        end
290        sockets << s
291        s.ipv6only! if ai.ipv6?
292        if reuseaddr
293          s.setsockopt(:SOCKET, :REUSEADDR, 1)
294        end
295        if !port
296          s.bind(ai)
297          port = s.local_address.ip_port
298        else
299          s.bind(ai.family_addrinfo(ai.ip_address, port))
300        end
301      }
302    rescue Errno::EADDRINUSE
303      sockets.each {|s|
304        s.close
305      }
306      retry
307    end
308    sockets
309  ensure
310    sockets.each {|s| s.close if !s.closed? } if $!
311  end
312  class << self
313    private :ip_sockets_port0
314  end
315
316  def self.tcp_server_sockets_port0(host)
317    ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
318    sockets = ip_sockets_port0(ai_list, true)
319    sockets.each {|s|
320      s.listen(5)
321    }
322    sockets
323  ensure
324    sockets.each {|s| s.close if !s.closed? } if $! && sockets
325  end
326  class << self
327    private :tcp_server_sockets_port0
328  end
329  # :startdoc:
330
331  # creates TCP/IP server sockets for _host_ and _port_.
332  # _host_ is optional.
333  #
334  # If no block given,
335  # it returns an array of listening sockets.
336  #
337  # If a block is given, the block is called with the sockets.
338  # The value of the block is returned.
339  # The socket is closed when this method returns.
340  #
341  # If _port_ is 0, actual port number is choosen dynamically.
342  # However all sockets in the result has same port number.
343  #
344  #   # tcp_server_sockets returns two sockets.
345  #   sockets = Socket.tcp_server_sockets(1296)
346  #   p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
347  #
348  #   # The sockets contains IPv6 and IPv4 sockets.
349  #   sockets.each {|s| p s.local_address }
350  #   #=> #<Addrinfo: [::]:1296 TCP>
351  #   #   #<Addrinfo: 0.0.0.0:1296 TCP>
352  #
353  #   # IPv6 and IPv4 socket has same port number, 53114, even if it is choosen dynamically.
354  #   sockets = Socket.tcp_server_sockets(0)
355  #   sockets.each {|s| p s.local_address }
356  #   #=> #<Addrinfo: [::]:53114 TCP>
357  #   #   #<Addrinfo: 0.0.0.0:53114 TCP>
358  #
359  #   # The block is called with the sockets.
360  #   Socket.tcp_server_sockets(0) {|sockets|
361  #     p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
362  #   }
363  #
364  def self.tcp_server_sockets(host=nil, port)
365    if port == 0
366      sockets = tcp_server_sockets_port0(host)
367    else
368      begin
369        last_error = nil
370        sockets = []
371        Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
372          begin
373            s = ai.listen
374          rescue SystemCallError
375            last_error = $!
376            next
377          end
378          sockets << s
379        }
380        if sockets.empty?
381          raise last_error
382        end
383      ensure
384        sockets.each {|s| s.close if !s.closed? } if $!
385      end
386    end
387    if block_given?
388      begin
389        yield sockets
390      ensure
391        sockets.each {|s| s.close if !s.closed? }
392      end
393    else
394      sockets
395    end
396  end
397
398  # yield socket and client address for each a connection accepted via given sockets.
399  #
400  # The arguments are a list of sockets.
401  # The individual argument should be a socket or an array of sockets.
402  #
403  # This method yields the block sequentially.
404  # It means that the next connection is not accepted until the block returns.
405  # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
406  #
407  def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
408    sockets.flatten!(1)
409    if sockets.empty?
410      raise ArgumentError, "no sockets"
411    end
412    loop {
413      readable, _, _ = IO.select(sockets)
414      readable.each {|r|
415        begin
416          sock, addr = r.accept_nonblock
417        rescue IO::WaitReadable
418          next
419        end
420        yield sock, addr
421      }
422    }
423  end
424
425  # creates a TCP/IP server on _port_ and calls the block for each connection accepted.
426  # The block is called with a socket and a client_address as an Addrinfo object.
427  #
428  # If _host_ is specified, it is used with _port_ to determine the server addresses.
429  #
430  # The socket is *not* closed when the block returns.
431  # So application should close it explicitly.
432  #
433  # This method calls the block sequentially.
434  # It means that the next connection is not accepted until the block returns.
435  # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
436  #
437  # Note that Addrinfo.getaddrinfo is used to determine the server socket addresses.
438  # When Addrinfo.getaddrinfo returns two or more addresses,
439  # IPv4 and IPv6 address for example,
440  # all of them are used.
441  # Socket.tcp_server_loop succeeds if one socket can be used at least.
442  #
443  #   # Sequential echo server.
444  #   # It services only one client at a time.
445  #   Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
446  #     begin
447  #       IO.copy_stream(sock, sock)
448  #     ensure
449  #       sock.close
450  #     end
451  #   }
452  #
453  #   # Threaded echo server
454  #   # It services multiple clients at a time.
455  #   # Note that it may accept connections too much.
456  #   Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
457  #     Thread.new {
458  #       begin
459  #         IO.copy_stream(sock, sock)
460  #       ensure
461  #         sock.close
462  #       end
463  #     }
464  #   }
465  #
466  def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
467    tcp_server_sockets(host, port) {|sockets|
468      accept_loop(sockets, &b)
469    }
470  end
471
472  # :call-seq:
473  #   Socket.udp_server_sockets([host, ] port)
474  #
475  # Creates UDP/IP sockets for a UDP server.
476  #
477  # If no block given, it returns an array of sockets.
478  #
479  # If a block is given, the block is called with the sockets.
480  # The value of the block is returned.
481  # The sockets are closed when this method returns.
482  #
483  # If _port_ is zero, some port is choosen.
484  # But the choosen port is used for the all sockets.
485  #
486  #   # UDP/IP echo server
487  #   Socket.udp_server_sockets(0) {|sockets|
488  #     p sockets.first.local_address.ip_port     #=> 32963
489  #     Socket.udp_server_loop_on(sockets) {|msg, msg_src|
490  #       msg_src.reply msg
491  #     }
492  #   }
493  #
494  def self.udp_server_sockets(host=nil, port)
495    last_error = nil
496    sockets = []
497
498    ipv6_recvpktinfo = nil
499    if defined? Socket::AncillaryData
500      if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
501        ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
502      elsif defined? Socket::IPV6_PKTINFO # RFC 2292
503        ipv6_recvpktinfo = Socket::IPV6_PKTINFO
504      end
505    end
506
507    local_addrs = Socket.ip_address_list
508
509    ip_list = []
510    Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
511      if ai.ipv4? && ai.ip_address == "0.0.0.0"
512        local_addrs.each {|a|
513          next if !a.ipv4?
514          ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
515        }
516      elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
517        local_addrs.each {|a|
518          next if !a.ipv6?
519          ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
520        }
521      else
522        ip_list << ai
523      end
524    }
525
526    if port == 0
527      sockets = ip_sockets_port0(ip_list, false)
528    else
529      ip_list.each {|ip|
530        ai = Addrinfo.udp(ip.ip_address, port)
531        begin
532          s = ai.bind
533        rescue SystemCallError
534          last_error = $!
535          next
536        end
537        sockets << s
538      }
539      if sockets.empty?
540        raise last_error
541      end
542    end
543
544    pktinfo_sockets = {}
545    sockets.each {|s|
546      ai = s.local_address
547      if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
548        s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
549        pktinfo_sockets[s] = true
550      end
551    }
552
553    if block_given?
554      begin
555        yield sockets
556      ensure
557        sockets.each {|s| s.close if !s.closed? } if sockets
558      end
559    else
560      sockets
561    end
562  end
563
564  # :call-seq:
565  #   Socket.udp_server_recv(sockets) {|msg, msg_src| ... }
566  #
567  # Receive UDP/IP packets from the given _sockets_.
568  # For each packet received, the block is called.
569  #
570  # The block receives _msg_ and _msg_src_.
571  # _msg_ is a string which is the payload of the received packet.
572  # _msg_src_ is a Socket::UDPSource object which is used for reply.
573  #
574  # Socket.udp_server_loop can be implemented using this method as follows.
575  #
576  #   udp_server_sockets(host, port) {|sockets|
577  #     loop {
578  #       readable, _, _ = IO.select(sockets)
579  #       udp_server_recv(readable) {|msg, msg_src| ... }
580  #     }
581  #   }
582  #
583  def self.udp_server_recv(sockets)
584    sockets.each {|r|
585      begin
586        msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock
587      rescue IO::WaitReadable
588        next
589      end
590      ai = r.local_address
591      if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
592        ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
593        yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
594          r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
595        }
596      else
597        yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
598          r.send reply_msg, 0, sender_addrinfo
599        }
600      end
601    }
602  end
603
604  # :call-seq:
605  #   Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... }
606  #
607  # Run UDP/IP server loop on the given sockets.
608  #
609  # The return value of Socket.udp_server_sockets is appropriate for the argument.
610  #
611  # It calls the block for each message received.
612  #
613  def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
614    loop {
615      readable, _, _ = IO.select(sockets)
616      udp_server_recv(readable, &b)
617    }
618  end
619
620  # :call-seq:
621  #   Socket.udp_server_loop(port) {|msg, msg_src| ... }
622  #   Socket.udp_server_loop(host, port) {|msg, msg_src| ... }
623  #
624  # creates a UDP/IP server on _port_ and calls the block for each message arrived.
625  # The block is called with the message and its source information.
626  #
627  # This method allocates sockets internally using _port_.
628  # If _host_ is specified, it is used conjunction with _port_ to determine the server addresses.
629  #
630  # The _msg_ is a string.
631  #
632  # The _msg_src_ is a Socket::UDPSource object.
633  # It is used for reply.
634  #
635  #   # UDP/IP echo server.
636  #   Socket.udp_server_loop(9261) {|msg, msg_src|
637  #     msg_src.reply msg
638  #   }
639  #
640  def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
641    udp_server_sockets(host, port) {|sockets|
642      udp_server_loop_on(sockets, &b)
643    }
644  end
645
646  # UDP/IP address information used by Socket.udp_server_loop.
647  class UDPSource
648    def initialize(remote_address, local_address, &reply_proc)
649      @remote_address = remote_address
650      @local_address = local_address
651      @reply_proc = reply_proc
652    end
653    attr_reader :remote_address, :local_address
654
655    def inspect
656      "\#<#{self.class}: #{@remote_address.inspect_sockaddr} to #{@local_address.inspect_sockaddr}>"
657    end
658
659    def reply(msg)
660      @reply_proc.call msg
661    end
662  end
663
664  # creates a new socket connected to path using UNIX socket socket.
665  #
666  # If a block is given, the block is called with the socket.
667  # The value of the block is returned.
668  # The socket is closed when this method returns.
669  #
670  # If no block is given, the socket is returned.
671  #
672  #   # talk to /tmp/sock socket.
673  #   Socket.unix("/tmp/sock") {|sock|
674  #     t = Thread.new { IO.copy_stream(sock, STDOUT) }
675  #     IO.copy_stream(STDIN, sock)
676  #     t.join
677  #   }
678  #
679  def self.unix(path) # :yield: socket
680    addr = Addrinfo.unix(path)
681    sock = addr.connect
682    if block_given?
683      begin
684        yield sock
685      ensure
686        sock.close if !sock.closed?
687      end
688    else
689      sock
690    end
691  end
692
693  # creates a UNIX server socket on _path_
694  #
695  # If no block given, it returns a listening socket.
696  #
697  # If a block is given, it is called with the socket and the block value is returned.
698  # When the block exits, the socket is closed and the socket file is removed.
699  #
700  #   socket = Socket.unix_server_socket("/tmp/s")
701  #   p socket                  #=> #<Socket:fd 3>
702  #   p socket.local_address    #=> #<Addrinfo: /tmp/s SOCK_STREAM>
703  #
704  #   Socket.unix_server_socket("/tmp/sock") {|s|
705  #     p s                     #=> #<Socket:fd 3>
706  #     p s.local_address       #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
707  #   }
708  #
709  def self.unix_server_socket(path)
710    begin
711      st = File.lstat(path)
712    rescue Errno::ENOENT
713    end
714    if st && st.socket? && st.owned?
715      File.unlink path
716    end
717    s = Addrinfo.unix(path).listen
718    if block_given?
719      begin
720        yield s
721      ensure
722        s.close if !s.closed?
723        File.unlink path
724      end
725    else
726      s
727    end
728  end
729
730  # creates a UNIX socket server on _path_.
731  # It calls the block for each socket accepted.
732  #
733  # If _host_ is specified, it is used with _port_ to determine the server ports.
734  #
735  # The socket is *not* closed when the block returns.
736  # So application should close it.
737  #
738  # This method deletes the socket file pointed by _path_ at first if
739  # the file is a socket file and it is owned by the user of the application.
740  # This is safe only if the directory of _path_ is not changed by a malicious user.
741  # So don't use /tmp/malicious-users-directory/socket.
742  # Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit.
743  #
744  #   # Sequential echo server.
745  #   # It services only one client at a time.
746  #   Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
747  #     begin
748  #       IO.copy_stream(sock, sock)
749  #     ensure
750  #       sock.close
751  #     end
752  #   }
753  #
754  def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
755    unix_server_socket(path) {|serv|
756      accept_loop(serv, &b)
757    }
758  end
759
760end
761