ソースを参照

add NICKSERV SESSIONS command to list sessions

tags/v1.1.0-rc1
Shivaram Lingamneni 5年前
コミット
da656c07c8
6個のファイルの変更122行の追加22行の削除
  1. 31
    13
      irc/client.go
  2. 2
    2
      irc/commands.go
  3. 2
    0
      irc/gateways.go
  4. 32
    0
      irc/getters.go
  5. 47
    0
      irc/nickserv.go
  6. 8
    7
      irc/server.go

+ 31
- 13
irc/client.go ファイルの表示

@@ -94,7 +94,14 @@ type Client struct {
94 94
 type Session struct {
95 95
 	client *Client
96 96
 
97
-	socket    *Socket
97
+	ctime time.Time
98
+	atime time.Time
99
+
100
+	socket      *Socket
101
+	realIP      net.IP
102
+	proxiedIP   net.IP
103
+	rawHostname string
104
+
98 105
 	idletimer IdleTimer
99 106
 	fakelag   Fakelag
100 107
 
@@ -104,9 +111,6 @@ type Session struct {
104 111
 	maxlenRest   uint32
105 112
 	capState     caps.State
106 113
 	capVersion   caps.Version
107
-
108
-	// TODO track per-connection real IP, proxied IP, and hostname here,
109
-	// so we can list attached sessions and their details
110 114
 }
111 115
 
112 116
 // sets the session quit message, if there isn't one already
@@ -187,6 +191,8 @@ func RunNewClient(server *Server, conn clientConn) {
187 191
 		socket:     socket,
188 192
 		capVersion: caps.Cap301,
189 193
 		capState:   caps.NoneState,
194
+		ctime:      now,
195
+		atime:      now,
190 196
 	}
191 197
 	session.SetMaxlenRest()
192 198
 	client.sessions = []*Session{session}
@@ -197,20 +203,29 @@ func RunNewClient(server *Server, conn clientConn) {
197 203
 		client.certfp, _ = socket.CertFP()
198 204
 	}
199 205
 
206
+	remoteAddr := conn.Conn.RemoteAddr()
200 207
 	if conn.IsTor {
201 208
 		client.SetMode(modes.TLS, true)
202
-		client.realIP = utils.IPv4LoopbackAddress
203
-		client.rawHostname = config.Server.TorListeners.Vhost
209
+		session.realIP = utils.AddrToIP(remoteAddr)
210
+		// cover up details of the tor proxying infrastructure (not a user privacy concern,
211
+		// but a hardening measure):
212
+		session.proxiedIP = utils.IPv4LoopbackAddress
213
+		session.rawHostname = config.Server.TorListeners.Vhost
204 214
 	} else {
205
-		remoteAddr := conn.Conn.RemoteAddr()
206
-		client.realIP = utils.AddrToIP(remoteAddr)
207
-		// Set the hostname for this client
208
-		// (may be overridden by a later PROXY command from stunnel)
209
-		client.rawHostname = utils.LookupHostname(client.realIP.String())
215
+		session.realIP = utils.AddrToIP(remoteAddr)
216
+		// set the hostname for this client (may be overridden later by PROXY or WEBIRC)
217
+		session.rawHostname = utils.LookupHostname(session.realIP.String())
218
+		if utils.AddrIsLocal(remoteAddr) {
219
+			// treat local connections as secure (may be overridden later by WEBIRC)
220
+			client.SetMode(modes.TLS, true)
221
+		}
210 222
 		if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
211 223
 			client.doIdentLookup(conn.Conn)
212 224
 		}
213 225
 	}
226
+	client.realIP = session.realIP
227
+	client.rawHostname = session.rawHostname
228
+	client.proxiedIP = session.proxiedIP
214 229
 
215 230
 	client.run(session)
216 231
 }
