Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS queries over TCP create a new socket per query, exhausting the available file descriptors #20

Open
postmodern opened this issue Jan 5, 2022 · 3 comments
Assignees

Comments

@postmodern
Copy link
Contributor

I noticed when performing many queries over TCP (ex: [:tcp, '8.8.8.8', 53]) that the ruby process would create a new TCP socket per-query. These TCP sockets would not be closed fast enough (ex; waiting in TIME_WAIT), and would eventually cause the process to hit it's maximum number of open file descriptors (ex: Errno::EADDRNOTAVAIL).

Steps To Reproduce

  1. ruby test.rb
  2. In another terminal run watch "netstat -n" to observe connections

Gemfile

source 'https://rubygems.org/'

gem 'async-dns'

test.rb

require 'bundler/setup'
require 'async/dns'

nameservers = [[:tcp, '8.8.8.8', 53]]
resolver = Async::DNS::Resolver.new(nameservers)
hostname = 'google.com'

Async do
  loop do
    begin
      addresses = resolver.addresses_for(hostname)
    rescue Async::DNS::ResolutionFailure
    end
  end
end

Note: if :tcp is changed to :udp, then only one socket is created and reused.

@ioquatix
Copy link
Member

ioquatix commented Jan 5, 2022

This gem doesn't use a pool, we should update it - it's based on a 10 year old code base which didn't have access to such a abstraction at the time.

@postmodern
Copy link
Contributor Author

I was looking through the code and it appears that UDP "sockets" are reused, where as the TCP "sockets" are used to create a new Transport for each request.

def try_datagram_server(request, socket)
socket.sendmsg(request.packet, 0)
data, peer = socket.recvmsg(UDP_TRUNCATION_SIZE)
return Async::DNS::decode_message(data)
end
def try_stream_server(request, socket)
transport = Transport.new(socket)
transport.write_chunk(request.packet)
input_data = transport.read_chunk
return Async::DNS::decode_message(input_data)
end

@ioquatix
Copy link
Member

ioquatix commented Jan 6, 2022

I will sort this out but it won't be this month probably, I have a lot on my plate, but I'll clear some time to review this during February.

@ioquatix ioquatix self-assigned this Jan 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants