mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 02:32:39 +01:00
parent
a742ef9639
commit
1a98a37a75
@ -243,6 +243,22 @@ server:
|
||||
# max-concurrent-connections: 128
|
||||
# max-connections-per-window: 1024
|
||||
|
||||
# pluggable IP ban mechanism, via subprocess invocation
|
||||
# this can be used to check new connections against a DNSBL, for example
|
||||
# see the manual for details on how to write an IP ban checking script
|
||||
ip-check-script:
|
||||
enabled: false
|
||||
command: "/usr/local/bin/check-ip-ban"
|
||||
# constant list of args to pass to the command; the actual query
|
||||
# and result are transmitted over stdin/stdout:
|
||||
args: []
|
||||
# timeout for process execution, after which we send a SIGTERM:
|
||||
timeout: 9s
|
||||
# how long after the SIGTERM before we follow up with a SIGKILL:
|
||||
kill-timeout: 1s
|
||||
# how many scripts are allowed to run at once? 0 for no limit:
|
||||
max-concurrency: 64
|
||||
|
||||
# IP cloaking hides users' IP addresses from other users and from channel admins
|
||||
# (but not from server admins), while still allowing channel admins to ban
|
||||
# offending IP addresses or networks. In place of hostnames derived from reverse
|
||||
@ -483,6 +499,8 @@ accounts:
|
||||
timeout: 9s
|
||||
# how long after the SIGTERM before we follow up with a SIGKILL:
|
||||
kill-timeout: 1s
|
||||
# how many scripts are allowed to run at once? 0 for no limit:
|
||||
max-concurrency: 64
|
||||
|
||||
# channel options
|
||||
channels:
|
||||
|
18
default.yaml
18
default.yaml
@ -270,6 +270,22 @@ server:
|
||||
# max-concurrent-connections: 128
|
||||
# max-connections-per-window: 1024
|
||||
|
||||
# pluggable IP ban mechanism, via subprocess invocation
|
||||
# this can be used to check new connections against a DNSBL, for example
|
||||
# see the manual for details on how to write an IP ban checking script
|
||||
ip-check-script:
|
||||
enabled: false
|
||||
command: "/usr/local/bin/check-ip-ban"
|
||||
# constant list of args to pass to the command; the actual query
|
||||
# and result are transmitted over stdin/stdout:
|
||||
args: []
|
||||
# timeout for process execution, after which we send a SIGTERM:
|
||||
timeout: 9s
|
||||
# how long after the SIGTERM before we follow up with a SIGKILL:
|
||||
kill-timeout: 1s
|
||||
# how many scripts are allowed to run at once? 0 for no limit:
|
||||
max-concurrency: 64
|
||||
|
||||
# IP cloaking hides users' IP addresses from other users and from channel admins
|
||||
# (but not from server admins), while still allowing channel admins to ban
|
||||
# offending IP addresses or networks. In place of hostnames derived from reverse
|
||||
@ -511,6 +527,8 @@ accounts:
|
||||
timeout: 9s
|
||||
# how long after the SIGTERM before we follow up with a SIGKILL:
|
||||
kill-timeout: 1s
|
||||
# how many scripts are allowed to run at once? 0 for no limit:
|
||||
max-concurrency: 64
|
||||
|
||||
# channel options
|
||||
channels:
|
||||
|
@ -1095,7 +1095,7 @@ func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName s
|
||||
config := am.server.Config()
|
||||
if config.Accounts.AuthScript.Enabled {
|
||||
var output AuthScriptOutput
|
||||
output, err = CheckAuthScript(config.Accounts.AuthScript,
|
||||
output, err = CheckAuthScript(am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
|
||||
AuthScriptInput{AccountName: accountName, Passphrase: passphrase, IP: client.IP().String()})
|
||||
if err != nil {
|
||||
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
||||
@ -1494,7 +1494,7 @@ func (am *AccountManager) AuthenticateByCertFP(client *Client, certfp, authzid s
|
||||
config := am.server.Config()
|
||||
if config.Accounts.AuthScript.Enabled {
|
||||
var output AuthScriptOutput
|
||||
output, err = CheckAuthScript(config.Accounts.AuthScript,
|
||||
output, err = CheckAuthScript(am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
|
||||
AuthScriptInput{Certfp: certfp, IP: client.IP().String()})
|
||||
if err != nil {
|
||||
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
||||
|
@ -4,13 +4,11 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
"net"
|
||||
|
||||
"github.com/oragono/oragono/irc/utils"
|
||||
)
|
||||
|
||||
// JSON-serializable input and output types for the script
|
||||
@ -27,84 +25,77 @@ type AuthScriptOutput struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// internal tupling of output and error for passing over a channel
|
||||
type authScriptResponse struct {
|
||||
output AuthScriptOutput
|
||||
err error
|
||||
}
|
||||
func CheckAuthScript(sem utils.Semaphore, config ScriptConfig, input AuthScriptInput) (output AuthScriptOutput, err error) {
|
||||
if sem != nil {
|
||||
sem.Acquire()
|
||||
defer sem.Release()
|
||||
}
|
||||
|
||||
func CheckAuthScript(config AuthScriptConfig, input AuthScriptInput) (output AuthScriptOutput, err error) {
|
||||
inputBytes, err := json.Marshal(input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(config.Command, config.Args...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
err = json.Unmarshal(outBytes, &output)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
channel := make(chan authScriptResponse, 1)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
if output.Error != "" {
|
||||
err = fmt.Errorf("Authentication process reported error: %s", output.Error)
|
||||
}
|
||||
stdin.Write(inputBytes)
|
||||
stdin.Write([]byte{'\n'})
|
||||
|
||||
// lots of potential race conditions here. we want to ensure that Wait()
|
||||
// will be called, and will return, on the other goroutine, no matter
|
||||
// where it is blocked. If it's blocked on ReadBytes(), we will kill it
|
||||
// (first with SIGTERM, then with SIGKILL) and ReadBytes will return
|
||||
// with EOF. If it's blocked on Wait(), then one of the kill signals
|
||||
// will succeed and unblock it.
|
||||
go processAuthScriptOutput(cmd, stdout, channel)
|
||||
outputTimer := time.NewTimer(config.Timeout)
|
||||
select {
|
||||
case response := <-channel:
|
||||
return response.output, response.err
|
||||
case <-outputTimer.C:
|
||||
}
|
||||
|
||||
err = errTimedOut
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
termTimer := time.NewTimer(config.Timeout)
|
||||
select {
|
||||
case <-channel:
|
||||
return
|
||||
case <-termTimer.C:
|
||||
}
|
||||
|
||||
cmd.Process.Kill()
|
||||
return
|
||||
}
|
||||
|
||||
func processAuthScriptOutput(cmd *exec.Cmd, stdout io.Reader, channel chan authScriptResponse) {
|
||||
var response authScriptResponse
|
||||
var out AuthScriptOutput
|
||||
type IPScriptResult uint
|
||||
|
||||
reader := bufio.NewReader(stdout)
|
||||
outBytes, err := reader.ReadBytes('\n')
|
||||
if err == nil {
|
||||
err = json.Unmarshal(outBytes, &out)
|
||||
if err == nil {
|
||||
response.output = out
|
||||
if out.Error != "" {
|
||||
err = fmt.Errorf("Authentication process reported error: %s", out.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
response.err = err
|
||||
const (
|
||||
IPNotChecked IPScriptResult = 0
|
||||
IPAccepted IPScriptResult = 1
|
||||
IPBanned IPScriptResult = 2
|
||||
IPRequireSASL IPScriptResult = 3
|
||||
)
|
||||
|
||||
// always call Wait() to ensure resource cleanup
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
response.err = err
|
||||
}
|
||||
|
||||
channel <- response
|
||||
type IPScriptInput struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type IPScriptOutput struct {
|
||||
Result IPScriptResult `json:"result"`
|
||||
BanMessage string `json:"banMessage"`
|
||||
// for caching: the network to which this result is applicable, and a TTL in seconds:
|
||||
CacheNet string `json:"cacheNet"`
|
||||
CacheSeconds int `json:"cacheSeconds"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func CheckIPBan(sem utils.Semaphore, config ScriptConfig, addr net.IP) (output IPScriptOutput, err error) {
|
||||
if sem != nil {
|
||||
sem.Acquire()
|
||||
defer sem.Release()
|
||||
}
|
||||
|
||||
inputBytes, err := json.Marshal(IPScriptInput{IP: addr.String()})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(outBytes, &output)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if output.Error != "" {
|
||||
err = fmt.Errorf("IP ban process reported error: %s", output.Error)
|
||||
} else if !(IPAccepted <= output.Result && output.Result <= IPRequireSASL) {
|
||||
err = fmt.Errorf("Invalid result from IP checking script: %d", output.Result)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ type Client struct {
|
||||
cloakedHostname string
|
||||
realname string
|
||||
realIP net.IP
|
||||
requireSASL bool
|
||||
registered bool
|
||||
registrationTimer *time.Timer
|
||||
resumeID string
|
||||
@ -297,8 +298,9 @@ type ClientDetails struct {
|
||||
|
||||
// RunClient sets up a new client and runs its goroutine.
|
||||
func (server *Server) RunClient(conn IRCConn) {
|
||||
config := server.Config()
|
||||
wConn := conn.UnderlyingConn()
|
||||
var isBanned bool
|
||||
var isBanned, requireSASL bool
|
||||
var banMsg string
|
||||
realIP := utils.AddrToIP(wConn.RemoteAddr())
|
||||
var proxiedIP net.IP
|
||||
@ -313,7 +315,10 @@ func (server *Server) RunClient(conn IRCConn) {
|
||||
proxiedIP = wConn.ProxiedIP
|
||||
ipToCheck = proxiedIP
|
||||
}
|
||||
isBanned, banMsg = server.checkBans(ipToCheck)
|
||||
// XXX only run the check script now if the IP cannot be replaced by PROXY or WEBIRC,
|
||||
// otherwise we'll do it in ApplyProxiedIP.
|
||||
checkScripts := proxiedIP != nil || !utils.IPInNets(realIP, config.Server.proxyAllowedFromNets)
|
||||
isBanned, requireSASL, banMsg = server.checkBans(config, ipToCheck, checkScripts)
|
||||
}
|
||||
|
||||
if isBanned {
|
||||
@ -327,7 +332,6 @@ func (server *Server) RunClient(conn IRCConn) {
|
||||
server.logger.Info("connect-ip", fmt.Sprintf("Client connecting: real IP %v, proxied IP %v", realIP, proxiedIP))
|
||||
|
||||
now := time.Now().UTC()
|
||||
config := server.Config()
|
||||
// give them 1k of grace over the limit:
|
||||
socket := NewSocket(conn, config.Server.MaxSendQBytes)
|
||||
client := &Client{
|
||||
@ -347,6 +351,7 @@ func (server *Server) RunClient(conn IRCConn) {
|
||||
nickMaskString: "*", // * is used until actual nick is given
|
||||
realIP: realIP,
|
||||
proxiedIP: proxiedIP,
|
||||
requireSASL: requireSASL,
|
||||
}
|
||||
client.writerSemaphore.Initialize(1)
|
||||
client.history.Initialize(config.History.ClientLength, time.Duration(config.History.AutoresizeWindow))
|
||||
@ -554,7 +559,7 @@ const (
|
||||
authFailSaslRequired
|
||||
)
|
||||
|
||||
func (client *Client) isAuthorized(server *Server, config *Config, session *Session) AuthOutcome {
|
||||
func (client *Client) isAuthorized(server *Server, config *Config, session *Session, forceRequireSASL bool) AuthOutcome {
|
||||
saslSent := client.account != ""
|
||||
// PASS requirement
|
||||
if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) {
|
||||
@ -565,7 +570,7 @@ func (client *Client) isAuthorized(server *Server, config *Config, session *Sess
|
||||
return authFailTorSaslRequired
|
||||
}
|
||||
// finally, enforce require-sasl
|
||||
if !saslSent && (config.Accounts.RequireSasl.Enabled || server.Defcon() <= 2) &&
|
||||
if !saslSent && (forceRequireSASL || config.Accounts.RequireSasl.Enabled || server.Defcon() <= 2) &&
|
||||
!utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
|
||||
return authFailSaslRequired
|
||||
}
|
||||
|
@ -282,13 +282,18 @@ type AccountConfig struct {
|
||||
AuthScript AuthScriptConfig `yaml:"auth-script"`
|
||||
}
|
||||
|
||||
type ScriptConfig struct {
|
||||
Enabled bool
|
||||
Command string
|
||||
Args []string
|
||||
Timeout time.Duration
|
||||
KillTimeout time.Duration `yaml:"kill-timeout"`
|
||||
MaxConcurrency uint `yaml:"max-concurrency"`
|
||||
}
|
||||
|
||||
type AuthScriptConfig struct {
|
||||
Enabled bool
|
||||
Command string
|
||||
Args []string
|
||||
Autocreate bool
|
||||
Timeout time.Duration
|
||||
KillTimeout time.Duration `yaml:"kill-timeout"`
|
||||
ScriptConfig `yaml:",inline"`
|
||||
Autocreate bool
|
||||
}
|
||||
|
||||
// AccountRegistrationConfig controls account registration.
|
||||
@ -526,8 +531,9 @@ type Config struct {
|
||||
supportedCaps *caps.Set
|
||||
capValues caps.Values
|
||||
Casemapping Casemapping
|
||||
EnforceUtf8 bool `yaml:"enforce-utf8"`
|
||||
OutputPath string `yaml:"output-path"`
|
||||
EnforceUtf8 bool `yaml:"enforce-utf8"`
|
||||
OutputPath string `yaml:"output-path"`
|
||||
IPCheckScript ScriptConfig `yaml:"ip-check-script"`
|
||||
}
|
||||
|
||||
Roleplay struct {
|
||||
|
@ -77,10 +77,11 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP net.IP, tls boo
|
||||
}
|
||||
proxiedIP = proxiedIP.To16()
|
||||
|
||||
isBanned, banMsg := client.server.checkBans(proxiedIP)
|
||||
isBanned, requireSASL, banMsg := client.server.checkBans(client.server.Config(), proxiedIP, true)
|
||||
if isBanned {
|
||||
return errBanned, banMsg
|
||||
}
|
||||
client.requireSASL = requireSASL
|
||||
// successfully added a limiter entry for the proxied IP;
|
||||
// remove the entry for the real IP if applicable (#197)
|
||||
client.server.connectionLimiter.RemoveClient(session.realIP)
|
||||
|
83
irc/script.go
Normal file
83
irc/script.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2020 Shivaram Lingamneni
|
||||
// released under the MIT license
|
||||
|
||||
package irc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// general-purpose scripting API for oragono "plugins"
|
||||
// invoke a command, send it a single newline-terminated string of bytes (typically JSON)
|
||||
// get back another newline-terminated string of bytes (or an error)
|
||||
|
||||
// internal tupling of output and error for passing over a channel
|
||||
type scriptResponse struct {
|
||||
output []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func RunScript(command string, args []string, input []byte, timeout, killTimeout time.Duration) (output []byte, err error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
channel := make(chan scriptResponse, 1)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stdin.Write(input)
|
||||
stdin.Write([]byte{'\n'})
|
||||
|
||||
// lots of potential race conditions here. we want to ensure that Wait()
|
||||
// will be called, and will return, on the other goroutine, no matter
|
||||
// where it is blocked. If it's blocked on ReadBytes(), we will kill it
|
||||
// (first with SIGTERM, then with SIGKILL) and ReadBytes will return
|
||||
// with EOF. If it's blocked on Wait(), then one of the kill signals
|
||||
// will succeed and unblock it.
|
||||
go processScriptOutput(cmd, stdout, channel)
|
||||
outputTimer := time.NewTimer(timeout)
|
||||
select {
|
||||
case response := <-channel:
|
||||
return response.output, response.err
|
||||
case <-outputTimer.C:
|
||||
}
|
||||
|
||||
err = errTimedOut
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
termTimer := time.NewTimer(killTimeout)
|
||||
select {
|
||||
case <-channel:
|
||||
return
|
||||
case <-termTimer.C:
|
||||
}
|
||||
|
||||
cmd.Process.Kill()
|
||||
return
|
||||
}
|
||||
|
||||
func processScriptOutput(cmd *exec.Cmd, stdout io.Reader, channel chan scriptResponse) {
|
||||
var response scriptResponse
|
||||
|
||||
reader := bufio.NewReader(stdout)
|
||||
response.output, response.err = reader.ReadBytes('\n')
|
||||
|
||||
// always call Wait() to ensure resource cleanup
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
response.err = err
|
||||
}
|
||||
|
||||
channel <- response
|
||||
}
|
@ -27,6 +27,8 @@ type ServerSemaphores struct {
|
||||
// each distinct operation MUST have its own semaphore;
|
||||
// methods that acquire a semaphore MUST NOT call methods that acquire another
|
||||
ClientDestroy utils.Semaphore
|
||||
IPCheckScript utils.Semaphore
|
||||
AuthScript utils.Semaphore
|
||||
}
|
||||
|
||||
// Initialize initializes a set of server semaphores.
|
||||
|
@ -150,10 +150,10 @@ func (server *Server) Run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
||||
func (server *Server) checkBans(config *Config, ipaddr net.IP, checkScripts bool) (banned bool, requireSASL bool, message string) {
|
||||
if server.Defcon() == 1 {
|
||||
if !(ipaddr.IsLoopback() || utils.IPInNets(ipaddr, server.Config().Server.secureNets)) {
|
||||
return true, "New connections to this server are temporarily restricted"
|
||||
return true, false, "New connections to this server are temporarily restricted"
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
||||
isBanned, info := server.dlines.CheckIP(ipaddr)
|
||||
if isBanned {
|
||||
server.logger.Info("connect-ip", fmt.Sprintf("Client from %v rejected by d-line", ipaddr))
|
||||
return true, info.BanMessage("You are banned from this server (%s)")
|
||||
return true, false, info.BanMessage("You are banned from this server (%s)")
|
||||
}
|
||||
|
||||
// check connection limits
|
||||
@ -169,27 +169,55 @@ func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
||||
if err == connection_limits.ErrLimitExceeded {
|
||||
// too many connections from one client, tell the client and close the connection
|
||||
server.logger.Info("connect-ip", fmt.Sprintf("Client from %v rejected for connection limit", ipaddr))
|
||||
return true, "Too many clients from your network"
|
||||
return true, false, "Too many clients from your network"
|
||||
} else if err == connection_limits.ErrThrottleExceeded {
|
||||
duration := server.Config().Server.IPLimits.BanDuration
|
||||
if duration == 0 {
|
||||
return false, ""
|
||||
duration := config.Server.IPLimits.BanDuration
|
||||
if duration != 0 {
|
||||
server.dlines.AddIP(ipaddr, duration, throttleMessage,
|
||||
"Exceeded automated connection throttle", "auto.connection.throttler")
|
||||
// they're DLINE'd for 15 minutes or whatever, so we can reset the connection throttle now,
|
||||
// and once their temporary DLINE is finished they can fill up the throttler again
|
||||
server.connectionLimiter.ResetThrottle(ipaddr)
|
||||
}
|
||||
server.dlines.AddIP(ipaddr, duration, throttleMessage, "Exceeded automated connection throttle", "auto.connection.throttler")
|
||||
// they're DLINE'd for 15 minutes or whatever, so we can reset the connection throttle now,
|
||||
// and once their temporary DLINE is finished they can fill up the throttler again
|
||||
server.connectionLimiter.ResetThrottle(ipaddr)
|
||||
|
||||
// this might not show up properly on some clients, but our objective here is just to close it out before it has a load impact on us
|
||||
server.logger.Info(
|
||||
"connect-ip",
|
||||
fmt.Sprintf("Client from %v exceeded connection throttle, d-lining for %v", ipaddr, duration))
|
||||
return true, throttleMessage
|
||||
return true, false, throttleMessage
|
||||
} else if err != nil {
|
||||
server.logger.Warning("internal", "unexpected ban result", err.Error())
|
||||
}
|
||||
|
||||
return false, ""
|
||||
if checkScripts && config.Server.IPCheckScript.Enabled {
|
||||
output, err := CheckIPBan(server.semaphores.IPCheckScript, config.Server.IPCheckScript, ipaddr)
|
||||
if err != nil {
|
||||
server.logger.Error("internal", "couldn't check IP ban script", ipaddr.String(), err.Error())
|
||||
return false, false, ""
|
||||
}
|
||||
// TODO: currently no way to cache results other than IPBanned
|
||||
if output.Result == IPBanned && output.CacheSeconds != 0 {
|
||||
network, err := utils.NormalizedNetFromString(output.CacheNet)
|
||||
if err != nil {
|
||||
server.logger.Error("internal", "invalid dline net from IP ban script", ipaddr.String(), output.CacheNet)
|
||||
} else {
|
||||
dlineDuration := time.Duration(output.CacheSeconds) * time.Second
|
||||
err := server.dlines.AddNetwork(network, dlineDuration, output.BanMessage, "", "")
|
||||
if err != nil {
|
||||
server.logger.Error("internal", "couldn't set dline from IP ban script", ipaddr.String(), err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
if output.Result == IPBanned {
|
||||
// XXX roll back IP connection/throttling addition for the IP
|
||||
server.connectionLimiter.RemoveClient(ipaddr)
|
||||
server.logger.Info("connect-ip", "Rejected client due to ip-check-script", ipaddr.String())
|
||||
return true, false, output.BanMessage
|
||||
} else if output.Result == IPRequireSASL {
|
||||
server.logger.Info("connect-ip", "Requiring SASL from client due to ip-check-script", ipaddr.String())
|
||||
return false, true, ""
|
||||
}
|
||||
}
|
||||
|
||||
return false, false, ""
|
||||
}
|
||||
|
||||
func (server *Server) checkTorLimits() (banned bool, message string) {
|
||||
@ -214,6 +242,12 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
|
||||
return // whether we succeeded or failed, either way `c` is not getting registered
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// try to complete registration normally
|
||||
// XXX(#1057) username can be filled in by an ident query without the client
|
||||
// having sent USER: check for both username and realname to ensure they did
|
||||
@ -229,7 +263,7 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
|
||||
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
|
||||
// before completing the other registration commands
|
||||
config := server.Config()
|
||||
authOutcome := c.isAuthorized(server, config, session)
|
||||
authOutcome := c.isAuthorized(server, config, session, c.requireSASL)
|
||||
var quitMessage string
|
||||
switch authOutcome {
|
||||
case authFailPass:
|
||||
@ -244,12 +278,6 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// we have the final value of the IP address: do the hostname lookup
|
||||
// (nickmask will be set below once nickname assignment succeeds)
|
||||
if session.rawHostname == "" {
|
||||
session.client.lookupHostname(session, false)
|
||||
}
|
||||
|
||||
rb := NewResponseBuffer(session)
|
||||
nickError := performNickChange(server, c, c, session, c.preregNick, rb)
|
||||
rb.Send(true)
|
||||
@ -489,6 +517,9 @@ func (server *Server) applyConfig(config *Config) (err error) {
|
||||
return fmt.Errorf("Cannot enable or disable relaying after launching the server, rehash aborted")
|
||||
} else if oldConfig.Server.Relaymsg.Separators != config.Server.Relaymsg.Separators {
|
||||
return fmt.Errorf("Cannot change relaying separators after launching the server, rehash aborted")
|
||||
} else if oldConfig.Server.IPCheckScript.MaxConcurrency != config.Server.IPCheckScript.MaxConcurrency ||
|
||||
oldConfig.Accounts.AuthScript.MaxConcurrency != config.Accounts.AuthScript.MaxConcurrency {
|
||||
return fmt.Errorf("Cannot change max-concurrency for scripts after launching the server, rehash aborted")
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,6 +544,17 @@ func (server *Server) applyConfig(config *Config) (err error) {
|
||||
server.logger.Debug("server", "Regenerating HELP indexes for new languages")
|
||||
server.helpIndexManager.GenerateIndices(config.languageManager)
|
||||
|
||||
if initial {
|
||||
maxIPConc := int(config.Server.IPCheckScript.MaxConcurrency)
|
||||
if maxIPConc != 0 {
|
||||
server.semaphores.IPCheckScript.Initialize(maxIPConc)
|
||||
}
|
||||
maxAuthConc := int(config.Accounts.AuthScript.MaxConcurrency)
|
||||
if maxAuthConc != 0 {
|
||||
server.semaphores.AuthScript.Initialize(maxAuthConc)
|
||||
}
|
||||
}
|
||||
|
||||
if oldConfig != nil {
|
||||
// if certain features were enabled by rehash, we need to load the corresponding data
|
||||
// from the store
|
||||
|
Loading…
Reference in New Issue
Block a user