/ext/socket/lib/socket.rb
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