3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 10:42:52 +01:00

RESUME should end cap negotiation without requiring CAP LS

This commit is contained in:
Shivaram Lingamneni 2019-02-09 20:01:47 -05:00
parent fef58bb935
commit 35abe7940a
3 changed files with 67 additions and 68 deletions

View File

@ -374,34 +374,17 @@ func (client *Client) Ping() {
} }
// Register sets the client details as appropriate when entering the network. // tryResume tries to resume if the client asked us to.
func (client *Client) Register() { func (client *Client) tryResume() (success bool) {
client.stateMutex.Lock()
alreadyRegistered := client.registered
client.registered = true
client.stateMutex.Unlock()
if alreadyRegistered {
return
}
// apply resume details if we're able to.
client.TryResume()
// finish registration
client.updateNickMask()
client.server.monitorManager.AlertAbout(client, true)
}
// TryResume tries to resume if the client asked us to.
func (client *Client) TryResume() {
if client.resumeDetails == nil {
return
}
server := client.server server := client.server
config := server.Config() config := server.Config()
defer func() {
if !success {
client.resumeDetails = nil
}
}()
oldnick := client.resumeDetails.OldNick oldnick := client.resumeDetails.OldNick
timestamp := client.resumeDetails.Timestamp timestamp := client.resumeDetails.Timestamp
var timestampString string var timestampString string
@ -412,7 +395,6 @@ func (client *Client) TryResume() {
oldClient := server.clients.Get(oldnick) oldClient := server.clients.Get(oldnick)
if oldClient == nil { if oldClient == nil {
client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old client not found")) client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old client not found"))
client.resumeDetails = nil
return return
} }
oldNick := oldClient.Nick() oldNick := oldClient.Nick()
@ -421,24 +403,23 @@ func (client *Client) TryResume() {
resumeAllowed := config.Server.AllowPlaintextResume || (oldClient.HasMode(modes.TLS) && client.HasMode(modes.TLS)) resumeAllowed := config.Server.AllowPlaintextResume || (oldClient.HasMode(modes.TLS) && client.HasMode(modes.TLS))
if !resumeAllowed { if !resumeAllowed {
client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old and new clients must have TLS")) client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old and new clients must have TLS"))
client.resumeDetails = nil
return return
} }
oldResumeToken := oldClient.ResumeToken() oldResumeToken := oldClient.ResumeToken()
if oldResumeToken == "" || !utils.SecretTokensMatch(oldResumeToken, client.resumeDetails.PresentedToken) { if oldResumeToken == "" || !utils.SecretTokensMatch(oldResumeToken, client.resumeDetails.PresentedToken) {
client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection, invalid resume token")) client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection, invalid resume token"))
client.resumeDetails = nil
return return
} }
err := server.clients.Resume(client, oldClient) err := server.clients.Resume(client, oldClient)
if err != nil { if err != nil {
client.resumeDetails = nil
client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection")) client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection"))
return return
} }
success = true
// this is a bit racey // this is a bit racey
client.resumeDetails.ResumedAt = time.Now() client.resumeDetails.ResumedAt = time.Now()
@ -520,13 +501,11 @@ func (client *Client) TryResume() {
client.Send(nil, client.server.name, "RESUME", "SUCCESS", oldNick) client.Send(nil, client.server.name, "RESUME", "SUCCESS", oldNick)
// after we send the rest of the registration burst, we'll try rejoining channels // after we send the rest of the registration burst, we'll try rejoining channels
return
} }
func (client *Client) tryResumeChannels() { func (client *Client) tryResumeChannels() {
details := client.resumeDetails details := client.resumeDetails
if details == nil {
return
}
channels := make([]*Channel, len(details.Channels)) channels := make([]*Channel, len(details.Channels))
for _, name := range details.Channels { for _, name := range details.Channels {

View File

@ -127,6 +127,16 @@ func (client *Client) Registered() bool {
return client.registered return client.registered
} }
func (client *Client) SetRegistered() {
// `registered` is only written from the client's own goroutine, but may be
// read from other goroutines; therefore, the client's own goroutine may read
// the value without synchronization, but must write it with synchronization,
// and other goroutines must read it with synchronization
client.stateMutex.Lock()
client.registered = true
client.stateMutex.Unlock()
}
func (client *Client) Destroyed() bool { func (client *Client) Destroyed() bool {
client.stateMutex.RLock() client.stateMutex.RLock()
defer client.stateMutex.RUnlock() defer client.stateMutex.RUnlock()

View File

@ -384,49 +384,58 @@ func (server *Server) generateMessageID() string {
// //
func (server *Server) tryRegister(c *Client) { func (server *Server) tryRegister(c *Client) {
if c.registered { resumed := false
return // try to complete registration, either via RESUME token or normally
if c.resumeDetails != nil {
if !c.tryResume() {
return
}
resumed = true
} else {
if c.preregNick == "" || !c.HasUsername() || c.capState == caps.NegotiatingState {
return
}
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
// before completing the other registration commands
config := server.Config()
if !c.isAuthorized(config) {
c.Quit(c.t("Bad password"))
c.destroy(false)
return
}
rb := NewResponseBuffer(c)
nickAssigned := performNickChange(server, c, c, c.preregNick, rb)
rb.Send(true)
if !nickAssigned {
c.preregNick = ""
return
}
// check KLINEs
isBanned, info := server.klines.CheckMasks(c.AllNickmasks()...)
if isBanned {
c.Quit(info.BanMessage(c.t("You are banned from this server (%s)")))
c.destroy(false)
return
}
} }
if c.preregNick == "" || !c.HasUsername() || c.capState == caps.NegotiatingState { // registration has succeeded:
return c.SetRegistered()
}
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
// before completing the other registration commands
config := server.Config()
if !c.isAuthorized(config) {
c.Quit(c.t("Bad password"))
c.destroy(false)
return
}
rb := NewResponseBuffer(c)
nickAssigned := performNickChange(server, c, c, c.preregNick, rb)
rb.Send(true)
if !nickAssigned {
c.preregNick = ""
return
}
// check KLINEs
isBanned, info := server.klines.CheckMasks(c.AllNickmasks()...)
if isBanned {
c.Quit(info.BanMessage(c.t("You are banned from this server (%s)")))
c.destroy(false)
return
}
// count new user in statistics // count new user in statistics
server.stats.ChangeTotal(1) server.stats.ChangeTotal(1)
if !resumed {
server.monitorManager.AlertAbout(c, true)
}
// continue registration // continue registration
server.logger.Debug("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname)) server.logger.Debug("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", c.nick, c.username, c.rawHostname, c.IPString(), c.realname)) server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", c.nick, c.username, c.rawHostname, c.IPString(), c.realname))
// "register"; this includes the initial phase of session resumption
c.Register()
// send welcome text // send welcome text
//NOTE(dan): we specifically use the NICK here instead of the nickmask //NOTE(dan): we specifically use the NICK here instead of the nickmask
// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask // see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
@ -436,7 +445,7 @@ func (server *Server) tryRegister(c *Client) {
//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter //TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString) c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
rb = NewResponseBuffer(c) rb := NewResponseBuffer(c)
c.RplISupport(rb) c.RplISupport(rb)
server.MOTD(c, rb) server.MOTD(c, rb)
rb.Send(true) rb.Send(true)
@ -449,8 +458,9 @@ func (server *Server) tryRegister(c *Client) {
c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
} }
// if resumed, send fake channel joins if resumed {
c.tryResumeChannels() c.tryResumeChannels()
}
} }
// t returns the translated version of the given string, based on the languages configured by the client. // t returns the translated version of the given string, based on the languages configured by the client.