The ident protocol allows an upstream IRC server to correctly kill connections of a specific user, as opposed to all bouncer connections.
Should we send the username? Or should we send an opaque token instead?
There is a guarantee that the ident remote address will be the one we connected to, so we can check that (at least according to a comment in the ircd-seven source code).
An unfortunate fact is that ident queries are performed by ircds as soon as the client connects, even if registration hasn't completed yet 1. So doing
net.Dialand then storing the local/remote addresses is racy (and the race happens in practice, probably due to the TLS handshake taking some time to complete).
There are essentially two ways to fix the race:
- Pick the local and remote addresses prior to connecting. This has essentially two issues:
- After we pick a local port but before we actually bind to it, another process can bind to the port.
- A hostname may map to multiple IP addresses. The Go stdlib may try to connect to any of these, sometimes in parallel (e.g. in dual IPv4/IPv6 configurations). Picking the remote address prior to dialing forces a single IP address.
- In the identd server, if the remote/local address tuple is unknown, wait for a bit (with a timeout) to see whether we've just connected to it.
- This prevents the use of a third-party identd.
(2) also has the downside of potentially keeping identd connections alive more than necessary, and having to start a timer for each of these connections. Given that identd is an open-bar server, this is annoying.
For now I've just delayed the TLS handshake and that seems to be good enough.
Considered doing per-network ident tokens to enhance privacy, but I'm worried about the abuse potential. It would be possible for a banned user to bypass the ban by specifying an alternative network address (e.g. by connecting via plain-text instead of TLS, or by connecting to an IP address instead of a domain name, etc).
Per-user ident tokens are probably good enough.