Skip to content

Make socat listen on IPv6 and IPv4 - #31

Open
TobiX wants to merge 1 commit into
chromedp:mainfrom
TobiX:ipv6-socat
Open

TobiX wants to merge 1 commit into
chromedp:mainfrom
TobiX:ipv6-socat

Conversation

@TobiX

@TobiX TobiX commented Aug 14, 2026

Copy link
Copy Markdown

Running socat twice should cover most setups:

  • On "normal" dual-stack setups, the IPv6 listen creates a dual-stack socket and the IPv4 listen then fails
  • On dual-stack setups with bindv6only enabled, both socat processes will succeed.
  • On systems with IPv6 disabled, the first socat call will fail and the second will create a proper IPv4 listening socket

This should help on most systems with "proper" IPv6 setup, especially with recent podman versions, which creates both, an IPv4 and an IPv6 forward, when using -p 9222:9222', which leads to the situation that if the container only provides an IPv4 listening socket, requests to localhostfail with an unhelpfulConnection reset by peer`:

$ curl -v http://localhost:9222/json/version
* Host localhost:9222 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9222...
* Established connection to localhost (::1 port 9222) from ::1 port 57868 
* using HTTP/1.x
> GET /json/version HTTP/1.1
> Host: localhost:9222
> User-Agent: curl/8.21.0
> Accept: */*
> 
* Request completely sent off
* Recv failure: Connection reset by peer
* closing connection #0
curl: (56) Recv failure: Connection reset by peer

vs.

 curl -v -4 http://localhost:9222/json/version
* Host localhost:9222 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying 127.0.0.1:9222...
* Established connection to localhost (127.0.0.1 port 9222) from 127.0.0.1 port 46858 
* using HTTP/1.x
> GET /json/version HTTP/1.1
> Host: localhost:9222
> User-Agent: curl/8.21.0
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Security-Policy:frame-ancestors 'none'
< Content-Length:408
< Content-Type:application/json; charset=UTF-8
< 
{
   "Browser": "Chrome/151.0.7922.47",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.7922.47 Safari/537.36",
   "V8-Version": "15.1.206.10",
   "WebKit-Version": "537.36 (@c583827a354674180dc8856eb65aeb3f0afa5ee9)",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/9c4617c8-ccb3-44dd-af7e-f938eeb1a7ad"
}
* Connection #0 to host localhost:9222 left intact

Running socat twice should cover most setups:

- On "normal" dual-stack setups, the IPv6 listen creates a dual-stack
  socket and the IPv4 listen then fails
- On dual-stack setups with `bindv6only` enabled, both socat processes
  will succeed.
- On systems with IPv6 disabled, the first socat call will fail and the
  second will create a proper IPv4 listening socket
@kenshaw

kenshaw commented Aug 18, 2026

Copy link
Copy Markdown
Member

@TobiX I understand the necessity of this, but before I merge it, I want to think about if this is the best way for this. I really didn't like that I had to fork/add socat to begin with, and another additional process feels even worse to me. I'll take the time to see if this is really the only option, or if there might be some other better/more prudent way, and/or some kind of a better hack for this. I haven't looked at the Chrome/ium sources recently, there's a decent chance this may not even be necessary any more.

@TobiX

TobiX commented Aug 18, 2026

Copy link
Copy Markdown
Author

I think headless-shell (and normal chrome, too?) just ignores the --remote-debugging-address switch and always binds to 127.0.0.1...

I found multiple places in the Chromium source which reads those options, only one parses the address switch:

(Listening code is probably this one? https://github.com/chromium/chromium/blob/f84ed0f1583f88581b9d4093b7459103066cb1bd/headless/lib/browser/headless_devtools.cc#L41)

Stupid sidenode: The local listening code is written in a way to support systems without IPv4 stack (it does IPv6 localhost only after IPv4 localhost fails) - That's certainly a choice 😺

It seems the Chrome developers really don't like debugging servers on anything non-localhost, see: https://chromium-review.googlesource.com/c/chromium/src/+/5512634 😞

We could switch socat to a different tool with dedicated IPv6 support, but I didn't find anything adequate in my short search...

So here is another (stupid) idea to solve the problem: Add an LD_PRELOAD helper library which "fixes" Chrome's socket/bind/listen call. But making that IPv6-aware would be very brittle, since one would have to somehow change an IPv4 socket into an IPv6 socket...

Footnote: In most cases my change doesn't add an additional socat process, since one socat is enough for a dual-socket listener. Unfortunately, socat has no internal support for "proper" dual-stack support, so I had to resort to the two-call workaround...

@TobiX

TobiX commented Aug 23, 2026

Copy link
Copy Markdown
Author

@kenshaw I commited crimes: https://codeberg.org/TobiX/listenany - this was more a test to see it was possible, it doesn't cover any edge-cases yet (IPv4- or IPV6-only systems, non-standard bindv6only settings)

PS: Chromium's internal webserver has another don't-do-that gate: It checks the Host header and only allows localhost or IP addresses (but they can be IPv4 or IPv6!)

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