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