mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 10:42:52 +01:00
add NICKSERV SESSIONS command to list sessions
This commit is contained in:
parent
b11bf503e7
commit
da656c07c8
@ -94,7 +94,14 @@ type Client struct {
|
||||
type Session struct {
|
||||
client *Client
|
||||
|
||||
socket *Socket
|
||||
ctime time.Time
|
||||
atime time.Time
|
||||
|
||||
socket *Socket
|
||||
realIP net.IP
|
||||
proxiedIP net.IP
|
||||
rawHostname string
|
||||
|
||||
idletimer IdleTimer
|
||||
fakelag Fakelag
|
||||
|
||||
@ -104,9 +111,6 @@ type Session struct {
|
||||
maxlenRest uint32
|
||||
capState caps.State
|
||||
capVersion caps.Version
|
||||
|
||||
// TODO track per-connection real IP, proxied IP, and hostname here,
|
||||
// so we can list attached sessions and their details
|
||||
}
|
||||
|
||||
// sets the session quit message, if there isn't one already
|
||||
@ -187,6 +191,8 @@ func RunNewClient(server *Server, conn clientConn) {
|
||||
socket: socket,
|
||||
capVersion: caps.Cap301,
|
||||
capState: caps.NoneState,
|
||||
ctime: now,
|
||||
atime: now,
|
||||
}
|
||||
session.SetMaxlenRest()
|
||||
client.sessions = []*Session{session}
|
||||
@ -197,20 +203,29 @@ func RunNewClient(server *Server, conn clientConn) {
|
||||
client.certfp, _ = socket.CertFP()
|
||||
}
|
||||
|
||||
remoteAddr := conn.Conn.RemoteAddr()
|
||||
if conn.IsTor {
|
||||
client.SetMode(modes.TLS, true)
|
||||
client.realIP = utils.IPv4LoopbackAddress
|
||||
client.rawHostname = config.Server.TorListeners.Vhost
|
||||
session.realIP = utils.AddrToIP(remoteAddr)
|
||||
// cover up details of the tor proxying infrastructure (not a user privacy concern,
|
||||
// but a hardening measure):
|
||||
session.proxiedIP = utils.IPv4LoopbackAddress
|
||||
session.rawHostname = config.Server.TorListeners.Vhost
|
||||
} else {
|
||||
remoteAddr := conn.Conn.RemoteAddr()
|
||||
client.realIP = utils.AddrToIP(remoteAddr)
|
||||
// Set the hostname for this client
|
||||
// (may be overridden by a later PROXY command from stunnel)
|
||||
client.rawHostname = utils.LookupHostname(client.realIP.String())
|
||||
session.realIP = utils.AddrToIP(remoteAddr)
|
||||
// set the hostname for this client (may be overridden later by PROXY or WEBIRC)
|
||||
session.rawHostname = utils.LookupHostname(session.realIP.String())
|
||||
if utils.AddrIsLocal(remoteAddr) {
|
||||
// treat local connections as secure (may be overridden later by WEBIRC)
|
||||
client.SetMode(modes.TLS, true)
|
||||
}
|
||||
if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
|
||||
client.doIdentLookup(conn.Conn)
|
||||
}
|
||||
}
|
||||
client.realIP = session.realIP
|
||||
client.rawHostname = session.rawHostname
|
||||
client.proxiedIP = session.proxiedIP
|
||||
|
||||
client.run(session)
|
||||
}
|
||||
@ -389,10 +404,13 @@ func (session *Session) playReattachMessages() {
|
||||
//
|
||||
|
||||
// Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
|
||||
func (client *Client) Active() {
|
||||
func (client *Client) Active(session *Session) {
|
||||
// TODO normalize all times to utc?
|
||||
now := time.Now()
|
||||
client.stateMutex.Lock()
|
||||
defer client.stateMutex.Unlock()
|
||||
client.atime = time.Now()
|
||||
session.atime = now
|
||||
client.atime = now
|
||||
}
|
||||
|
||||
// Ping sends the client a PING message.
|
||||
|
@ -58,8 +58,8 @@ func (cmd *Command) Run(server *Server, client *Client, session *Session, msg ir
|
||||
session.idletimer.Touch()
|
||||
}
|
||||
|
||||
if !cmd.leaveClientIdle {
|
||||
client.Active()
|
||||
if client.registered && !cmd.leaveClientIdle {
|
||||
client.Active(session)
|
||||
}
|
||||
|
||||
return exiting
|
||||
|
@ -73,7 +73,9 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls boo
|
||||
|
||||
client.stateMutex.Lock()
|
||||
defer client.stateMutex.Unlock()
|
||||
session.proxiedIP = parsedProxiedIP
|
||||
client.proxiedIP = parsedProxiedIP
|
||||
session.rawHostname = rawHostname
|
||||
client.rawHostname = rawHostname
|
||||
// nickmask will be updated when the client completes registration
|
||||
// set tls info
|
||||
|
@ -4,6 +4,7 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/oragono/oragono/irc/isupport"
|
||||
@ -70,6 +71,37 @@ func (client *Client) Sessions() (sessions []*Session) {
|
||||
return
|
||||
}
|
||||
|
||||
type SessionData struct {
|
||||
ctime time.Time
|
||||
atime time.Time
|
||||
ip net.IP
|
||||
hostname string
|
||||
}
|
||||
|
||||
func (client *Client) AllSessionData(currentSession *Session) (data []SessionData, currentIndex int) {
|
||||
currentIndex = -1
|
||||
client.stateMutex.RLock()
|
||||
defer client.stateMutex.RUnlock()
|
||||
|
||||
data = make([]SessionData, len(client.sessions))
|
||||
for i, session := range client.sessions {
|
||||
if session == currentSession {
|
||||
currentIndex = i
|
||||
}
|
||||
data[i] = SessionData{
|
||||
atime: session.atime,
|
||||
ctime: session.ctime,
|
||||
hostname: session.rawHostname,
|
||||
}
|
||||
if session.proxiedIP != nil {
|
||||
data[i].ip = session.proxiedIP
|
||||
} else {
|
||||
data[i].ip = session.realIP
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (client *Client) AddSession(session *Session) (success bool) {
|
||||
client.stateMutex.Lock()
|
||||
defer client.stateMutex.Unlock()
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goshuirc/irc-go/ircfmt"
|
||||
|
||||
"github.com/oragono/oragono/irc/modes"
|
||||
)
|
||||
|
||||
// "enabled" callbacks for specific nickserv commands
|
||||
@ -26,6 +28,10 @@ func nsEnforceEnabled(config *Config) bool {
|
||||
return servCmdRequiresNickRes(config) && config.Accounts.NickReservation.AllowCustomEnforcement
|
||||
}
|
||||
|
||||
func servCmdRequiresBouncerEnabled(config *Config) bool {
|
||||
return config.Accounts.Bouncer.Enabled
|
||||
}
|
||||
|
||||
var (
|
||||
// ZNC's nickserv module will not detect this unless it is:
|
||||
// 1. sent with prefix `nickserv`
|
||||
@ -142,6 +148,16 @@ an administrator can set use this command to set up user accounts.`,
|
||||
capabs: []string{"accreg"},
|
||||
minParams: 2,
|
||||
},
|
||||
"sessions": {
|
||||
handler: nsSessionsHandler,
|
||||
help: `Syntax: $bSESSIONS [nickname]$b
|
||||
|
||||
SESSIONS lists information about the sessions currently attached, via
|
||||
the server's bouncer functionality, to your nickname. An administrator
|
||||
can use this command to list another user's sessions.`,
|
||||
helpShort: `$bSESSIONS$b lists the sessions attached to a nickname.`,
|
||||
enabled: servCmdRequiresBouncerEnabled,
|
||||
},
|
||||
"unregister": {
|
||||
handler: nsUnregisterHandler,
|
||||
help: `Syntax: $bUNREGISTER <username> [code]$b
|
||||
@ -569,3 +585,34 @@ func nsEnforceHandler(server *Server, client *Client, command string, params []s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nsSessionsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
target := client
|
||||
|
||||
if 0 < len(params) {
|
||||
// same permissions check as RPL_WHOISACTUALLY for now:
|
||||
if !client.HasMode(modes.Operator) {
|
||||
nsNotice(rb, client.t("Command restricted"))
|
||||
return
|
||||
}
|
||||
target = server.clients.Get(params[0])
|
||||
if target == nil {
|
||||
nsNotice(rb, client.t("No such nick"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sessionData, currentIndex := target.AllSessionData(rb.session)
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Nickname %s has %d attached session(s)"), target.Nick(), len(sessionData)))
|
||||
for i, session := range sessionData {
|
||||
if currentIndex == i {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Session %d (currently attached session):"), i+1))
|
||||
} else {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
|
||||
}
|
||||
nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(IRCv3TimestampFormat)))
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(IRCv3TimestampFormat)))
|
||||
}
|
||||
}
|
||||
|
@ -428,17 +428,18 @@ func (server *Server) tryRegister(c *Client, session *Session) {
|
||||
}
|
||||
|
||||
// continue registration
|
||||
server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
|
||||
server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", c.nick, c.username, c.rawHostname, c.IPString(), c.realname))
|
||||
d := c.Details()
|
||||
server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", d.nick, d.username, d.realname))
|
||||
server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", d.nick, d.username, c.RawHostname(), c.IPString(), d.realname))
|
||||
|
||||
// send welcome text
|
||||
//NOTE(dan): we specifically use the NICK here instead of the nickmask
|
||||
// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
|
||||
c.Send(nil, server.name, RPL_WELCOME, c.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), c.nick))
|
||||
c.Send(nil, server.name, RPL_YOURHOST, c.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
|
||||
c.Send(nil, server.name, RPL_CREATED, c.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
|
||||
c.Send(nil, server.name, RPL_WELCOME, d.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), d.nick))
|
||||
c.Send(nil, server.name, RPL_YOURHOST, d.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
|
||||
c.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
|
||||
//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
|
||||
c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
|
||||
c.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
|
||||
|
||||
rb := NewResponseBuffer(session)
|
||||
c.RplISupport(rb)
|
||||
@ -447,7 +448,7 @@ func (server *Server) tryRegister(c *Client, session *Session) {
|
||||
|
||||
modestring := c.ModeString()
|
||||
if modestring != "+" {
|
||||
c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString())
|
||||
c.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, c.ModeString())
|
||||
}
|
||||
if server.logger.IsLoggingRawIO() {
|
||||
c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
||||
|
Loading…
Reference in New Issue
Block a user