From 7726160ec793843ff9bb2d6be6b63d4dd0eeca56 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sat, 13 Apr 2024 21:43:41 -0400 Subject: [PATCH] add a config switch to accept hostnames from WEBIRC See #1686; this allows i2pd to pass the i2p address to Ergo, which may be useful for moderation under some circumstances. --- default.yaml | 4 +++ irc/client.go | 69 ++++++++++++++++++++++++++---------------------- irc/gateways.go | 1 + irc/handlers.go | 17 ++++++++++-- irc/server.go | 4 +-- traditional.yaml | 3 +++ 6 files changed, 62 insertions(+), 36 deletions(-) diff --git a/default.yaml b/default.yaml index 36f3ee1e..abe48b14 100644 --- a/default.yaml +++ b/default.yaml @@ -218,6 +218,10 @@ server: # - "192.168.1.1" # - "192.168.10.1/24" + # whether to accept the hostname parameter on the WEBIRC line as the IRC hostname + # (the default/recommended Ergo configuration will use cloaks instead) + accept-hostname: false + # maximum length of clients' sendQ in bytes # this should be big enough to hold bursts of channel/direct messages max-sendq: 96k diff --git a/irc/client.go b/irc/client.go index 47aca273..218f4d3d 100644 --- a/irc/client.go +++ b/irc/client.go @@ -160,13 +160,14 @@ type Session struct { idleTimer *time.Timer pingSent bool // we sent PING to a putatively idle connection and we're waiting for PONG - sessionID int64 - socket *Socket - realIP net.IP - proxiedIP net.IP - rawHostname string - isTor bool - hideSTS bool + sessionID int64 + socket *Socket + realIP net.IP + proxiedIP net.IP + rawHostname string + hostnameFinalized bool + isTor bool + hideSTS bool fakelag Fakelag deferredFakelagCount int @@ -488,12 +489,21 @@ func (client *Client) resizeHistory(config *Config) { } } -// resolve an IP to an IRC-ready hostname, using reverse DNS, forward-confirming if necessary, -// and sending appropriate notices to the client -func (client *Client) lookupHostname(session *Session, overwrite bool) { +// once we have the final IP address (from the connection itself or from proxy data), +// compute the various possibilities for the hostname: +// * In the default/recommended configuration, via the cloak algorithm +// * If hostname lookup is enabled, via (forward-confirmed) reverse DNS +// * If WEBIRC was used, possibly via the hostname passed on the WEBIRC line +func (client *Client) finalizeHostname(session *Session) { + // only allow this once, since registration can fail (e.g. if the nickname is in use) + if session.hostnameFinalized { + return + } + session.hostnameFinalized = true + if session.isTor { return - } // else: even if cloaking is enabled, look up the real hostname to show to operators + } config := client.server.Config() ip := session.realIP @@ -501,30 +511,27 @@ func (client *Client) lookupHostname(session *Session, overwrite bool) { ip = session.proxiedIP } - var hostname string - lookupSuccessful := false - if config.Server.lookupHostnames { - session.Notice("*** Looking up your hostname...") - hostname, lookupSuccessful = utils.LookupHostname(ip, config.Server.ForwardConfirmHostnames) - if lookupSuccessful { - session.Notice("*** Found your hostname") + // even if cloaking is enabled, we may want to look up the real hostname to show to operators: + if session.rawHostname == "" { + var hostname string + lookupSuccessful := false + if config.Server.lookupHostnames { + session.Notice("*** Looking up your hostname...") + hostname, lookupSuccessful = utils.LookupHostname(ip, config.Server.ForwardConfirmHostnames) + if lookupSuccessful { + session.Notice("*** Found your hostname") + } else { + session.Notice("*** Couldn't look up your hostname") + } } else { - session.Notice("*** Couldn't look up your hostname") + hostname = utils.IPStringToHostname(ip.String()) } - } else { - hostname = utils.IPStringToHostname(ip.String()) + session.rawHostname = hostname } - session.rawHostname = hostname - cloakedHostname := config.Server.Cloaks.ComputeCloak(ip) - client.stateMutex.Lock() - defer client.stateMutex.Unlock() - // update the hostname if this is a new connection, but not if it's a reattach - if overwrite || client.rawHostname == "" { - client.rawHostname = hostname - client.cloakedHostname = cloakedHostname - client.updateNickMaskNoMutex() - } + // these will be discarded if this is actually a reattach: + client.rawHostname = session.rawHostname + client.cloakedHostname = config.Server.Cloaks.ComputeCloak(ip) } func (client *Client) doIdentLookup(conn net.Conn) { diff --git a/irc/gateways.go b/irc/gateways.go index cbc9c4c6..08adac93 100644 --- a/irc/gateways.go +++ b/irc/gateways.go @@ -32,6 +32,7 @@ type webircConfig struct { Fingerprint *string // legacy name for certfp, #1050 Certfp string Hosts []string + AcceptHostname bool `yaml:"accept-hostname"` allowedNets []net.IPNet } diff --git a/irc/handlers.go b/irc/handlers.go index 73393a71..62eb7604 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -3512,8 +3512,9 @@ func webircHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo } } + config := server.Config() givenPassword := []byte(msg.Params[0]) - for _, info := range server.Config().Server.WebIRC { + for _, info := range config.Server.WebIRC { if utils.IPInNets(client.realIP, info.allowedNets) { // confirm password and/or fingerprint if 0 < len(info.Password) && bcrypt.CompareHashAndPassword(info.Password, givenPassword) != nil { @@ -3523,11 +3524,23 @@ func webircHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo continue } - err, quitMsg := client.ApplyProxiedIP(rb.session, net.ParseIP(msg.Params[3]), secure) + candidateIP := msg.Params[3] + err, quitMsg := client.ApplyProxiedIP(rb.session, net.ParseIP(candidateIP), secure) if err != nil { client.Quit(quitMsg, rb.session) return true } else { + if info.AcceptHostname { + candidateHostname := msg.Params[2] + if candidateHostname != candidateIP { + if utils.IsHostname(candidateHostname) { + rb.session.rawHostname = candidateHostname + } else { + // log this at debug level since it may be spammy + server.logger.Debug("internal", "invalid hostname from WEBIRC", candidateHostname) + } + } + } return false } } diff --git a/irc/server.go b/irc/server.go index 1e7915ea..eef47c2c 100644 --- a/irc/server.go +++ b/irc/server.go @@ -314,9 +314,7 @@ func (server *Server) checkBanScriptExemptSASL(config *Config, session *Session) func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) { // XXX PROXY or WEBIRC MUST be sent as the first line of the session; // if we are here at all that means we have the final value of the IP - if session.rawHostname == "" { - session.client.lookupHostname(session, false) - } + c.finalizeHostname(session) // try to complete registration normally // XXX(#1057) username can be filled in by an ident query without the client diff --git a/traditional.yaml b/traditional.yaml index 862fe47d..460a9bd4 100644 --- a/traditional.yaml +++ b/traditional.yaml @@ -192,6 +192,9 @@ server: # - "192.168.1.1" # - "192.168.10.1/24" + # whether to accept the hostname parameter on the WEBIRC line as the IRC hostname + accept-hostname: true + # maximum length of clients' sendQ in bytes # this should be big enough to hold bursts of channel/direct messages max-sendq: 96k