Sfoglia il codice sorgente

add NICKSERV SESSIONS command to list sessions

tags/v1.1.0-rc1
Shivaram Lingamneni 5 anni fa
parent
commit
da656c07c8
6 ha cambiato i file con 122 aggiunte e 22 eliminazioni
  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 Vedi File

94
 type Session struct {
94
 type Session struct {
95
 	client *Client
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
 	idletimer IdleTimer
105
 	idletimer IdleTimer
99
 	fakelag   Fakelag
106
 	fakelag   Fakelag
100
 
107
 
104
 	maxlenRest   uint32
111
 	maxlenRest   uint32
105
 	capState     caps.State
112
 	capState     caps.State
106
 	capVersion   caps.Version
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
 // sets the session quit message, if there isn't one already
116
 // sets the session quit message, if there isn't one already
187
 		socket:     socket,
191
 		socket:     socket,
188
 		capVersion: caps.Cap301,
192
 		capVersion: caps.Cap301,
189
 		capState:   caps.NoneState,
193
 		capState:   caps.NoneState,
194
+		ctime:      now,
195
+		atime:      now,
190
 	}
196
 	}
191
 	session.SetMaxlenRest()
197
 	session.SetMaxlenRest()
192
 	client.sessions = []*Session{session}
198
 	client.sessions = []*Session{session}
197
 		client.certfp, _ = socket.CertFP()
203
 		client.certfp, _ = socket.CertFP()
198
 	}
204
 	}
199
 
205
 
206
+	remoteAddr := conn.Conn.RemoteAddr()
200
 	if conn.IsTor {
207
 	if conn.IsTor {
201
 		client.SetMode(modes.TLS, true)
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
 	} else {
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
 		if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
222
 		if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
211
 			client.doIdentLookup(conn.Conn)
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
 	client.run(session)
230
 	client.run(session)
216
 }
231
 }
389
 //
404
 //
390
 
405
 
391
 // Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
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
 	client.stateMutex.Lock()
410
 	client.stateMutex.Lock()
394
 	defer client.stateMutex.Unlock()
411
 	defer client.stateMutex.Unlock()
395
-	client.atime = time.Now()
412
+	session.atime = now
413
+	client.atime = now
396
 }
414
 }
397
 
415
 
398
 // Ping sends the client a PING message.
416
 // Ping sends the client a PING message.

+ 2
- 2
irc/commands.go Vedi File

58
 		session.idletimer.Touch()
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
 	return exiting
65
 	return exiting

+ 2
- 0
irc/gateways.go Vedi File

73
 
73
 
74
 	client.stateMutex.Lock()
74
 	client.stateMutex.Lock()
75
 	defer client.stateMutex.Unlock()
75
 	defer client.stateMutex.Unlock()
76
+	session.proxiedIP = parsedProxiedIP
76
 	client.proxiedIP = parsedProxiedIP
77
 	client.proxiedIP = parsedProxiedIP
78
+	session.rawHostname = rawHostname
77
 	client.rawHostname = rawHostname
79
 	client.rawHostname = rawHostname
78
 	// nickmask will be updated when the client completes registration
80
 	// nickmask will be updated when the client completes registration
79
 	// set tls info
81
 	// set tls info

+ 32
- 0
irc/getters.go Vedi File

4
 package irc
4
 package irc
5
 
5
 
6
 import (
6
 import (
7
+	"net"
7
 	"time"
8
 	"time"
8
 
9
 
9
 	"github.com/oragono/oragono/irc/isupport"
10
 	"github.com/oragono/oragono/irc/isupport"
70
 	return
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
 func (client *Client) AddSession(session *Session) (success bool) {
105
 func (client *Client) AddSession(session *Session) (success bool) {
74
 	client.stateMutex.Lock()
106
 	client.stateMutex.Lock()
75
 	defer client.stateMutex.Unlock()
107
 	defer client.stateMutex.Unlock()

+ 47
- 0
irc/nickserv.go Vedi File

7
 	"fmt"
7
 	"fmt"
8
 
8
 
9
 	"github.com/goshuirc/irc-go/ircfmt"
9
 	"github.com/goshuirc/irc-go/ircfmt"
10
+
11
+	"github.com/oragono/oragono/irc/modes"
10
 )
12
 )
11
 
13
 
12
 // "enabled" callbacks for specific nickserv commands
14
 // "enabled" callbacks for specific nickserv commands
26
 	return servCmdRequiresNickRes(config) && config.Accounts.NickReservation.AllowCustomEnforcement
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
 var (
35
 var (
30
 	// ZNC's nickserv module will not detect this unless it is:
36
 	// ZNC's nickserv module will not detect this unless it is:
31
 	// 1. sent with prefix `nickserv`
37
 	// 1. sent with prefix `nickserv`
142
 			capabs:    []string{"accreg"},
148
 			capabs:    []string{"accreg"},
143
 			minParams: 2,
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
 		"unregister": {
161
 		"unregister": {
146
 			handler: nsUnregisterHandler,
162
 			handler: nsUnregisterHandler,
147
 			help: `Syntax: $bUNREGISTER <username> [code]$b
163
 			help: `Syntax: $bUNREGISTER <username> [code]$b
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 Vedi File

428
 	}
428
 	}
429
 
429
 
430
 	// continue registration
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
 	// send welcome text
435
 	// send welcome text
435
 	//NOTE(dan): we specifically use the NICK here instead of the nickmask
436
 	//NOTE(dan): we specifically use the NICK here instead of the nickmask
436
 	// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
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
 	//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
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
 	rb := NewResponseBuffer(session)
444
 	rb := NewResponseBuffer(session)
444
 	c.RplISupport(rb)
445
 	c.RplISupport(rb)
447
 
448
 
448
 	modestring := c.ModeString()
449
 	modestring := c.ModeString()
449
 	if modestring != "+" {
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
 	if server.logger.IsLoggingRawIO() {
453
 	if server.logger.IsLoggingRawIO() {
453
 		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."))
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."))

Loading…
Annulla
Salva