PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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