Skip to content

TCP and SSL: treat an IP literal as an address, not a host name - #12

Open
dominicletz wants to merge 2 commits into
masterfrom
cursor/tcp-ssl-ip-literal-84b4
Open

dominicletz wants to merge 2 commits into
masterfrom
cursor/tcp-ssl-ip-literal-84b4

Conversation

@dominicletz

@dominicletz dominicletz commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Rebase of #8 onto current master. The address-parser commit that #8 was stacked on already landed as #7, so it is not in this branch.

Socket.TCP.connect/3 and Socket.SSL.connect/3 turned every binary into a charlist and handed it to the resolver in the socket's family. "::1" therefore came back :nxdomain unless the caller had also passed version: 6, while a tuple carries its own family. A literal is now parsed first ("::1", "[::1]", or an IPv4 literal) and only a real host name is sent as a charlist. The @spec of Socket.TCP.connect/3 and connect!/3 now says Socket.Address.t(), which is what the clauses accept.

This is the base of the stack. Merge this first, then #13, then #11.

mix test on this branch is 20 tests, 0 failures (Elixir 1.14.0 / OTP 25). mix format --check-formatted passes.

A charlist literal such as ~c"::1" is still passed straight to the resolver. Binaries and tuples are the forms the tests cover, and a charlist did not match connect/3 at all before this change.

Open in Web Open in Cursor 

Note

Medium Risk
Changes shared connect entry points for TCP and SSL; hostname behavior is preserved for non-literals, but any caller relying on the old charlist-only binary path could see different IPv6 results.

Reviewed by Cursor Bugbot for commit c137c8d. Configure here.

connect/3 turned every binary into a charlist and handed it to
:gen_tcp.connect/4, which resolves it as a name in the socket's family.
"::1" therefore came back :nxdomain on a socket nobody had declared
:inet6, while a tuple carries its own family. Parse first, and fall back
to a charlist only for what really is a host name.

The @SPEC already announced :inet.ip_address(), which the code could not
handle. It now says Socket.Address.t(), which it does.
connect/3 carried the same charlist conversion as Socket.TCP.connect/3,
with the same effect: the literal went to the resolver instead of to the
socket.

Its test reads a failure rather than a success — no TLS server answers on
the other end — because :nxdomain is what the resolution path returns and
a handshake timeout is not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants