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

refactor rehash to rely more on server.config

This commit is contained in:
Shivaram Lingamneni 2018-07-16 03:46:40 -04:00
parent 1a5db02236
commit 1383190249
11 changed files with 135 additions and 233 deletions

View File

@ -515,8 +515,9 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
return return
} }
if len(topic) > client.server.limits.TopicLen { topicLimit := client.server.Limits().TopicLen
topic = topic[:client.server.limits.TopicLen] if len(topic) > topicLimit {
topic = topic[:topicLimit]
} }
channel.stateMutex.Lock() channel.stateMutex.Lock()

View File

@ -85,9 +85,9 @@ type Client struct {
// NewClient returns a client with all the appropriate info setup. // NewClient returns a client with all the appropriate info setup.
func NewClient(server *Server, conn net.Conn, isTLS bool) *Client { func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
now := time.Now() now := time.Now()
limits := server.Limits() config := server.Config()
fullLineLenLimit := limits.LineLen.Tags + limits.LineLen.Rest fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
socket := NewSocket(conn, fullLineLenLimit*2, server.MaxSendQBytes()) socket := NewSocket(conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
client := &Client{ client := &Client{
atime: now, atime: now,
authorized: server.Password() == nil, authorized: server.Password() == nil,
@ -112,7 +112,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
// error is not useful to us here anyways so we can ignore it // error is not useful to us here anyways so we can ignore it
client.certfp, _ = client.socket.CertFP() client.certfp, _ = client.socket.CertFP()
} }
if server.checkIdent && !utils.AddrIsUnix(conn.RemoteAddr()) { if config.Server.CheckIdent && !utils.AddrIsUnix(conn.RemoteAddr()) {
_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String()) _, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
serverPort, _ := strconv.Atoi(serverPortString) serverPort, _ := strconv.Atoi(serverPortString)
if err != nil { if err != nil {

View File

@ -28,10 +28,10 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
// PassConfig holds the connection password. // here's how this works: exported (capitalized) members of the config structs
type PassConfig struct { // are defined in the YAML file and deserialized directly from there. They may
Password string // be postprocessed and overwritten by LoadConfig. Unexported (lowercase) members
} // are derived from the exported members in LoadConfig.
// TLSListenConfig defines configuration options for listening on TLS. // TLSListenConfig defines configuration options for listening on TLS.
type TLSListenConfig struct { type TLSListenConfig struct {
@ -51,15 +51,6 @@ func (conf *TLSListenConfig) Config() (*tls.Config, error) {
}, err }, err
} }
// PasswordBytes returns the bytes represented by the password hash.
func (conf *PassConfig) PasswordBytes() []byte {
bytes, err := passwd.DecodePasswordHash(conf.Password)
if err != nil {
log.Fatal("decode password error: ", err)
}
return bytes
}
type AccountConfig struct { type AccountConfig struct {
Registration AccountRegistrationConfig Registration AccountRegistrationConfig
AuthenticationEnabled bool `yaml:"authentication-enabled"` AuthenticationEnabled bool `yaml:"authentication-enabled"`
@ -171,11 +162,24 @@ func (conf *OperConfig) PasswordBytes() []byte {
} }
// LineLenConfig controls line lengths. // LineLenConfig controls line lengths.
type LineLenConfig struct { type LineLenLimits struct {
Tags int Tags int
Rest int Rest int
} }
// Various server-enforced limits on data size.
type Limits struct {
AwayLen int `yaml:"awaylen"`
ChanListModes int `yaml:"chan-list-modes"`
ChannelLen int `yaml:"channellen"`
KickLen int `yaml:"kicklen"`
MonitorEntries int `yaml:"monitor-entries"`
NickLen int `yaml:"nicklen"`
TopicLen int `yaml:"topiclen"`
WhowasEntries int `yaml:"whowas-entries"`
LineLen LineLenLimits `yaml:"linelen"`
}
// STSConfig controls the STS configuration/ // STSConfig controls the STS configuration/
type STSConfig struct { type STSConfig struct {
Enabled bool Enabled bool
@ -219,9 +223,10 @@ type Config struct {
} }
Server struct { Server struct {
PassConfig
Password string Password string
passwordBytes []byte
Name string Name string
nameCasefolded string
Listen []string Listen []string
TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"` TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"`
STS STSConfig STS STSConfig
@ -251,14 +256,19 @@ type Config struct {
Accounts AccountConfig Accounts AccountConfig
Channels struct { Channels struct {
DefaultModes *string `yaml:"default-modes"` RawDefaultModes *string `yaml:"default-modes"`
Registration ChannelRegistrationConfig defaultModes modes.Modes
Registration ChannelRegistrationConfig
} }
OperClasses map[string]*OperClassConfig `yaml:"oper-classes"` OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
Opers map[string]*OperConfig Opers map[string]*OperConfig
// parsed operator definitions, unexported so they can't be defined
// directly in YAML:
operators map[string]*Oper
Logging []logger.LoggingConfig Logging []logger.LoggingConfig
Debug struct { Debug struct {
@ -267,17 +277,7 @@ type Config struct {
StackImpact StackImpactConfig StackImpact StackImpactConfig
} }
Limits struct { Limits Limits
AwayLen uint `yaml:"awaylen"`
ChanListModes uint `yaml:"chan-list-modes"`
ChannelLen uint `yaml:"channellen"`
KickLen uint `yaml:"kicklen"`
MonitorEntries uint `yaml:"monitor-entries"`
NickLen uint `yaml:"nicklen"`
TopicLen uint `yaml:"topiclen"`
WhowasEntries uint `yaml:"whowas-entries"`
LineLen LineLenConfig `yaml:"linelen"`
}
Fakelag FakelagConfig Fakelag FakelagConfig
@ -437,11 +437,6 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Filename = filename config.Filename = filename
// we need this so PasswordBytes returns the correct info
if config.Server.Password != "" {
config.Server.PassConfig.Password = config.Server.Password
}
if config.Network.Name == "" { if config.Network.Name == "" {
return nil, ErrNetworkNameMissing return nil, ErrNetworkNameMissing
} }
@ -691,5 +686,33 @@ func LoadConfig(filename string) (config *Config, err error) {
} }
} }
// casefold/validate server name
config.Server.nameCasefolded, err = Casefold(config.Server.Name)
if err != nil {
return nil, fmt.Errorf("Server name isn't valid [%s]: %s", config.Server.Name, err.Error())
}
// process operator definitions, store them to config.operators
operclasses, err := config.OperatorClasses()
if err != nil {
return nil, err
}
opers, err := config.Operators(operclasses)
if err != nil {
return nil, err
}
config.operators = opers
// parse default channel modes
config.Channels.defaultModes = ParseDefaultChannelModes(config.Channels.RawDefaultModes)
if config.Server.Password != "" {
bytes, err := passwd.DecodePasswordHash(config.Server.Password)
if err != nil {
return nil, err
}
config.Server.passwordBytes = bytes
}
return config, nil return config, nil
} }

View File

@ -267,7 +267,7 @@ func schemaChangeV2ToV3(config *Config, tx *buntdb.Tx) error {
} }
// explicitly store the channel modes // explicitly store the channel modes
defaultModes := ParseDefaultChannelModes(config) defaultModes := ParseDefaultChannelModes(config.Channels.RawDefaultModes)
modeStrings := make([]string, len(defaultModes)) modeStrings := make([]string, len(defaultModes))
for i, mode := range defaultModes { for i, mode := range defaultModes {
modeStrings[i] = string(mode) modeStrings[i] = string(mode)

View File

@ -6,15 +6,12 @@ package irc
import ( import (
"github.com/oragono/oragono/irc/isupport" "github.com/oragono/oragono/irc/isupport"
"github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/modes"
"sync/atomic"
) )
func (server *Server) MaxSendQBytes() int { func (server *Server) Config() *Config {
return int(atomic.LoadUint32(&server.maxSendQBytes)) server.configurableStateMutex.RLock()
} defer server.configurableStateMutex.RUnlock()
return server.config
func (server *Server) SetMaxSendQBytes(m int) {
atomic.StoreUint32(&server.maxSendQBytes, uint32(m))
} }
func (server *Server) ISupport() *isupport.List { func (server *Server) ISupport() *isupport.List {
@ -24,63 +21,41 @@ func (server *Server) ISupport() *isupport.List {
} }
func (server *Server) Limits() Limits { func (server *Server) Limits() Limits {
server.configurableStateMutex.RLock() return server.Config().Limits
defer server.configurableStateMutex.RUnlock()
return server.limits
} }
func (server *Server) Password() []byte { func (server *Server) Password() []byte {
server.configurableStateMutex.RLock() return server.Config().Server.passwordBytes
defer server.configurableStateMutex.RUnlock()
return server.password
} }
func (server *Server) RecoverFromErrors() bool { func (server *Server) RecoverFromErrors() bool {
server.configurableStateMutex.RLock() // default to true if unset
defer server.configurableStateMutex.RUnlock() rfe := server.Config().Debug.RecoverFromErrors
return server.recoverFromErrors return rfe == nil || *rfe
} }
func (server *Server) ProxyAllowedFrom() []string { func (server *Server) ProxyAllowedFrom() []string {
server.configurableStateMutex.RLock() return server.Config().Server.ProxyAllowedFrom
defer server.configurableStateMutex.RUnlock()
return server.proxyAllowedFrom
} }
func (server *Server) WebIRCConfig() []webircConfig { func (server *Server) WebIRCConfig() []webircConfig {
server.configurableStateMutex.RLock() return server.Config().Server.WebIRC
defer server.configurableStateMutex.RUnlock()
return server.webirc
} }
func (server *Server) DefaultChannelModes() modes.Modes { func (server *Server) DefaultChannelModes() modes.Modes {
server.configurableStateMutex.RLock() return server.Config().Channels.defaultModes
defer server.configurableStateMutex.RUnlock()
return server.defaultChannelModes
} }
func (server *Server) ChannelRegistrationEnabled() bool { func (server *Server) ChannelRegistrationEnabled() bool {
server.configurableStateMutex.RLock() return server.Config().Channels.Registration.Enabled
defer server.configurableStateMutex.RUnlock()
return server.config.Channels.Registration.Enabled
} }
func (server *Server) AccountConfig() *AccountConfig { func (server *Server) AccountConfig() *AccountConfig {
server.configurableStateMutex.RLock() return &server.Config().Accounts
defer server.configurableStateMutex.RUnlock()
if server.config == nil {
return nil
}
return &server.config.Accounts
} }
func (server *Server) FakelagConfig() *FakelagConfig { func (server *Server) FakelagConfig() *FakelagConfig {
server.configurableStateMutex.RLock() return &server.Config().Fakelag
defer server.configurableStateMutex.RUnlock()
if server.config == nil {
return nil
}
return &server.config.Fakelag
} }
func (server *Server) GetOperator(name string) (oper *Oper) { func (server *Server) GetOperator(name string) (oper *Oper) {
@ -90,7 +65,7 @@ func (server *Server) GetOperator(name string) (oper *Oper) {
} }
server.configurableStateMutex.RLock() server.configurableStateMutex.RLock()
defer server.configurableStateMutex.RUnlock() defer server.configurableStateMutex.RUnlock()
return server.operators[name] return server.config.operators[name]
} }
func (client *Client) Nick() string { func (client *Client) Nick() string {

View File

@ -1501,12 +1501,12 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
var online []string var online []string
var offline []string var offline []string
limit := server.Limits().MonitorEntries limits := server.Limits()
targets := strings.Split(msg.Params[1], ",") targets := strings.Split(msg.Params[1], ",")
for _, target := range targets { for _, target := range targets {
// check name length // check name length
if len(target) < 1 || len(targets) > server.limits.NickLen { if len(target) < 1 || len(targets) > limits.NickLen {
continue continue
} }
@ -1516,9 +1516,9 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
continue continue
} }
err = server.monitorManager.Add(client, casefoldedTarget, limit) err = server.monitorManager.Add(client, casefoldedTarget, limits.MonitorEntries)
if err == errMonitorLimitExceeded { if err == errMonitorLimitExceeded {
rb.Add(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ",")) rb.Add(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(limits.MonitorEntries), strings.Join(targets, ","))
break break
} else if err != nil { } else if err != nil {
continue continue
@ -1814,14 +1814,15 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
} }
// if no password exists, skip checking // if no password exists, skip checking
if len(server.password) == 0 { serverPassword := server.Password()
if serverPassword == nil {
client.SetAuthorized(true) client.SetAuthorized(true)
return false return false
} }
// check the provided password // check the provided password
password := []byte(msg.Params[0]) password := []byte(msg.Params[0])
if passwd.ComparePassword(server.password, password) != nil { if passwd.ComparePassword(serverPassword, password) != nil {
rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect")) rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect"))
rb.Add(nil, server.name, "ERROR", client.t("Password incorrect")) rb.Add(nil, server.name, "ERROR", client.t("Password incorrect"))
return true return true

View File

@ -87,12 +87,12 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool)
} }
// ParseDefaultChannelModes parses the `default-modes` line of the config // ParseDefaultChannelModes parses the `default-modes` line of the config
func ParseDefaultChannelModes(config *Config) modes.Modes { func ParseDefaultChannelModes(rawModes *string) modes.Modes {
if config.Channels.DefaultModes == nil { if rawModes == nil {
// not present in config, fall back to compile-time default // not present in config, fall back to compile-time default
return DefaultChannelModes return DefaultChannelModes
} }
modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ") modeChangeStrings := strings.Fields(*rawModes)
modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...) modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
defaultChannelModes := make(modes.Modes, 0) defaultChannelModes := make(modes.Modes, 0)
for _, modeChange := range modeChanges { for _, modeChange := range modeChanges {

View File

@ -27,10 +27,8 @@ func TestParseDefaultChannelModes(t *testing.T) {
{nil, modes.Modes{modes.NoOutside, modes.OpOnlyTopic}}, {nil, modes.Modes{modes.NoOutside, modes.OpOnlyTopic}},
} }
var config Config
for _, testcase := range parseTests { for _, testcase := range parseTests {
config.Channels.DefaultModes = testcase.raw result := ParseDefaultChannelModes(testcase.raw)
result := ParseDefaultChannelModes(&config)
if !reflect.DeepEqual(result, testcase.expected) { if !reflect.DeepEqual(result, testcase.expected) {
t.Errorf("expected modes %s, got %s", testcase.expected, result) t.Errorf("expected modes %s, got %s", testcase.expected, result)
} }

View File

@ -58,24 +58,6 @@ var (
CapValues = caps.NewValues() CapValues = caps.NewValues()
) )
// Limits holds the maximum limits for various things such as topic lengths.
type Limits struct {
AwayLen int
ChannelLen int
KickLen int
MonitorEntries int
NickLen int
TopicLen int
ChanListModes int
LineLen LineLenLimits
}
// LineLenLimits holds the maximum limits for IRC lines.
type LineLenLimits struct {
Tags int
Rest int
}
// ListenerWrapper wraps a listener so it can be safely reconfigured or stopped // ListenerWrapper wraps a listener so it can be safely reconfigured or stopped
type ListenerWrapper struct { type ListenerWrapper struct {
listener net.Listener listener net.Listener
@ -91,7 +73,6 @@ type Server struct {
batches *BatchManager batches *BatchManager
channels *ChannelManager channels *ChannelManager
channelRegistry *ChannelRegistry channelRegistry *ChannelRegistry
checkIdent bool
clients *ClientManager clients *ClientManager
config *Config config *Config
configFilename string configFilename string
@ -99,35 +80,23 @@ type Server struct {
connectionLimiter *connection_limits.Limiter connectionLimiter *connection_limits.Limiter
connectionThrottler *connection_limits.Throttler connectionThrottler *connection_limits.Throttler
ctime time.Time ctime time.Time
defaultChannelModes modes.Modes
dlines *DLineManager dlines *DLineManager
loggingRawIO bool
isupport *isupport.List isupport *isupport.List
klines *KLineManager klines *KLineManager
languages *languages.Manager languages *languages.Manager
limits Limits
listeners map[string]*ListenerWrapper listeners map[string]*ListenerWrapper
logger *logger.Manager logger *logger.Manager
maxSendQBytes uint32
monitorManager *MonitorManager monitorManager *MonitorManager
motdLines []string motdLines []string
name string name string
nameCasefolded string nameCasefolded string
networkName string
operators map[string]*Oper
operclasses map[string]*OperClass
password []byte
passwords *passwd.SaltedManager passwords *passwd.SaltedManager
recoverFromErrors bool
rehashMutex sync.Mutex // tier 4 rehashMutex sync.Mutex // tier 4
rehashSignal chan os.Signal rehashSignal chan os.Signal
pprofServer *http.Server pprofServer *http.Server
proxyAllowedFrom []string
signals chan os.Signal signals chan os.Signal
snomasks *SnoManager snomasks *SnoManager
store *buntdb.DB store *buntdb.DB
stsEnabled bool
webirc []webircConfig
whoWas *WhoWasList whoWas *WhoWasList
stats *Stats stats *Stats
semaphores *ServerSemaphores semaphores *ServerSemaphores
@ -204,35 +173,35 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
func (server *Server) setISupport() { func (server *Server) setISupport() {
maxTargetsString := strconv.Itoa(maxTargets) maxTargetsString := strconv.Itoa(maxTargets)
server.configurableStateMutex.RLock() config := server.Config()
// add RPL_ISUPPORT tokens // add RPL_ISUPPORT tokens
isupport := isupport.NewList() isupport := isupport.NewList()
isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen)) isupport.Add("AWAYLEN", strconv.Itoa(config.Limits.AwayLen))
isupport.Add("CASEMAPPING", "ascii") isupport.Add("CASEMAPPING", "ascii")
isupport.Add("CHANMODES", strings.Join([]string{modes.Modes{modes.BanMask, modes.ExceptMask, modes.InviteMask}.String(), "", modes.Modes{modes.UserLimit, modes.Key}.String(), modes.Modes{modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.ChanRoleplaying, modes.Secret}.String()}, ",")) isupport.Add("CHANMODES", strings.Join([]string{modes.Modes{modes.BanMask, modes.ExceptMask, modes.InviteMask}.String(), "", modes.Modes{modes.UserLimit, modes.Key}.String(), modes.Modes{modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.ChanRoleplaying, modes.Secret}.String()}, ","))
isupport.Add("CHANNELLEN", strconv.Itoa(server.limits.ChannelLen)) isupport.Add("CHANNELLEN", strconv.Itoa(config.Limits.ChannelLen))
isupport.Add("CHANTYPES", "#") isupport.Add("CHANTYPES", "#")
isupport.Add("ELIST", "U") isupport.Add("ELIST", "U")
isupport.Add("EXCEPTS", "") isupport.Add("EXCEPTS", "")
isupport.Add("INVEX", "") isupport.Add("INVEX", "")
isupport.Add("KICKLEN", strconv.Itoa(server.limits.KickLen)) isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(server.limits.ChanListModes))) isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
isupport.Add("MAXTARGETS", maxTargetsString) isupport.Add("MAXTARGETS", maxTargetsString)
isupport.Add("MODES", "") isupport.Add("MODES", "")
isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries)) isupport.Add("MONITOR", strconv.Itoa(config.Limits.MonitorEntries))
isupport.Add("NETWORK", server.networkName) isupport.Add("NETWORK", config.Network.Name)
isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen)) isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen))
isupport.Add("PREFIX", "(qaohv)~&@%+") isupport.Add("PREFIX", "(qaohv)~&@%+")
isupport.Add("RPCHAN", "E") isupport.Add("RPCHAN", "E")
isupport.Add("RPUSER", "E") isupport.Add("RPUSER", "E")
isupport.Add("STATUSMSG", "~&@%+") isupport.Add("STATUSMSG", "~&@%+")
isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString)) isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString))
isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen)) isupport.Add("TOPICLEN", strconv.Itoa(config.Limits.TopicLen))
isupport.Add("UTF8MAPPING", casemappingName) isupport.Add("UTF8MAPPING", casemappingName)
// account registration // account registration
if server.config.Accounts.Registration.Enabled { if config.Accounts.Registration.Enabled {
// 'none' isn't shown in the REGCALLBACKS vars // 'none' isn't shown in the REGCALLBACKS vars
var enabledCallbacks []string var enabledCallbacks []string
for _, name := range server.config.Accounts.Registration.EnabledCallbacks { for _, name := range server.config.Accounts.Registration.EnabledCallbacks {
@ -246,8 +215,6 @@ func (server *Server) setISupport() {
isupport.Add("REGCREDTYPES", "passphrase,certfp") isupport.Add("REGCREDTYPES", "passphrase,certfp")
} }
server.configurableStateMutex.RUnlock()
isupport.RegenerateCachedReply() isupport.RegenerateCachedReply()
server.configurableStateMutex.Lock() server.configurableStateMutex.Lock()
@ -672,7 +639,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, target.AccountName(), client.t("is logged in as")) rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, target.AccountName(), client.t("is logged in as"))
} }
if target.HasMode(modes.Bot) { if target.HasMode(modes.Bot) {
rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.networkName))) rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name)))
} }
if 0 < len(target.languages) { if 0 < len(target.languages) {
@ -744,13 +711,16 @@ func (server *Server) rehash() error {
return nil return nil
} }
func (server *Server) applyConfig(config *Config, initial bool) error { func (server *Server) applyConfig(config *Config, initial bool) (err error) {
if initial { if initial {
server.ctime = time.Now() server.ctime = time.Now()
server.configFilename = config.Filename server.configFilename = config.Filename
server.name = config.Server.Name
server.nameCasefolded = config.Server.nameCasefolded
} else { } else {
// enforce configs that can't be changed after launch: // enforce configs that can't be changed after launch:
if server.limits.LineLen.Tags != config.Limits.LineLen.Tags || server.limits.LineLen.Rest != config.Limits.LineLen.Rest { currentLimits := server.Limits()
if currentLimits.LineLen.Tags != config.Limits.LineLen.Tags || currentLimits.LineLen.Rest != config.Limits.LineLen.Rest {
return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted") return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted")
} else if server.name != config.Server.Name { } else if server.name != config.Server.Name {
return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted") return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
@ -759,48 +729,11 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
} }
} }
server.logger.Info("rehash", "Using config file", server.configFilename)
casefoldedName, err := Casefold(config.Server.Name)
if err != nil {
return fmt.Errorf("Server name isn't valid [%s]: %s", config.Server.Name, err.Error())
}
// confirm operator stuff all exists and is fine
operclasses, err := config.OperatorClasses()
if err != nil {
return fmt.Errorf("Error rehashing config file operclasses: %s", err.Error())
}
opers, err := config.Operators(operclasses)
if err != nil {
return fmt.Errorf("Error rehashing config file opers: %s", err.Error())
}
// TODO: support rehash of existing operator perms?
// sanity checks complete, start modifying server state // sanity checks complete, start modifying server state
server.logger.Info("rehash", "Using config file", server.configFilename)
oldConfig := server.Config()
if initial { // first, reload config sections for functionality implemented in subpackages:
server.name = config.Server.Name
server.nameCasefolded = casefoldedName
}
server.configurableStateMutex.Lock()
server.networkName = config.Network.Name
if config.Server.Password != "" {
server.password = config.Server.PasswordBytes()
} else {
server.password = nil
}
// apply new WebIRC command restrictions
server.webirc = config.Server.WebIRC
// apply new PROXY command restrictions
server.proxyAllowedFrom = config.Server.ProxyAllowedFrom
server.recoverFromErrors = true
if config.Debug.RecoverFromErrors != nil {
server.recoverFromErrors = *config.Debug.RecoverFromErrors
}
server.configurableStateMutex.Unlock()
err = server.connectionLimiter.ApplyConfig(config.Server.ConnectionLimiter) err = server.connectionLimiter.ApplyConfig(config.Server.ConnectionLimiter)
if err != nil { if err != nil {
@ -812,6 +745,16 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
return err return err
} }
// reload logging config
wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
err = server.logger.ApplyConfig(config.Logging)
if err != nil {
return err
}
nowLoggingRawIO := server.logger.IsLoggingRawIO()
// notify existing clients if raw i/o logging was enabled by a rehash
sendRawOutputNotice := !wasLoggingRawIO && nowLoggingRawIO
// setup new and removed caps // setup new and removed caps
addedCaps := caps.NewSet() addedCaps := caps.NewSet()
removedCaps := caps.NewSet() removedCaps := caps.NewSet()
@ -844,8 +787,7 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
server.languages = lm server.languages = lm
// SASL // SASL
oldAccountConfig := server.AccountConfig() authPreviouslyEnabled := oldConfig != nil && oldConfig.Accounts.AuthenticationEnabled
authPreviouslyEnabled := oldAccountConfig != nil && oldAccountConfig.AuthenticationEnabled
if config.Accounts.AuthenticationEnabled && !authPreviouslyEnabled { if config.Accounts.AuthenticationEnabled && !authPreviouslyEnabled {
// enabling SASL // enabling SASL
SupportedCapabilities.Enable(caps.SASL) SupportedCapabilities.Enable(caps.SASL)
@ -857,39 +799,39 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
removedCaps.Add(caps.SASL) removedCaps.Add(caps.SASL)
} }
nickReservationPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.NickReservation.Enabled nickReservationPreviouslyDisabled := oldConfig != nil && !oldConfig.Accounts.NickReservation.Enabled
nickReservationNowEnabled := config.Accounts.NickReservation.Enabled nickReservationNowEnabled := config.Accounts.NickReservation.Enabled
if nickReservationPreviouslyDisabled && nickReservationNowEnabled { if nickReservationPreviouslyDisabled && nickReservationNowEnabled {
server.accounts.buildNickToAccountIndex() server.accounts.buildNickToAccountIndex()
} }
hsPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.VHosts.Enabled hsPreviouslyDisabled := oldConfig != nil && !oldConfig.Accounts.VHosts.Enabled
hsNowEnabled := config.Accounts.VHosts.Enabled hsNowEnabled := config.Accounts.VHosts.Enabled
if hsPreviouslyDisabled && hsNowEnabled { if hsPreviouslyDisabled && hsNowEnabled {
server.accounts.initVHostRequestQueue() server.accounts.initVHostRequestQueue()
} }
// STS // STS
stsPreviouslyEnabled := oldConfig != nil && oldConfig.Server.STS.Enabled
stsValue := config.Server.STS.Value() stsValue := config.Server.STS.Value()
var stsDisabled bool stsDisabledByRehash := false
stsCurrentCapValue, _ := CapValues.Get(caps.STS) stsCurrentCapValue, _ := CapValues.Get(caps.STS)
server.logger.Debug("rehash", "STS Vals", stsCurrentCapValue, stsValue, fmt.Sprintf("server[%v] config[%v]", server.stsEnabled, config.Server.STS.Enabled)) server.logger.Debug("rehash", "STS Vals", stsCurrentCapValue, stsValue, fmt.Sprintf("server[%v] config[%v]", stsPreviouslyEnabled, config.Server.STS.Enabled))
if config.Server.STS.Enabled && !server.stsEnabled { if config.Server.STS.Enabled && !stsPreviouslyEnabled {
// enabling STS // enabling STS
SupportedCapabilities.Enable(caps.STS) SupportedCapabilities.Enable(caps.STS)
addedCaps.Add(caps.STS) addedCaps.Add(caps.STS)
CapValues.Set(caps.STS, stsValue) CapValues.Set(caps.STS, stsValue)
} else if !config.Server.STS.Enabled && server.stsEnabled { } else if !config.Server.STS.Enabled && stsPreviouslyEnabled {
// disabling STS // disabling STS
SupportedCapabilities.Disable(caps.STS) SupportedCapabilities.Disable(caps.STS)
removedCaps.Add(caps.STS) removedCaps.Add(caps.STS)
stsDisabled = true stsDisabledByRehash = true
} else if config.Server.STS.Enabled && server.stsEnabled && stsValue != stsCurrentCapValue { } else if config.Server.STS.Enabled && stsPreviouslyEnabled && stsValue != stsCurrentCapValue {
// STS policy updated // STS policy updated
CapValues.Set(caps.STS, stsValue) CapValues.Set(caps.STS, stsValue)
updatedCaps.Add(caps.STS) updatedCaps.Add(caps.STS)
} }
server.stsEnabled = config.Server.STS.Enabled
// burst new and removed caps // burst new and removed caps
var capBurstClients ClientSet var capBurstClients ClientSet
@ -912,7 +854,7 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
} }
for sClient := range capBurstClients { for sClient := range capBurstClients {
if stsDisabled { if stsDisabledByRehash {
// remove STS policy // remove STS policy
//TODO(dan): this is an ugly hack. we can write this better. //TODO(dan): this is an ugly hack. we can write this better.
stsPolicy := "sts=duration=0" stsPolicy := "sts=duration=0"
@ -932,44 +874,8 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
} }
} }
// set server options
server.configurableStateMutex.Lock()
lineLenConfig := LineLenLimits{
Tags: config.Limits.LineLen.Tags,
Rest: config.Limits.LineLen.Rest,
}
server.limits = Limits{
AwayLen: int(config.Limits.AwayLen),
ChannelLen: int(config.Limits.ChannelLen),
KickLen: int(config.Limits.KickLen),
MonitorEntries: int(config.Limits.MonitorEntries),
NickLen: int(config.Limits.NickLen),
TopicLen: int(config.Limits.TopicLen),
ChanListModes: int(config.Limits.ChanListModes),
LineLen: lineLenConfig,
}
server.operclasses = operclasses
server.operators = opers
server.checkIdent = config.Server.CheckIdent
server.defaultChannelModes = ParseDefaultChannelModes(config)
server.configurableStateMutex.Unlock()
// set new sendqueue size
server.SetMaxSendQBytes(config.Server.MaxSendQBytes)
server.loadMOTD(config.Server.MOTD, config.Server.MOTDFormatting) server.loadMOTD(config.Server.MOTD, config.Server.MOTDFormatting)
// reload logging config
err = server.logger.ApplyConfig(config.Logging)
if err != nil {
return err
}
nowLoggingRawIO := server.logger.IsLoggingRawIO()
// notify clients if raw i/o logging was enabled by a rehash
sendRawOutputNotice := !initial && !server.loggingRawIO && nowLoggingRawIO
server.loggingRawIO = nowLoggingRawIO
// save a pointer to the new config // save a pointer to the new config
server.configurableStateMutex.Lock() server.configurableStateMutex.Lock()
server.config = config server.config = config

View File

@ -52,11 +52,9 @@ func BitsetSet(set []uint64, position uint, on bool) (changed bool) {
} }
// BitsetEmpty returns whether the bitset is empty. // BitsetEmpty returns whether the bitset is empty.
// Right now, this is technically free of race conditions because we don't // This has false positives under concurrent modification (i.e., it can return true
// have a method that can simultaneously modify two bits separated by a word boundary // even though w.r.t. the sequence of atomic modifications, there was no point at
// such that one of those modifications is an unset. If we did, there would be a race // which the bitset was completely empty), but that's not how we're using this method.
// that could produce false positives. It's probably better to assume that they are
// already possible under concurrent modification (which is not how we're using this).
func BitsetEmpty(set []uint64) (empty bool) { func BitsetEmpty(set []uint64) (empty bool) {
for i := 0; i < len(set); i++ { for i := 0; i < len(set); i++ {
if atomic.LoadUint64(&set[i]) != 0 { if atomic.LoadUint64(&set[i]) != 0 {

View File

@ -32,7 +32,7 @@ type WhoWas struct {
} }
// NewWhoWasList returns a new WhoWasList // NewWhoWasList returns a new WhoWasList
func NewWhoWasList(size uint) *WhoWasList { func NewWhoWasList(size int) *WhoWasList {
return &WhoWasList{ return &WhoWasList{
buffer: make([]WhoWas, size), buffer: make([]WhoWas, size),
start: -1, start: -1,