From 353aeb03894309cc0593731154115886de655a92 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 12 May 2019 03:12:50 -0400 Subject: [PATCH 1/7] normalize most times in the app to UTC Fixes #480 --- irc/channel.go | 8 ++++---- irc/client.go | 11 +++++------ irc/connection_limits/throttler.go | 2 +- irc/dline.go | 4 ++-- irc/getters.go | 2 +- irc/handlers.go | 2 +- irc/hostserv.go | 2 +- irc/kline.go | 2 +- irc/server.go | 2 +- 9 files changed, 17 insertions(+), 18 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index 550973af..73ad7806 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -56,7 +56,7 @@ func NewChannel(s *Server, name string, registered bool) *Channel { } channel := &Channel{ - createdTime: time.Now(), // may be overwritten by applyRegInfo + createdTime: time.Now().UTC(), // may be overwritten by applyRegInfo lists: map[modes.Mode]*UserMaskSet{ modes.BanMask: NewUserMaskSet(), modes.ExceptMask: NewUserMaskSet(), @@ -292,7 +292,7 @@ func (channel *Channel) SetRegistered(founder string) error { return errChannelAlreadyRegistered } channel.registeredFounder = founder - channel.registeredTime = time.Now() + channel.registeredTime = time.Now().UTC() channel.accountToUMode[founder] = modes.ChannelFounder return nil } @@ -686,7 +686,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) // 2. Send JOIN and MODE lines to channel participants (including the new client) // 3. Replay missed message history to the client func (channel *Channel) Resume(newClient, oldClient *Client, timestamp time.Time) { - now := time.Now() + now := time.Now().UTC() channel.resumeAndAnnounce(newClient, oldClient) if !timestamp.IsZero() { channel.replayHistoryForResume(newClient, timestamp, now) @@ -910,7 +910,7 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe channel.stateMutex.Lock() channel.topic = topic channel.topicSetBy = client.nickMaskString - channel.topicSetTime = time.Now() + channel.topicSetTime = time.Now().UTC() channel.stateMutex.Unlock() prefix := client.NickMaskString() diff --git a/irc/client.go b/irc/client.go index 48857b8b..45705f06 100644 --- a/irc/client.go +++ b/irc/client.go @@ -165,7 +165,7 @@ type ClientDetails struct { // NewClient sets up a new client and runs its goroutine. func RunNewClient(server *Server, conn clientConn) { - now := time.Now() + now := time.Now().UTC() config := server.Config() fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest // give them 1k of grace over the limit: @@ -409,8 +409,7 @@ func (client *Client) playReattachMessages(session *Session) { // Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client). func (client *Client) Active(session *Session) { - // TODO normalize all times to utc? - now := time.Now() + now := time.Now().UTC() client.stateMutex.Lock() defer client.stateMutex.Unlock() session.atime = now @@ -472,7 +471,7 @@ func (client *Client) tryResume() (success bool) { success = true // this is a bit racey - client.resumeDetails.ResumedAt = time.Now() + client.resumeDetails.ResumedAt = time.Now().UTC() client.nickTimer.Touch(nil) @@ -496,7 +495,7 @@ func (client *Client) tryResume() (success bool) { hostname := client.Hostname() friends := make(ClientSet) - oldestLostMessage := time.Now() + oldestLostMessage := time.Now().UTC() // work out how much time, if any, is not covered by history buffers for _, channel := range channels { @@ -569,7 +568,7 @@ func (client *Client) tryResumeChannels() { // replay direct PRIVSMG history if !details.Timestamp.IsZero() { - now := time.Now() + now := time.Now().UTC() items, complete := client.history.Between(details.Timestamp, now, false, 0) rb := NewResponseBuffer(client.Sessions()[0]) client.replayPrivmsgHistory(rb, items, complete) diff --git a/irc/connection_limits/throttler.go b/irc/connection_limits/throttler.go index e45aabc4..19f589c2 100644 --- a/irc/connection_limits/throttler.go +++ b/irc/connection_limits/throttler.go @@ -45,7 +45,7 @@ type GenericThrottle struct { // it either denies it (by returning false) or allows it (by returning true) // and records it func (g *GenericThrottle) Touch() (throttled bool, remainingTime time.Duration) { - return g.touch(time.Now()) + return g.touch(time.Now().UTC()) } func (g *GenericThrottle) touch(now time.Time) (throttled bool, remainingTime time.Duration) { diff --git a/irc/dline.go b/irc/dline.go index c723c204..44adf28f 100644 --- a/irc/dline.go +++ b/irc/dline.go @@ -34,7 +34,7 @@ type IPBanInfo struct { } func (info IPBanInfo) timeLeft() time.Duration { - return info.TimeCreated.Add(info.Duration).Sub(time.Now()) + return time.Until(info.TimeCreated.Add(info.Duration)) } func (info IPBanInfo) TimeLeft() string { @@ -114,7 +114,7 @@ func (dm *DLineManager) AddNetwork(network net.IPNet, duration time.Duration, re Reason: reason, OperReason: operReason, OperName: operName, - TimeCreated: time.Now(), + TimeCreated: time.Now().UTC(), Duration: duration, } diff --git a/irc/getters.go b/irc/getters.go index cdeadea5..d231d66a 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -356,7 +356,7 @@ func (channel *Channel) Rename(name, nameCasefolded string) { channel.name = name channel.nameCasefolded = nameCasefolded if channel.registeredFounder != "" { - channel.registeredTime = time.Now() + channel.registeredTime = time.Now().UTC() } channel.stateMutex.Unlock() } diff --git a/irc/handlers.go b/irc/handlers.go index feda4447..6b9a4f45 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2377,7 +2377,7 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R // TIME func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { - rb.Add(nil, server.name, RPL_TIME, client.nick, server.name, time.Now().Format(time.RFC1123)) + rb.Add(nil, server.name, RPL_TIME, client.nick, server.name, time.Now().UTC().Format(time.RFC1123)) return false } diff --git a/irc/hostserv.go b/irc/hostserv.go index ff35a80e..a0d7d49c 100644 --- a/irc/hostserv.go +++ b/irc/hostserv.go @@ -176,7 +176,7 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s hsNotice(rb, client.t("An error occurred")) return } - elapsed := time.Now().Sub(account.VHost.LastRequestTime) + elapsed := time.Since(account.VHost.LastRequestTime) remainingTime := server.AccountConfig().VHosts.UserRequests.Cooldown - elapsed // you can update your existing request, but if you were rejected, // you can't spam a replacement request diff --git a/irc/kline.go b/irc/kline.go index 29ee7b79..4099f482 100644 --- a/irc/kline.go +++ b/irc/kline.go @@ -72,7 +72,7 @@ func (km *KLineManager) AddMask(mask string, duration time.Duration, reason, ope Reason: reason, OperReason: operReason, OperName: operName, - TimeCreated: time.Now(), + TimeCreated: time.Now().UTC(), Duration: duration, } km.addMaskInternal(mask, info) diff --git a/irc/server.go b/irc/server.go index 87ced2b0..6944973a 100644 --- a/irc/server.go +++ b/irc/server.go @@ -110,6 +110,7 @@ type clientConn struct { func NewServer(config *Config, logger *logger.Manager) (*Server, error) { // initialize data structures server := &Server{ + ctime: time.Now().UTC(), connectionLimiter: connection_limits.NewLimiter(), connectionThrottler: connection_limits.NewThrottler(), listeners: make(map[string]*ListenerWrapper), @@ -576,7 +577,6 @@ func (server *Server) rehash() error { func (server *Server) applyConfig(config *Config, initial bool) (err error) { if initial { - server.ctime = time.Now() server.configFilename = config.Filename server.name = config.Server.Name server.nameCasefolded = config.Server.nameCasefolded From 80a594802f74872b8c50c61c7c4cd82a68d96b52 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 12 May 2019 04:30:48 -0400 Subject: [PATCH 2/7] remove more indirections --- irc/connection_limits/limiter.go | 15 ++++----------- irc/connection_limits/throttler.go | 15 ++++----------- irc/connection_limits/throttler_test.go | 4 ++-- irc/monitor.go | 10 +++------- irc/server.go | 24 +++++++++++------------- irc/snomanager.go | 5 +---- 6 files changed, 25 insertions(+), 48 deletions(-) diff --git a/irc/connection_limits/limiter.go b/irc/connection_limits/limiter.go index d7572a94..8ce15a39 100644 --- a/irc/connection_limits/limiter.go +++ b/irc/connection_limits/limiter.go @@ -98,17 +98,6 @@ func (cl *Limiter) RemoveClient(addr net.IP) { } } -// NewLimiter returns a new connection limit handler. -// The handler is functional, but disabled; it can be enabled via `ApplyConfig`. -func NewLimiter() *Limiter { - var cl Limiter - - // initialize empty population; all other state is configurable - cl.population = make(map[string]int) - - return &cl -} - // ApplyConfig atomically applies a config update to a connection limit handler func (cl *Limiter) ApplyConfig(config LimiterConfig) error { // assemble exempted nets @@ -120,6 +109,10 @@ func (cl *Limiter) ApplyConfig(config LimiterConfig) error { cl.Lock() defer cl.Unlock() + if cl.population == nil { + cl.population = make(map[string]int) + } + cl.enabled = config.Enabled cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32) cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128) diff --git a/irc/connection_limits/throttler.go b/irc/connection_limits/throttler.go index 19f589c2..99a785ba 100644 --- a/irc/connection_limits/throttler.go +++ b/irc/connection_limits/throttler.go @@ -150,17 +150,6 @@ func (ct *Throttler) BanMessage() string { return ct.banMessage } -// NewThrottler returns a new client connection throttler. -// The throttler is functional, but disabled; it can be enabled via `ApplyConfig`. -func NewThrottler() *Throttler { - var ct Throttler - - // initialize empty population; all other state is configurable - ct.population = make(map[string]ThrottleDetails) - - return &ct -} - // ApplyConfig atomically applies a config update to a throttler func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error { // assemble exempted nets @@ -172,6 +161,10 @@ func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error { ct.Lock() defer ct.Unlock() + if ct.population == nil { + ct.population = make(map[string]ThrottleDetails) + } + ct.enabled = config.Enabled ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32) ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128) diff --git a/irc/connection_limits/throttler_test.go b/irc/connection_limits/throttler_test.go index c31c4276..08a79f89 100644 --- a/irc/connection_limits/throttler_test.go +++ b/irc/connection_limits/throttler_test.go @@ -72,9 +72,9 @@ func makeTestThrottler(v4len, v6len int) *Throttler { ConnectionsPerCidr: maxConnections, Duration: minute, } - throttler := NewThrottler() + var throttler Throttler throttler.ApplyConfig(config) - return throttler + return &throttler } func TestConnectionThrottle(t *testing.T) { diff --git a/irc/monitor.go b/irc/monitor.go index bf92b969..801d2202 100644 --- a/irc/monitor.go +++ b/irc/monitor.go @@ -19,13 +19,9 @@ type MonitorManager struct { // (all nicks must be normalized externally by casefolding) } -// NewMonitorManager returns a new MonitorManager. -func NewMonitorManager() *MonitorManager { - mm := MonitorManager{ - watching: make(map[*Client]map[string]bool), - watchedby: make(map[string]map[*Client]bool), - } - return &mm +func (mm *MonitorManager) Initialize() { + mm.watching = make(map[*Client]map[string]bool) + mm.watchedby = make(map[string]map[*Client]bool) } // AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line. diff --git a/irc/server.go b/irc/server.go index 6944973a..b8830a50 100644 --- a/irc/server.go +++ b/irc/server.go @@ -67,15 +67,15 @@ type Server struct { clients ClientManager config unsafe.Pointer configFilename string - connectionLimiter *connection_limits.Limiter - connectionThrottler *connection_limits.Throttler + connectionLimiter connection_limits.Limiter + connectionThrottler connection_limits.Throttler ctime time.Time dlines *DLineManager helpIndexManager HelpIndexManager klines *KLineManager listeners map[string]*ListenerWrapper logger *logger.Manager - monitorManager *MonitorManager + monitorManager MonitorManager name string nameCasefolded string rehashMutex sync.Mutex // tier 4 @@ -83,7 +83,7 @@ type Server struct { pprofServer *http.Server resumeManager ResumeManager signals chan os.Signal - snomasks *SnoManager + snomasks SnoManager store *buntdb.DB torLimiter connection_limits.TorLimiter whoWas WhoWasList @@ -110,21 +110,19 @@ type clientConn struct { func NewServer(config *Config, logger *logger.Manager) (*Server, error) { // initialize data structures server := &Server{ - ctime: time.Now().UTC(), - connectionLimiter: connection_limits.NewLimiter(), - connectionThrottler: connection_limits.NewThrottler(), - listeners: make(map[string]*ListenerWrapper), - logger: logger, - monitorManager: NewMonitorManager(), - rehashSignal: make(chan os.Signal, 1), - signals: make(chan os.Signal, len(ServerExitSignals)), - snomasks: NewSnoManager(), + ctime: time.Now().UTC(), + listeners: make(map[string]*ListenerWrapper), + logger: logger, + rehashSignal: make(chan os.Signal, 1), + signals: make(chan os.Signal, len(ServerExitSignals)), } server.clients.Initialize() server.semaphores.Initialize() server.resumeManager.Initialize(server) server.whoWas.Initialize(config.Limits.WhowasEntries) + server.monitorManager.Initialize() + server.snomasks.Initialize() if err := server.applyConfig(config, true); err != nil { return nil, err diff --git a/irc/snomanager.go b/irc/snomanager.go index d6f3b657..b7e48107 100644 --- a/irc/snomanager.go +++ b/irc/snomanager.go @@ -14,11 +14,8 @@ type SnoManager struct { sendLists map[sno.Mask]map[*Client]bool } -// NewSnoManager returns a new SnoManager -func NewSnoManager() *SnoManager { - var m SnoManager +func (m *SnoManager) Initialize() { m.sendLists = make(map[sno.Mask]map[*Client]bool) - return &m } // AddMasks adds the given snomasks to the client. From 0b55fed7c5053e1c2e744fcaaa1585bf81d9ff0b Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 12 May 2019 20:57:34 -0400 Subject: [PATCH 3/7] consolidate acceptClient into RunNewClient --- irc/client.go | 30 +++++++++++++++++++++++++----- irc/server.go | 27 +-------------------------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/irc/client.go b/irc/client.go index 45705f06..1801a7c6 100644 --- a/irc/client.go +++ b/irc/client.go @@ -163,8 +163,29 @@ type ClientDetails struct { accountName string } -// NewClient sets up a new client and runs its goroutine. -func RunNewClient(server *Server, conn clientConn) { +// RunClient sets up a new client and runs its goroutine. +func (server *Server) RunClient(conn clientConn) { + var isBanned bool + var banMsg string + var realIP net.IP + if conn.IsTor { + realIP = utils.IPv4LoopbackAddress + isBanned, banMsg = server.checkTorLimits() + } else { + realIP = utils.AddrToIP(conn.Conn.RemoteAddr()) + isBanned, banMsg = server.checkBans(realIP) + } + + if isBanned { + // this might not show up properly on some clients, + // but our objective here is just to close the connection out before it has a load impact on us + conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg))) + conn.Conn.Close() + return + } + + server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", realIP)) + now := time.Now().UTC() config := server.Config() fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest @@ -194,6 +215,7 @@ func RunNewClient(server *Server, conn clientConn) { capState: caps.NoneState, ctime: now, atime: now, + realIP: realIP, } session.SetMaxlenRest() client.sessions = []*Session{session} @@ -204,19 +226,17 @@ func RunNewClient(server *Server, conn clientConn) { client.certfp, _ = socket.CertFP() } - remoteAddr := conn.Conn.RemoteAddr() if conn.IsTor { client.SetMode(modes.TLS, true) - session.realIP = utils.AddrToIP(remoteAddr) // cover up details of the tor proxying infrastructure (not a user privacy concern, // but a hardening measure): session.proxiedIP = utils.IPv4LoopbackAddress session.rawHostname = config.Server.TorListeners.Vhost } else { - session.realIP = utils.AddrToIP(remoteAddr) // set the hostname for this client (may be overridden later by PROXY or WEBIRC) session.rawHostname = utils.LookupHostname(session.realIP.String()) client.cloakedHostname = config.Server.Cloaks.ComputeCloak(session.realIP) + remoteAddr := conn.Conn.RemoteAddr() if utils.AddrIsLocal(remoteAddr) { // treat local connections as secure (may be overridden later by WEBIRC) client.SetMode(modes.TLS, true) diff --git a/irc/server.go b/irc/server.go index b8830a50..814ddfe1 100644 --- a/irc/server.go +++ b/irc/server.go @@ -27,7 +27,6 @@ import ( "github.com/oragono/oragono/irc/logger" "github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/sno" - "github.com/oragono/oragono/irc/utils" "github.com/tidwall/buntdb" ) @@ -207,30 +206,6 @@ func (server *Server) Run() { } } -func (server *Server) acceptClient(conn clientConn) { - var isBanned bool - var banMsg string - var ipaddr net.IP - if conn.IsTor { - ipaddr = utils.IPv4LoopbackAddress - isBanned, banMsg = server.checkTorLimits() - } else { - ipaddr = utils.AddrToIP(conn.Conn.RemoteAddr()) - isBanned, banMsg = server.checkBans(ipaddr) - } - - if isBanned { - // this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us - conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg))) - conn.Conn.Close() - return - } - - server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr)) - - go RunNewClient(server, conn) -} - func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) { // check DLINEs isBanned, info := server.dlines.CheckIP(ipaddr) @@ -338,7 +313,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor b IsTor: isTor, } // hand off the connection - go server.acceptClient(newConn) + go server.RunClient(newConn) } if shouldStop { From 6ded2ea4669ad1e426a0bc9fa883163b3c9b1ad5 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 13 May 2019 00:39:59 -0400 Subject: [PATCH 4/7] fix #483 --- irc/channel.go | 10 ++++------ irc/client.go | 6 +++--- irc/handlers.go | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index 73ad7806..5e79163b 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -975,7 +975,6 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod nickmask := client.NickMaskString() account := client.AccountName() chname := channel.Name() - now := time.Now().UTC() // STATUSMSG targets are prefixed with the supplied min-prefix, e.g., @#channel if minPrefixMode != modes.Mode(0) { @@ -983,7 +982,6 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod } // send echo-message - // TODO this should use `now` as the time for consistency if rb.session.capabilities.Has(caps.EchoMessage) { var tagsToUse map[string]string if rb.session.capabilities.Has(caps.MessageTags) { @@ -1005,9 +1003,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod tagsToUse = clientOnlyTags } if histType == history.Tagmsg && session.capabilities.Has(caps.MessageTags) { - session.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, chname) + session.sendFromClientInternal(false, message.Time, message.Msgid, nickmask, account, tagsToUse, command, chname) } else { - session.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, chname, message) + session.sendSplitMsgFromClientInternal(false, nickmask, account, tagsToUse, command, chname, message) } } @@ -1030,9 +1028,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod } if histType == history.Tagmsg { - session.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, chname) + session.sendFromClientInternal(false, message.Time, message.Msgid, nickmask, account, tagsToUse, command, chname) } else { - session.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, chname, message) + session.sendSplitMsgFromClientInternal(false, nickmask, account, tagsToUse, command, chname, message) } } } diff --git a/irc/client.go b/irc/client.go index 1801a7c6..ea01b658 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1091,12 +1091,12 @@ func (client *Client) destroy(beingResumed bool, session *Session) { // SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client. // Adds account-tag to the line as well. -func (session *Session) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) { +func (session *Session) sendSplitMsgFromClientInternal(blocking bool, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) { if session.capabilities.Has(caps.MaxLine) || message.Wrapped == nil { - session.sendFromClientInternal(blocking, serverTime, message.Msgid, nickmask, accountName, tags, command, target, message.Message) + session.sendFromClientInternal(blocking, message.Time, message.Msgid, nickmask, accountName, tags, command, target, message.Message) } else { for _, messagePair := range message.Wrapped { - session.sendFromClientInternal(blocking, serverTime, messagePair.Msgid, nickmask, accountName, tags, command, target, messagePair.Message) + session.sendFromClientInternal(blocking, message.Time, messagePair.Msgid, nickmask, accountName, tags, command, target, messagePair.Message) } } } diff --git a/irc/handlers.go b/irc/handlers.go index 6b9a4f45..66ba8e35 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2019,7 +2019,7 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R session.sendFromClientInternal(false, splitMsg.Time, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick) } } else { - session.sendSplitMsgFromClientInternal(false, splitMsg.Time, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg) + session.sendSplitMsgFromClientInternal(false, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg) } } } @@ -2039,7 +2039,7 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R if histType == history.Tagmsg && rb.session.capabilities.Has(caps.MessageTags) { session.sendFromClientInternal(false, splitMsg.Time, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick) } else { - session.sendSplitMsgFromClientInternal(false, splitMsg.Time, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg) + session.sendSplitMsgFromClientInternal(false, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick, splitMsg) } } if histType != history.Notice && user.Away() { From 89a50d772c29bc898d3f64b6e78584ead59c5108 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 13 May 2019 00:40:59 -0400 Subject: [PATCH 5/7] WHO should leave the client idle Some clients seem to automatically poll for channel state --- irc/commands.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc/commands.go b/irc/commands.go index e058a3e7..c505721b 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -309,8 +309,9 @@ func init() { minParams: 4, }, "WHO": { - handler: whoHandler, - minParams: 1, + handler: whoHandler, + minParams: 1, + leaveClientIdle: true, }, "WHOIS": { handler: whoisHandler, From 316d45917dbcec1760c3608802a7874b373ee7a3 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 13 May 2019 01:54:50 -0400 Subject: [PATCH 6/7] pass the correct quit message when a proxied client is banned If you were banned and the ban was only detected when you proxied (because you were proxying from a DLINE'd IP), you'd get an incorrect quit message: `QUIT: Bad or unauthorized PROXY command`. This propagates the correct ban message as the quit line. --- irc/errors.go | 6 +----- irc/gateways.go | 31 +++++++++++++++---------------- irc/handlers.go | 8 +++++++- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/irc/errors.go b/irc/errors.go index 0a5c3c5f..1fa8e5f7 100644 --- a/irc/errors.go +++ b/irc/errors.go @@ -13,7 +13,6 @@ var ( errAccountAlreadyVerified = errors.New(`Account is already verified`) errAccountCantDropPrimaryNick = errors.New("Can't unreserve primary nickname") errAccountCreation = errors.New("Account could not be created") - errAccountCredUpdate = errors.New("Could not update password hash to new method") errAccountDoesNotExist = errors.New("Account does not exist") errAccountInvalidCredentials = errors.New("Invalid account credentials") errAccountBadPassphrase = errors.New(`Passphrase contains forbidden characters or is otherwise invalid`) @@ -28,7 +27,6 @@ var ( errCallbackFailed = errors.New("Account verification could not be sent") errCertfpAlreadyExists = errors.New(`An account already exists for your certificate fingerprint`) errChannelNotOwnedByAccount = errors.New("Channel not owned by the specified account") - errChannelDoesNotExist = errors.New("Channel does not exist") errChannelAlreadyRegistered = errors.New("Channel is already registered") errChannelNameInUse = errors.New(`Channel name in use`) errInvalidChannelName = errors.New(`Invalid channel name`) @@ -38,12 +36,10 @@ var ( errNicknameReserved = errors.New("nickname is reserved") errNoExistingBan = errors.New("Ban does not exist") errNoSuchChannel = errors.New(`No such channel`) - errRenamePrivsNeeded = errors.New(`Only chanops can rename channels`) errInsufficientPrivs = errors.New("Insufficient privileges") - errSaslFail = errors.New("SASL failed") - errResumeTokenAlreadySet = errors.New("Client was already assigned a resume token") errInvalidUsername = errors.New("Invalid username") errFeatureDisabled = errors.New(`That feature is disabled`) + errBanned = errors.New("IP or nickmask banned") errInvalidParams = errors.New("Invalid parameters") ) diff --git a/irc/gateways.go b/irc/gateways.go index 95f05aee..09a551ef 100644 --- a/irc/gateways.go +++ b/irc/gateways.go @@ -46,24 +46,22 @@ func (wc *webircConfig) Populate() (err error) { } // ApplyProxiedIP applies the given IP to the client. -func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls bool) (success bool) { +func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls bool) (err error, quitMsg string) { // PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself // is whitelisted: if client.isTor { - return false + return errBadProxyLine, "" } // ensure IP is sane parsedProxiedIP := net.ParseIP(proxiedIP).To16() if parsedProxiedIP == nil { - client.Quit(fmt.Sprintf(client.t("Proxied IP address is not valid: [%s]"), proxiedIP), session) - return false + return errBadProxyLine, fmt.Sprintf(client.t("Proxied IP address is not valid: [%s]"), proxiedIP) } isBanned, banMsg := client.server.checkBans(parsedProxiedIP) if isBanned { - client.Quit(banMsg, session) - return false + return errBanned, banMsg } // given IP is sane! override the client's current IP @@ -84,7 +82,7 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls boo client.certfp = "" client.SetMode(modes.TLS, tls) - return true + return nil, "" } // handle the PROXY command: http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt @@ -93,9 +91,13 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls boo // unfortunately, an ipv6 SOURCEIP can start with a double colon; in this case, // the message is invalid IRC and can't be parsed normally, hence the special handling. func handleProxyCommand(server *Server, client *Client, session *Session, line string) (err error) { + var quitMsg string defer func() { if err != nil { - client.Quit(client.t("Bad or unauthorized PROXY command"), session) + if quitMsg == "" { + quitMsg = client.t("Bad or unauthorized PROXY command") + } + client.Quit(quitMsg, session) } }() @@ -106,13 +108,10 @@ func handleProxyCommand(server *Server, client *Client, session *Session, line s if utils.IPInNets(client.realIP, server.Config().Server.proxyAllowedFromNets) { // assume PROXY connections are always secure - if client.ApplyProxiedIP(session, params[2], true) { - return nil - } else { - return errBadProxyLine - } + err, quitMsg = client.ApplyProxiedIP(session, params[2], true) + return + } else { + // real source IP is not authorized to issue PROXY: + return errBadGatewayAddress } - - // real source IP is not authorized to issue PROXY: - return errBadGatewayAddress } diff --git a/irc/handlers.go b/irc/handlers.go index 66ba8e35..9508304a 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2576,7 +2576,13 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re if strings.HasPrefix(proxiedIP, "[") && strings.HasSuffix(proxiedIP, "]") { proxiedIP = proxiedIP[1 : len(proxiedIP)-1] } - return !client.ApplyProxiedIP(rb.session, proxiedIP, secure) + err, quitMsg := client.ApplyProxiedIP(rb.session, proxiedIP, secure) + if err != nil { + client.Quit(quitMsg, rb.session) + return true + } else { + return false + } } } From 8f0977f59e13b8facf4f4ed2b64b8b40fa034748 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 13 May 2019 02:24:58 -0400 Subject: [PATCH 7/7] human-readable times for NS SESSIONS --- irc/nickserv.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc/nickserv.go b/irc/nickserv.go index 9a69c90f..85c01fd2 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -5,6 +5,7 @@ package irc import ( "fmt" + "time" "github.com/goshuirc/irc-go/ircfmt" @@ -612,7 +613,7 @@ func nsSessionsHandler(server *Server, client *Client, command string, params [] } nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String())) nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname)) - nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(IRCv3TimestampFormat))) - nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(IRCv3TimestampFormat))) + nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123))) + nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123))) } }