mirror of
https://github.com/ergochat/ergo.git
synced 2025-04-01 13:27:07 +02:00
commit
12b2a0751b
@ -56,7 +56,7 @@ func NewChannel(s *Server, name string, registered bool) *Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel := &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{
|
lists: map[modes.Mode]*UserMaskSet{
|
||||||
modes.BanMask: NewUserMaskSet(),
|
modes.BanMask: NewUserMaskSet(),
|
||||||
modes.ExceptMask: NewUserMaskSet(),
|
modes.ExceptMask: NewUserMaskSet(),
|
||||||
@ -292,7 +292,7 @@ func (channel *Channel) SetRegistered(founder string) error {
|
|||||||
return errChannelAlreadyRegistered
|
return errChannelAlreadyRegistered
|
||||||
}
|
}
|
||||||
channel.registeredFounder = founder
|
channel.registeredFounder = founder
|
||||||
channel.registeredTime = time.Now()
|
channel.registeredTime = time.Now().UTC()
|
||||||
channel.accountToUMode[founder] = modes.ChannelFounder
|
channel.accountToUMode[founder] = modes.ChannelFounder
|
||||||
return nil
|
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)
|
// 2. Send JOIN and MODE lines to channel participants (including the new client)
|
||||||
// 3. Replay missed message history to the client
|
// 3. Replay missed message history to the client
|
||||||
func (channel *Channel) Resume(newClient, oldClient *Client, timestamp time.Time) {
|
func (channel *Channel) Resume(newClient, oldClient *Client, timestamp time.Time) {
|
||||||
now := time.Now()
|
now := time.Now().UTC()
|
||||||
channel.resumeAndAnnounce(newClient, oldClient)
|
channel.resumeAndAnnounce(newClient, oldClient)
|
||||||
if !timestamp.IsZero() {
|
if !timestamp.IsZero() {
|
||||||
channel.replayHistoryForResume(newClient, timestamp, now)
|
channel.replayHistoryForResume(newClient, timestamp, now)
|
||||||
@ -910,7 +910,7 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
|
|||||||
channel.stateMutex.Lock()
|
channel.stateMutex.Lock()
|
||||||
channel.topic = topic
|
channel.topic = topic
|
||||||
channel.topicSetBy = client.nickMaskString
|
channel.topicSetBy = client.nickMaskString
|
||||||
channel.topicSetTime = time.Now()
|
channel.topicSetTime = time.Now().UTC()
|
||||||
channel.stateMutex.Unlock()
|
channel.stateMutex.Unlock()
|
||||||
|
|
||||||
prefix := client.NickMaskString()
|
prefix := client.NickMaskString()
|
||||||
@ -975,7 +975,6 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
|
|||||||
nickmask := client.NickMaskString()
|
nickmask := client.NickMaskString()
|
||||||
account := client.AccountName()
|
account := client.AccountName()
|
||||||
chname := channel.Name()
|
chname := channel.Name()
|
||||||
now := time.Now().UTC()
|
|
||||||
|
|
||||||
// STATUSMSG targets are prefixed with the supplied min-prefix, e.g., @#channel
|
// STATUSMSG targets are prefixed with the supplied min-prefix, e.g., @#channel
|
||||||
if minPrefixMode != modes.Mode(0) {
|
if minPrefixMode != modes.Mode(0) {
|
||||||
@ -983,7 +982,6 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send echo-message
|
// send echo-message
|
||||||
// TODO this should use `now` as the time for consistency
|
|
||||||
if rb.session.capabilities.Has(caps.EchoMessage) {
|
if rb.session.capabilities.Has(caps.EchoMessage) {
|
||||||
var tagsToUse map[string]string
|
var tagsToUse map[string]string
|
||||||
if rb.session.capabilities.Has(caps.MessageTags) {
|
if rb.session.capabilities.Has(caps.MessageTags) {
|
||||||
@ -1005,9 +1003,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
|
|||||||
tagsToUse = clientOnlyTags
|
tagsToUse = clientOnlyTags
|
||||||
}
|
}
|
||||||
if histType == history.Tagmsg && session.capabilities.Has(caps.MessageTags) {
|
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 {
|
} 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 {
|
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 {
|
} else {
|
||||||
session.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, chname, message)
|
session.sendSplitMsgFromClientInternal(false, nickmask, account, tagsToUse, command, chname, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,9 +163,30 @@ type ClientDetails struct {
|
|||||||
accountName string
|
accountName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient sets up a new client and runs its goroutine.
|
// RunClient sets up a new client and runs its goroutine.
|
||||||
func RunNewClient(server *Server, conn clientConn) {
|
func (server *Server) RunClient(conn clientConn) {
|
||||||
now := time.Now()
|
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()
|
config := server.Config()
|
||||||
fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest
|
fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest
|
||||||
// give them 1k of grace over the limit:
|
// give them 1k of grace over the limit:
|
||||||
@ -194,6 +215,7 @@ func RunNewClient(server *Server, conn clientConn) {
|
|||||||
capState: caps.NoneState,
|
capState: caps.NoneState,
|
||||||
ctime: now,
|
ctime: now,
|
||||||
atime: now,
|
atime: now,
|
||||||
|
realIP: realIP,
|
||||||
}
|
}
|
||||||
session.SetMaxlenRest()
|
session.SetMaxlenRest()
|
||||||
client.sessions = []*Session{session}
|
client.sessions = []*Session{session}
|
||||||
@ -204,19 +226,17 @@ func RunNewClient(server *Server, conn clientConn) {
|
|||||||
client.certfp, _ = socket.CertFP()
|
client.certfp, _ = socket.CertFP()
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr := conn.Conn.RemoteAddr()
|
|
||||||
if conn.IsTor {
|
if conn.IsTor {
|
||||||
client.SetMode(modes.TLS, true)
|
client.SetMode(modes.TLS, true)
|
||||||
session.realIP = utils.AddrToIP(remoteAddr)
|
|
||||||
// cover up details of the tor proxying infrastructure (not a user privacy concern,
|
// cover up details of the tor proxying infrastructure (not a user privacy concern,
|
||||||
// but a hardening measure):
|
// but a hardening measure):
|
||||||
session.proxiedIP = utils.IPv4LoopbackAddress
|
session.proxiedIP = utils.IPv4LoopbackAddress
|
||||||
session.rawHostname = config.Server.TorListeners.Vhost
|
session.rawHostname = config.Server.TorListeners.Vhost
|
||||||
} else {
|
} else {
|
||||||
session.realIP = utils.AddrToIP(remoteAddr)
|
|
||||||
// set the hostname for this client (may be overridden later by PROXY or WEBIRC)
|
// set the hostname for this client (may be overridden later by PROXY or WEBIRC)
|
||||||
session.rawHostname = utils.LookupHostname(session.realIP.String())
|
session.rawHostname = utils.LookupHostname(session.realIP.String())
|
||||||
client.cloakedHostname = config.Server.Cloaks.ComputeCloak(session.realIP)
|
client.cloakedHostname = config.Server.Cloaks.ComputeCloak(session.realIP)
|
||||||
|
remoteAddr := conn.Conn.RemoteAddr()
|
||||||
if utils.AddrIsLocal(remoteAddr) {
|
if utils.AddrIsLocal(remoteAddr) {
|
||||||
// treat local connections as secure (may be overridden later by WEBIRC)
|
// treat local connections as secure (may be overridden later by WEBIRC)
|
||||||
client.SetMode(modes.TLS, true)
|
client.SetMode(modes.TLS, true)
|
||||||
@ -409,8 +429,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).
|
// 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) {
|
func (client *Client) Active(session *Session) {
|
||||||
// TODO normalize all times to utc?
|
now := time.Now().UTC()
|
||||||
now := time.Now()
|
|
||||||
client.stateMutex.Lock()
|
client.stateMutex.Lock()
|
||||||
defer client.stateMutex.Unlock()
|
defer client.stateMutex.Unlock()
|
||||||
session.atime = now
|
session.atime = now
|
||||||
@ -472,7 +491,7 @@ func (client *Client) tryResume() (success bool) {
|
|||||||
success = true
|
success = true
|
||||||
|
|
||||||
// this is a bit racey
|
// this is a bit racey
|
||||||
client.resumeDetails.ResumedAt = time.Now()
|
client.resumeDetails.ResumedAt = time.Now().UTC()
|
||||||
|
|
||||||
client.nickTimer.Touch(nil)
|
client.nickTimer.Touch(nil)
|
||||||
|
|
||||||
@ -496,7 +515,7 @@ func (client *Client) tryResume() (success bool) {
|
|||||||
hostname := client.Hostname()
|
hostname := client.Hostname()
|
||||||
|
|
||||||
friends := make(ClientSet)
|
friends := make(ClientSet)
|
||||||
oldestLostMessage := time.Now()
|
oldestLostMessage := time.Now().UTC()
|
||||||
|
|
||||||
// work out how much time, if any, is not covered by history buffers
|
// work out how much time, if any, is not covered by history buffers
|
||||||
for _, channel := range channels {
|
for _, channel := range channels {
|
||||||
@ -569,7 +588,7 @@ func (client *Client) tryResumeChannels() {
|
|||||||
|
|
||||||
// replay direct PRIVSMG history
|
// replay direct PRIVSMG history
|
||||||
if !details.Timestamp.IsZero() {
|
if !details.Timestamp.IsZero() {
|
||||||
now := time.Now()
|
now := time.Now().UTC()
|
||||||
items, complete := client.history.Between(details.Timestamp, now, false, 0)
|
items, complete := client.history.Between(details.Timestamp, now, false, 0)
|
||||||
rb := NewResponseBuffer(client.Sessions()[0])
|
rb := NewResponseBuffer(client.Sessions()[0])
|
||||||
client.replayPrivmsgHistory(rb, items, complete)
|
client.replayPrivmsgHistory(rb, items, complete)
|
||||||
@ -1072,12 +1091,12 @@ func (client *Client) destroy(beingResumed bool, session *Session) {
|
|||||||
|
|
||||||
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
|
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
|
||||||
// Adds account-tag to the line as well.
|
// 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 {
|
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 {
|
} else {
|
||||||
for _, messagePair := range message.Wrapped {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,6 +311,7 @@ func init() {
|
|||||||
"WHO": {
|
"WHO": {
|
||||||
handler: whoHandler,
|
handler: whoHandler,
|
||||||
minParams: 1,
|
minParams: 1,
|
||||||
|
leaveClientIdle: true,
|
||||||
},
|
},
|
||||||
"WHOIS": {
|
"WHOIS": {
|
||||||
handler: whoisHandler,
|
handler: whoisHandler,
|
||||||
|
@ -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
|
// ApplyConfig atomically applies a config update to a connection limit handler
|
||||||
func (cl *Limiter) ApplyConfig(config LimiterConfig) error {
|
func (cl *Limiter) ApplyConfig(config LimiterConfig) error {
|
||||||
// assemble exempted nets
|
// assemble exempted nets
|
||||||
@ -120,6 +109,10 @@ func (cl *Limiter) ApplyConfig(config LimiterConfig) error {
|
|||||||
cl.Lock()
|
cl.Lock()
|
||||||
defer cl.Unlock()
|
defer cl.Unlock()
|
||||||
|
|
||||||
|
if cl.population == nil {
|
||||||
|
cl.population = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
cl.enabled = config.Enabled
|
cl.enabled = config.Enabled
|
||||||
cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
|
cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
|
||||||
cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
|
cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
|
||||||
|
@ -45,7 +45,7 @@ type GenericThrottle struct {
|
|||||||
// it either denies it (by returning false) or allows it (by returning true)
|
// it either denies it (by returning false) or allows it (by returning true)
|
||||||
// and records it
|
// and records it
|
||||||
func (g *GenericThrottle) Touch() (throttled bool, remainingTime time.Duration) {
|
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) {
|
func (g *GenericThrottle) touch(now time.Time) (throttled bool, remainingTime time.Duration) {
|
||||||
@ -150,17 +150,6 @@ func (ct *Throttler) BanMessage() string {
|
|||||||
return ct.banMessage
|
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
|
// ApplyConfig atomically applies a config update to a throttler
|
||||||
func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
|
func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
|
||||||
// assemble exempted nets
|
// assemble exempted nets
|
||||||
@ -172,6 +161,10 @@ func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
|
|||||||
ct.Lock()
|
ct.Lock()
|
||||||
defer ct.Unlock()
|
defer ct.Unlock()
|
||||||
|
|
||||||
|
if ct.population == nil {
|
||||||
|
ct.population = make(map[string]ThrottleDetails)
|
||||||
|
}
|
||||||
|
|
||||||
ct.enabled = config.Enabled
|
ct.enabled = config.Enabled
|
||||||
ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
|
ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
|
||||||
ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
|
ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
|
||||||
|
@ -72,9 +72,9 @@ func makeTestThrottler(v4len, v6len int) *Throttler {
|
|||||||
ConnectionsPerCidr: maxConnections,
|
ConnectionsPerCidr: maxConnections,
|
||||||
Duration: minute,
|
Duration: minute,
|
||||||
}
|
}
|
||||||
throttler := NewThrottler()
|
var throttler Throttler
|
||||||
throttler.ApplyConfig(config)
|
throttler.ApplyConfig(config)
|
||||||
return throttler
|
return &throttler
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnectionThrottle(t *testing.T) {
|
func TestConnectionThrottle(t *testing.T) {
|
||||||
|
@ -34,7 +34,7 @@ type IPBanInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (info IPBanInfo) timeLeft() time.Duration {
|
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 {
|
func (info IPBanInfo) TimeLeft() string {
|
||||||
@ -114,7 +114,7 @@ func (dm *DLineManager) AddNetwork(network net.IPNet, duration time.Duration, re
|
|||||||
Reason: reason,
|
Reason: reason,
|
||||||
OperReason: operReason,
|
OperReason: operReason,
|
||||||
OperName: operName,
|
OperName: operName,
|
||||||
TimeCreated: time.Now(),
|
TimeCreated: time.Now().UTC(),
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ var (
|
|||||||
errAccountAlreadyVerified = errors.New(`Account is already verified`)
|
errAccountAlreadyVerified = errors.New(`Account is already verified`)
|
||||||
errAccountCantDropPrimaryNick = errors.New("Can't unreserve primary nickname")
|
errAccountCantDropPrimaryNick = errors.New("Can't unreserve primary nickname")
|
||||||
errAccountCreation = errors.New("Account could not be created")
|
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")
|
errAccountDoesNotExist = errors.New("Account does not exist")
|
||||||
errAccountInvalidCredentials = errors.New("Invalid account credentials")
|
errAccountInvalidCredentials = errors.New("Invalid account credentials")
|
||||||
errAccountBadPassphrase = errors.New(`Passphrase contains forbidden characters or is otherwise invalid`)
|
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")
|
errCallbackFailed = errors.New("Account verification could not be sent")
|
||||||
errCertfpAlreadyExists = errors.New(`An account already exists for your certificate fingerprint`)
|
errCertfpAlreadyExists = errors.New(`An account already exists for your certificate fingerprint`)
|
||||||
errChannelNotOwnedByAccount = errors.New("Channel not owned by the specified account")
|
errChannelNotOwnedByAccount = errors.New("Channel not owned by the specified account")
|
||||||
errChannelDoesNotExist = errors.New("Channel does not exist")
|
|
||||||
errChannelAlreadyRegistered = errors.New("Channel is already registered")
|
errChannelAlreadyRegistered = errors.New("Channel is already registered")
|
||||||
errChannelNameInUse = errors.New(`Channel name in use`)
|
errChannelNameInUse = errors.New(`Channel name in use`)
|
||||||
errInvalidChannelName = errors.New(`Invalid channel name`)
|
errInvalidChannelName = errors.New(`Invalid channel name`)
|
||||||
@ -38,12 +36,10 @@ var (
|
|||||||
errNicknameReserved = errors.New("nickname is reserved")
|
errNicknameReserved = errors.New("nickname is reserved")
|
||||||
errNoExistingBan = errors.New("Ban does not exist")
|
errNoExistingBan = errors.New("Ban does not exist")
|
||||||
errNoSuchChannel = errors.New(`No such channel`)
|
errNoSuchChannel = errors.New(`No such channel`)
|
||||||
errRenamePrivsNeeded = errors.New(`Only chanops can rename channels`)
|
|
||||||
errInsufficientPrivs = errors.New("Insufficient privileges")
|
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")
|
errInvalidUsername = errors.New("Invalid username")
|
||||||
errFeatureDisabled = errors.New(`That feature is disabled`)
|
errFeatureDisabled = errors.New(`That feature is disabled`)
|
||||||
|
errBanned = errors.New("IP or nickmask banned")
|
||||||
errInvalidParams = errors.New("Invalid parameters")
|
errInvalidParams = errors.New("Invalid parameters")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,24 +46,22 @@ func (wc *webircConfig) Populate() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ApplyProxiedIP applies the given IP to the client.
|
// 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
|
// PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself
|
||||||
// is whitelisted:
|
// is whitelisted:
|
||||||
if client.isTor {
|
if client.isTor {
|
||||||
return false
|
return errBadProxyLine, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure IP is sane
|
// ensure IP is sane
|
||||||
parsedProxiedIP := net.ParseIP(proxiedIP).To16()
|
parsedProxiedIP := net.ParseIP(proxiedIP).To16()
|
||||||
if parsedProxiedIP == nil {
|
if parsedProxiedIP == nil {
|
||||||
client.Quit(fmt.Sprintf(client.t("Proxied IP address is not valid: [%s]"), proxiedIP), session)
|
return errBadProxyLine, fmt.Sprintf(client.t("Proxied IP address is not valid: [%s]"), proxiedIP)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isBanned, banMsg := client.server.checkBans(parsedProxiedIP)
|
isBanned, banMsg := client.server.checkBans(parsedProxiedIP)
|
||||||
if isBanned {
|
if isBanned {
|
||||||
client.Quit(banMsg, session)
|
return errBanned, banMsg
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// given IP is sane! override the client's current IP
|
// 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.certfp = ""
|
||||||
client.SetMode(modes.TLS, tls)
|
client.SetMode(modes.TLS, tls)
|
||||||
|
|
||||||
return true
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the PROXY command: http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
// 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,
|
// 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.
|
// 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) {
|
func handleProxyCommand(server *Server, client *Client, session *Session, line string) (err error) {
|
||||||
|
var quitMsg string
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
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) {
|
if utils.IPInNets(client.realIP, server.Config().Server.proxyAllowedFromNets) {
|
||||||
// assume PROXY connections are always secure
|
// assume PROXY connections are always secure
|
||||||
if client.ApplyProxiedIP(session, params[2], true) {
|
err, quitMsg = client.ApplyProxiedIP(session, params[2], true)
|
||||||
return nil
|
return
|
||||||
} else {
|
} else {
|
||||||
return errBadProxyLine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// real source IP is not authorized to issue PROXY:
|
// real source IP is not authorized to issue PROXY:
|
||||||
return errBadGatewayAddress
|
return errBadGatewayAddress
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -356,7 +356,7 @@ func (channel *Channel) Rename(name, nameCasefolded string) {
|
|||||||
channel.name = name
|
channel.name = name
|
||||||
channel.nameCasefolded = nameCasefolded
|
channel.nameCasefolded = nameCasefolded
|
||||||
if channel.registeredFounder != "" {
|
if channel.registeredFounder != "" {
|
||||||
channel.registeredTime = time.Now()
|
channel.registeredTime = time.Now().UTC()
|
||||||
}
|
}
|
||||||
channel.stateMutex.Unlock()
|
channel.stateMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
session.sendFromClientInternal(false, splitMsg.Time, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick)
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if histType == history.Tagmsg && rb.session.capabilities.Has(caps.MessageTags) {
|
||||||
session.sendFromClientInternal(false, splitMsg.Time, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick)
|
session.sendFromClientInternal(false, splitMsg.Time, splitMsg.Msgid, nickMaskString, accountName, clientOnlyTags, msg.Command, tnick)
|
||||||
} else {
|
} 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() {
|
if histType != history.Notice && user.Away() {
|
||||||
@ -2377,7 +2377,7 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
|||||||
|
|
||||||
// TIME
|
// TIME
|
||||||
func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2576,7 +2576,13 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
if strings.HasPrefix(proxiedIP, "[") && strings.HasSuffix(proxiedIP, "]") {
|
if strings.HasPrefix(proxiedIP, "[") && strings.HasSuffix(proxiedIP, "]") {
|
||||||
proxiedIP = proxiedIP[1 : len(proxiedIP)-1]
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s
|
|||||||
hsNotice(rb, client.t("An error occurred"))
|
hsNotice(rb, client.t("An error occurred"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
elapsed := time.Now().Sub(account.VHost.LastRequestTime)
|
elapsed := time.Since(account.VHost.LastRequestTime)
|
||||||
remainingTime := server.AccountConfig().VHosts.UserRequests.Cooldown - elapsed
|
remainingTime := server.AccountConfig().VHosts.UserRequests.Cooldown - elapsed
|
||||||
// you can update your existing request, but if you were rejected,
|
// you can update your existing request, but if you were rejected,
|
||||||
// you can't spam a replacement request
|
// you can't spam a replacement request
|
||||||
|
@ -72,7 +72,7 @@ func (km *KLineManager) AddMask(mask string, duration time.Duration, reason, ope
|
|||||||
Reason: reason,
|
Reason: reason,
|
||||||
OperReason: operReason,
|
OperReason: operReason,
|
||||||
OperName: operName,
|
OperName: operName,
|
||||||
TimeCreated: time.Now(),
|
TimeCreated: time.Now().UTC(),
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
}
|
}
|
||||||
km.addMaskInternal(mask, info)
|
km.addMaskInternal(mask, info)
|
||||||
|
@ -19,13 +19,9 @@ type MonitorManager struct {
|
|||||||
// (all nicks must be normalized externally by casefolding)
|
// (all nicks must be normalized externally by casefolding)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMonitorManager returns a new MonitorManager.
|
func (mm *MonitorManager) Initialize() {
|
||||||
func NewMonitorManager() *MonitorManager {
|
mm.watching = make(map[*Client]map[string]bool)
|
||||||
mm := MonitorManager{
|
mm.watchedby = make(map[string]map[*Client]bool)
|
||||||
watching: make(map[*Client]map[string]bool),
|
|
||||||
watchedby: make(map[string]map[*Client]bool),
|
|
||||||
}
|
|
||||||
return &mm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line.
|
// AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line.
|
||||||
|
@ -5,6 +5,7 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/goshuirc/irc-go/ircfmt"
|
"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("IP address: %s"), session.ip.String()))
|
||||||
nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
|
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("Created at: %s"), session.ctime.Format(time.RFC1123)))
|
||||||
nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(IRCv3TimestampFormat)))
|
nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"github.com/oragono/oragono/irc/logger"
|
"github.com/oragono/oragono/irc/logger"
|
||||||
"github.com/oragono/oragono/irc/modes"
|
"github.com/oragono/oragono/irc/modes"
|
||||||
"github.com/oragono/oragono/irc/sno"
|
"github.com/oragono/oragono/irc/sno"
|
||||||
"github.com/oragono/oragono/irc/utils"
|
|
||||||
"github.com/tidwall/buntdb"
|
"github.com/tidwall/buntdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,15 +66,15 @@ type Server struct {
|
|||||||
clients ClientManager
|
clients ClientManager
|
||||||
config unsafe.Pointer
|
config unsafe.Pointer
|
||||||
configFilename string
|
configFilename string
|
||||||
connectionLimiter *connection_limits.Limiter
|
connectionLimiter connection_limits.Limiter
|
||||||
connectionThrottler *connection_limits.Throttler
|
connectionThrottler connection_limits.Throttler
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
dlines *DLineManager
|
dlines *DLineManager
|
||||||
helpIndexManager HelpIndexManager
|
helpIndexManager HelpIndexManager
|
||||||
klines *KLineManager
|
klines *KLineManager
|
||||||
listeners map[string]*ListenerWrapper
|
listeners map[string]*ListenerWrapper
|
||||||
logger *logger.Manager
|
logger *logger.Manager
|
||||||
monitorManager *MonitorManager
|
monitorManager MonitorManager
|
||||||
name string
|
name string
|
||||||
nameCasefolded string
|
nameCasefolded string
|
||||||
rehashMutex sync.Mutex // tier 4
|
rehashMutex sync.Mutex // tier 4
|
||||||
@ -83,7 +82,7 @@ type Server struct {
|
|||||||
pprofServer *http.Server
|
pprofServer *http.Server
|
||||||
resumeManager ResumeManager
|
resumeManager ResumeManager
|
||||||
signals chan os.Signal
|
signals chan os.Signal
|
||||||
snomasks *SnoManager
|
snomasks SnoManager
|
||||||
store *buntdb.DB
|
store *buntdb.DB
|
||||||
torLimiter connection_limits.TorLimiter
|
torLimiter connection_limits.TorLimiter
|
||||||
whoWas WhoWasList
|
whoWas WhoWasList
|
||||||
@ -110,20 +109,19 @@ type clientConn struct {
|
|||||||
func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
|
func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
|
||||||
// initialize data structures
|
// initialize data structures
|
||||||
server := &Server{
|
server := &Server{
|
||||||
connectionLimiter: connection_limits.NewLimiter(),
|
ctime: time.Now().UTC(),
|
||||||
connectionThrottler: connection_limits.NewThrottler(),
|
|
||||||
listeners: make(map[string]*ListenerWrapper),
|
listeners: make(map[string]*ListenerWrapper),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
monitorManager: NewMonitorManager(),
|
|
||||||
rehashSignal: make(chan os.Signal, 1),
|
rehashSignal: make(chan os.Signal, 1),
|
||||||
signals: make(chan os.Signal, len(ServerExitSignals)),
|
signals: make(chan os.Signal, len(ServerExitSignals)),
|
||||||
snomasks: NewSnoManager(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.clients.Initialize()
|
server.clients.Initialize()
|
||||||
server.semaphores.Initialize()
|
server.semaphores.Initialize()
|
||||||
server.resumeManager.Initialize(server)
|
server.resumeManager.Initialize(server)
|
||||||
server.whoWas.Initialize(config.Limits.WhowasEntries)
|
server.whoWas.Initialize(config.Limits.WhowasEntries)
|
||||||
|
server.monitorManager.Initialize()
|
||||||
|
server.snomasks.Initialize()
|
||||||
|
|
||||||
if err := server.applyConfig(config, true); err != nil {
|
if err := server.applyConfig(config, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -208,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) {
|
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
||||||
// check DLINEs
|
// check DLINEs
|
||||||
isBanned, info := server.dlines.CheckIP(ipaddr)
|
isBanned, info := server.dlines.CheckIP(ipaddr)
|
||||||
@ -339,7 +313,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor b
|
|||||||
IsTor: isTor,
|
IsTor: isTor,
|
||||||
}
|
}
|
||||||
// hand off the connection
|
// hand off the connection
|
||||||
go server.acceptClient(newConn)
|
go server.RunClient(newConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldStop {
|
if shouldStop {
|
||||||
@ -576,7 +550,6 @@ func (server *Server) rehash() error {
|
|||||||
|
|
||||||
func (server *Server) applyConfig(config *Config, initial bool) (err error) {
|
func (server *Server) applyConfig(config *Config, initial bool) (err error) {
|
||||||
if initial {
|
if initial {
|
||||||
server.ctime = time.Now()
|
|
||||||
server.configFilename = config.Filename
|
server.configFilename = config.Filename
|
||||||
server.name = config.Server.Name
|
server.name = config.Server.Name
|
||||||
server.nameCasefolded = config.Server.nameCasefolded
|
server.nameCasefolded = config.Server.nameCasefolded
|
||||||
|
@ -14,11 +14,8 @@ type SnoManager struct {
|
|||||||
sendLists map[sno.Mask]map[*Client]bool
|
sendLists map[sno.Mask]map[*Client]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnoManager returns a new SnoManager
|
func (m *SnoManager) Initialize() {
|
||||||
func NewSnoManager() *SnoManager {
|
|
||||||
var m SnoManager
|
|
||||||
m.sendLists = make(map[sno.Mask]map[*Client]bool)
|
m.sendLists = make(map[sno.Mask]map[*Client]bool)
|
||||||
return &m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMasks adds the given snomasks to the client.
|
// AddMasks adds the given snomasks to the client.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user