@@ -389,10 +404,13 @@ func (session *Session) playReattachMessages() {
389 404
 //
390 405
 
391 406
 // Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
392
-func (client *Client) Active() {
407
+func (client *Client) Active(session *Session) {
408
+	// TODO normalize all times to utc?
409
+	now := time.Now()
393 410
 	client.stateMutex.Lock()
394 411
 	defer client.stateMutex.Unlock()
395
-	client.atime = time.Now()
412
+	session.atime = now
413
+	client.atime = now
396 414
 }
397 415
 
398 416
 // Ping sends the client a PING message.

+ 2
- 2
irc/commands.go ファイルの表示

@@ -58,8 +58,8 @@ func (cmd *Command) Run(server *Server, client *Client, session *Session, msg ir
58 58
 		session.idletimer.Touch()
59 59
 	}
60 60
 
61
-	if !cmd.leaveClientIdle {
62
-		client.Active()
61
+	if client.registered && !cmd.leaveClientIdle {
62
+		client.Active(session)
63 63
 	}
64 64
 
65 65
 	return exiting

+ 2
- 0
irc/gateways.go ファイルの表示

@@ -73,7 +73,9 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls boo
73 73
 
74 74
 	client.stateMutex.Lock()
75 75
 	defer client.stateMutex.Unlock()
76
+	session.proxiedIP = parsedProxiedIP
76 77
 	client.proxiedIP = parsedProxiedIP
78
+	session.rawHostname = rawHostname
77 79
 	client.rawHostname = rawHostname
78 80
 	// nickmask will be updated when the client completes registration
79 81
 	// set tls info

+ 32
- 0
irc/getters.go ファイルの表示

@@ -4,6 +4,7 @@
4 4
 package irc
5 5
 
6 6
 import (
7
+	"net"
7 8
 	"time"
8 9
 
9 10
 	"github.com/oragono/oragono/irc/isupport"
@@ -70,6 +71,37 @@ func (client *Client) Sessions() (sessions []*Session) {
70 71
 	return
71 72
 }
72 73
 
74
+type SessionData struct {
75
+	ctime    time.Time
76
+	atime    time.Time
77
+	ip       net.IP
78
+	hostname string
79
+}
80
+
81
+func (client *Client) AllSessionData(currentSession *Session) (data []SessionData, currentIndex int) {
82
+	currentIndex = -1
83
+	client.stateMutex.RLock()
84
+	defer client.stateMutex.RUnlock()
85
+
86
+	data = make([]SessionData, len(client.sessions))
87
+	for i, session := range client.sessions {
88
+		if session == currentSession {
89
+			currentIndex = i
90
+		}
91
+		data[i] = SessionData{
92
+			atime:    session.atime,
93
+			ctime:    session.ctime,
94
+			hostname: session.rawHostname,
95
+		}
96
+		if session.proxiedIP != nil {
97
+			data[i].ip = session.proxiedIP
98
+		} else {
99
+			data[i].ip = session.realIP
100
+		}
101
+	}
102
+	return
103
+}
104
+
73 105
 func (client *Client) AddSession(session *Session) (success bool) {
74 106
 	client.stateMutex.Lock()
75 107
 	defer client.stateMutex.Unlock()

+ 47
- 0
irc/nickserv.go ファイルの表示

@@ -7,6 +7,8 @@ import (
7 7
 	"fmt"
8 8
 
9 9
 	"github.com/goshuirc/irc-go/ircfmt"
10
+
11
+	"github.com/oragono/oragono/irc/modes"
10 12
 )
11 13
 
12 14
 // "enabled" callbacks for specific nickserv commands
@@ -26,6 +28,10 @@ func nsEnforceEnabled(config *Config) bool {
26 28
 	return servCmdRequiresNickRes(config) && config.Accounts.NickReservation.AllowCustomEnforcement
27 29
 }
28 30
 
31
+func servCmdRequiresBouncerEnabled(config *Config) bool {
32
+	return config.Accounts.Bouncer.Enabled
33
+}
34
+
29 35
 var (
30 36
 	// ZNC's nickserv module will not detect this unless it is:
31 37
 	// 1. sent with prefix `nickserv`
@@ -142,6 +148,16 @@ an administrator can set use this command to set up user accounts.`,
142 148
 			capabs:    []string{"accreg"},
143 149
 			minParams: 2,
144 150
 		},
151
+		"sessions": {
152
+			handler: nsSessionsHandler,
153
+			help: `Syntax: $bSESSIONS [nickname]$b
154
+
155
+SESSIONS lists information about the sessions currently attached, via
156
+the server's bouncer functionality, to your nickname. An administrator
157
+can use this command to list another user's sessions.`,
158
+			helpShort: `$bSESSIONS$b lists the sessions attached to a nickname.`,
159
+			enabled:   servCmdRequiresBouncerEnabled,
160
+		},
145 161
 		"unregister": {
146 162
 			handler: nsUnregisterHandler,
147 163
 			help: `Syntax: $bUNREGISTER <username> [code]$b
@@ -569,3 +585,34 @@ func nsEnforceHandler(server *Server, client *Client, command string, params []s
569 585
 		}
570 586
 	}
571 587
 }
588
+
589
+func nsSessionsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
590
+	target := client
591
+
592
+	if 0 < len(params) {
593
+		// same permissions check as RPL_WHOISACTUALLY for now:
594
+		if !client.HasMode(modes.Operator) {
595
+			nsNotice(rb, client.t("Command restricted"))
596
+			return
597
+		}
598
+		target = server.clients.Get(params[0])
599
+		if target == nil {
600
+			nsNotice(rb, client.t("No such nick"))
601
+			return
602
+		}
603
+	}
604
+
605
+	sessionData, currentIndex := target.AllSessionData(rb.session)
606
+	nsNotice(rb, fmt.Sprintf(client.t("Nickname %s has %d attached session(s)"), target.Nick(), len(sessionData)))
607
+	for i, session := range sessionData {
608
+		if currentIndex == i {
609
+			nsNotice(rb, fmt.Sprintf(client.t("Session %d (currently attached session):"), i+1))
610
+		} else {
611
+			nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
612
+		}
613
+		nsNotice(rb, fmt.Sprintf(client.t("IP address:  %s"), session.ip.String()))
614
+		nsNotice(rb, fmt.Sprintf(client.t("Hostname:    %s"), session.hostname))
615
+		nsNotice(rb, fmt.Sprintf(client.t("Created at:  %s"), session.ctime.Format(IRCv3TimestampFormat)))
616
+		nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(IRCv3TimestampFormat)))
617
+	}
618
+}

+ 8
- 7
irc/server.go ファイルの表示

@@ -428,17 +428,18 @@ func (server *Server) tryRegister(c *Client, session *Session) {
428 428
 	}
429 429
 
430 430
 	// continue registration
431
-	server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
432
-	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))
431
+	d := c.Details()
432
+	server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", d.nick, d.username, d.realname))
433
+	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))
433 434
 
434 435
 	// send welcome text
435 436
 	//NOTE(dan): we specifically use the NICK here instead of the nickmask
436 437
 	// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
437
-	c.Send(nil, server.name, RPL_WELCOME, c.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), c.nick))
438
-	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))
439
-	c.Send(nil, server.name, RPL_CREATED, c.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
438
+	c.Send(nil, server.name, RPL_WELCOME, d.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), d.nick))
439
+	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))
440
+	c.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
440 441
 	//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
441
-	c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
442
+	c.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
442 443
 
443 444
 	rb := NewResponseBuffer(session)
444 445
 	c.RplISupport(rb)
@@ -447,7 +448,7 @@ func (server *Server) tryRegister(c *Client, session *Session) {
447 448
 
448 449
 	modestring := c.ModeString()
449 450
 	if modestring != "+" {
450
-		c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString())
451
+		c.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, c.ModeString())
451 452
 	}
452 453
 	if server.logger.IsLoggingRawIO() {
453 454
 		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."))

読み込み中…
キャンセル
保存