Project

General

Profile

Actions

Feature #19430

open

Contribution wanted: DNS lookup by c-ares library

Added by mame (Yusuke Endoh) almost 2 years ago. Updated about 1 year ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:112326]

Description

Problem

At the present time, Ruby uses getaddrinfo(3) to resolve names. Because this function is synchronous, we cannot interrupt the thread performing name resolution until the DNS server returns a response.

We can see this behavior by setting blackhole.webpagetest.org (72.66.115.13) as a DNS server, which swallows all packets, and resolving any name:

# cat /etc/resolv.conf
nameserver 72.66.115.13

# ./local/bin/ruby -rsocket -e 'Addrinfo.getaddrinfo("www.ruby-lang.org", 80)'
^C^C^C^C

As we see, Ctrl+C does not stop ruby.

The current workaround that users can take is to do name resolution in a Ruby thread.

Thread.new { Addrinfo.getaddrinfo("www.ruby-lang.org", 80) }.value

The thread that calls this code is interruptible. (Note that the newly created thread itself will be stuck until the DNS lookup exceeds the time out.)

Proposal

We can solve this problem by using c-ares, which is an asynchronous name resolver, as a backend of Addrinfo.getaddrinfo, etc. (@sorah (Sorah Fukumori) told me about this library, thanks!)

https://c-ares.org/

I have created a PoC patch.

https://github.com/mame/ruby/commit/547806146993bbc25984011d423dcc0f913b211c

By applying this patch, we can interrupt Addrinfo.getaddrinfo by Ctrl+C.

# cat /etc/resolv.conf
nameserver 72.66.115.13

# ./local/bin/ruby -rsocket -e 'Addrinfo.getaddrinfo("www.ruby-lang.org", 80)'
^C-e:1:in `getaddrinfo': Interrupt
        from -e:1:in `<main>'

Discussion

About c-ares

According to the site of c-ares, some major tools including libcurl, Wireshark, and Apache Arrow are already using c-ares. In the language interpreter, node.js seems to be using c-ares.

I am honestly not sure about the compatibility of c-ares with getaddrinfo(3). I guess there is no major incompatibility because I have not experienced any name resolution problem of curl. @akr (Akira Tanaka) (who is the author and maintainer of Ruby's socket library) suggested to check if OS-specific name resolution, e.g., WINS on Windows, NIS on Solaris, etc., is supported. He also said that it may be acceptable even if they are not supported.

Whether to bundle c-ares source code with ruby would require further discussion. If this proposal is accepted, then c-ares will become a de facto essential dependency for practical use, like gmp, in my opinion. Incidentally, node.js bundles c-ares: https://github.com/nodejs/node/tree/main/deps/cares

Alternative approaches

Recent glibc provides getaddrinfo_a(3) which performs asynchronous name resolution. However, this function has a fatal problem of being incompatible with fork(2), which is heavily used in the Ruby ecosystem. In fact, the attempt to use getaddrinfo_a(3) (#17134) has been revert because it fails rails tests. (#17220)

Another alternative is to have a worker pthread inside Ruby that calls getaddrinfo(3). Instead of calling getaddrinfo(3) directly, Addrinfo.getaddrinfo would ask the worker to resolve a name and wait for a response. This method should be able to implement cancellation. (Simply put, this means reimplementation of getaddrinfo_a(3) on our own, taking into account of `fork(2).)

This has the advantages: not adding dependencies on external libraries and not having compatibility issues with getaddrinfo(3). However, it is considerably more difficult to implement and maintain. An internal pthread may have a non-trivial impact on the execution efficiency and memory usage. Also, we may need to implement a mechanism to dynamically change the number of workers depending on the load.

It would be ideal if we could try and evaluate both approaches. But my current impression is that using c-ares is the quickest and best compromise.

Contribution wanted

I have made it up to the PoC, but don't have much time to complete this. @naruse (Yui NARUSE) suggested me to create a ticket asking for contributions. Is anyone interested in this?

  • This patch changes rsock_getaddrinfo to accept a timeout argument. There are several places where Qnil is passed as a timeout (where I add // TODO in the PoC). We need to consider what timeout we should pass.
  • This cares only getaddrinfo, but we also need to care getnameinfo (and something else if any). There may be some issues I'm not aware of.
  • I have not yet tested this PoC seriously. It would be great if we could evaluate it with some real apps.

Also, it would be great to hear from someone who knows more about c-ares.


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #19965: Make the name resolution interruptibleClosedmame (Yusuke Endoh)Actions
Actions

Also available in: Atom PDF

Like2
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0