6 Commits

Author SHA1 Message Date
James Mills
962b6645c1 Release v1.6.3 2017-11-27 19:18:58 -08:00
James Mills
cee8bf9957 Added support for multi-layer channel privacy (Public, Private and Secret) (#36) 2017-11-27 19:16:17 -08:00
James Mills
9d93bca179 Added support for measuring secure vs. non-secure registerd clients (#34) 2017-11-26 17:31:11 -08:00
James Mills
ccae795335 Fixed graceful shutdown (#32) 2017-11-26 17:30:53 -08:00
James Mills
862eb429d4 Update README.md 2017-11-26 15:26:12 -08:00
James Mills
9e075dde67 Fixed send on closed channel bug (#29) 2017-11-26 13:25:21 -08:00
9 changed files with 112 additions and 22 deletions

View File

@@ -35,9 +35,9 @@ Discussion at:
* /server irc.mills.io +6697 (*use TLS/SSL*)
* /join #lobby
Or (*not recommended*)P
Or (**not recommended**):
* /server irc.mills.io (*default port 6667, non-TLS)
* /server irc.mills.io (*default port 6667, non-TLS*)
* /join #lobby
## Features
@@ -54,6 +54,7 @@ Or (*not recommended*)P
* Simple IRC operator privileges (*overrides most things*)
* Secure connection tracking (+z) and SecureOnly user mode (+Z)
* Secure channels (+Z)
* Three layers of channel privacy, Public, Private (+p) and Secret (s)
## Quick Start
@@ -144,6 +145,10 @@ There are a number of supported accompanying services that are being developed a
* [Soter](https://github.com/prologic/soter) -- An IRC Bot that persists channel modes and topics.
* [Cadmus](https://github.com/prologic/cadmus) -- An IRC Bot that logs channels and provides an interface for viewing and searching logs
## Recommended Mobile clients
* [Palaver (iOS)](https://palaverapp.com/) -- SASL, TLS, Server Password, Push Notifications, IRCv3 (*Also supports custom image upload service(s) for better privacy of shared photos/images over IRC*)
## License
eris is licensed under the MIT License.

View File

@@ -374,7 +374,7 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
return channel.applyModeMask(client, change.mode, change.op,
NewName(change.arg))
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, SecureChan:
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, Secret, SecureChan:
return channel.applyModeFlag(client, change.mode, change.op)
case Key:

View File

@@ -222,6 +222,12 @@ func (client *Client) destroy() {
// clean up server
if _, ok := client.socket.conn.(*tls.Conn); ok {
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Dec()
} else {
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Dec()
}
client.server.connections.Dec()
client.server.clients.Remove(client)

View File

@@ -20,6 +20,7 @@ var DefObjectives = map[float64]float64{
type Metrics struct {
namespace string
metrics map[string]prometheus.Metric
gaugevecs map[string]*prometheus.GaugeVec
sumvecs map[string]*prometheus.SummaryVec
}
@@ -27,6 +28,7 @@ func NewMetrics(namespace string) *Metrics {
return &Metrics{
namespace: namespace,
metrics: make(map[string]prometheus.Metric),
gaugevecs: make(map[string]*prometheus.GaugeVec),
sumvecs: make(map[string]*prometheus.SummaryVec),
}
}
@@ -101,6 +103,24 @@ func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) p
return guage
}
func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *prometheus.GaugeVec {
gauagevec := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: m.namespace,
Subsystem: subsystem,
Name: name,
Help: help,
},
labels,
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.gaugevecs[key] = gauagevec
prometheus.MustRegister(gauagevec)
return gauagevec
}
func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
summary := prometheus.NewSummary(
prometheus.SummaryOpts{
@@ -148,6 +168,11 @@ func (m *Metrics) Gauge(subsystem, name string) prometheus.Gauge {
return m.metrics[key].(prometheus.Gauge)
}
func (m *Metrics) GaugeVec(subsystem, name string) *prometheus.GaugeVec {
key := fmt.Sprintf("%s_%s", subsystem, name)
return m.gaugevecs[key]
}
func (m *Metrics) Summary(subsystem, name string) prometheus.Summary {
key := fmt.Sprintf("%s_%s", subsystem, name)
return m.metrics[key].(prometheus.Summary)

View File

@@ -70,7 +70,6 @@ var (
)
const (
Anonymous ChannelMode = 'a' // flag
BanMask ChannelMode = 'b' // arg
ChannelCreator ChannelMode = 'O' // flag
ChannelOperator ChannelMode = 'o' // arg
@@ -82,8 +81,6 @@ const (
NoOutside ChannelMode = 'n' // flag
OpOnlyTopic ChannelMode = 't' // flag
Private ChannelMode = 'p' // flag
Quiet ChannelMode = 'q' // flag
ReOp ChannelMode = 'r' // flag
Secret ChannelMode = 's' // flag, deprecated
UserLimit ChannelMode = 'l' // flag arg
Voice ChannelMode = 'v' // arg
@@ -93,7 +90,7 @@ const (
var (
SupportedChannelModes = ChannelModes{
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
OpOnlyTopic, Private, UserLimit, SecureChan,
OpOnlyTopic, Private, UserLimit, Secret, SecureChan,
}
)

22
irc/privacy.go Normal file
View File

@@ -0,0 +1,22 @@
package irc
func CanSeeChannel(client *Client, channel *Channel) bool {
isPrivate := channel.flags.Has(Private)
isSecret := channel.flags.Has(Secret)
isMember := channel.members.Has(client)
isOperator := client.flags[Operator]
isRegistered := client.flags[Registered]
isSecure := client.flags[SecureConn]
if !(isSecret || isPrivate) {
return true
}
if isSecret && (isMember || isOperator) {
return true
}
if isPrivate && (isMember || isOperator || (isRegistered && isSecure)) {
return true
}
return false
}

View File

@@ -489,8 +489,13 @@ func (target *Client) RplMOTDEnd() {
}
func (target *Client) RplList(channel *Channel) {
target.NumericReply(RPL_LIST,
"%s %d :%s", channel, channel.members.Count(), channel.topic)
target.NumericReply(
RPL_LIST,
"%s %d :%s",
channel,
channel.members.Count(),
channel.topic,
)
}
func (target *Client) RplListEnd(server *Server) {
@@ -504,8 +509,12 @@ func (target *Client) RplNamReply(channel *Channel) {
}
func (target *Client) RplWhoisChannels(client *Client) {
target.MultilineReply(client.WhoisChannelsNames(), RPL_WHOISCHANNELS,
"%s :%s", client.Nick())
target.MultilineReply(
client.WhoisChannelsNames(target),
RPL_WHOISCHANNELS,
"%s :%s",
client.Nick(),
)
}
func (target *Client) RplVersion() {

View File

@@ -44,13 +44,16 @@ type Server struct {
accounts PasswordStore
password []byte
signals chan os.Signal
done chan bool
whoWas *WhoWasList
ids map[string]*Identity
}
var (
SERVER_SIGNALS = []os.Signal{syscall.SIGINT, syscall.SIGHUP,
syscall.SIGTERM, syscall.SIGQUIT}
SERVER_SIGNALS = []os.Signal{
syscall.SIGINT, syscall.SIGHUP,
syscall.SIGTERM, syscall.SIGQUIT,
}
)
func NewServer(config *Config) *Server {
@@ -70,6 +73,7 @@ func NewServer(config *Config) *Server {
operators: config.Operators(),
accounts: NewMemoryPasswordStore(config.Accounts(), PasswordStoreOpts{}),
signals: make(chan os.Signal, len(SERVER_SIGNALS)),
done: make(chan bool),
whoWas: NewWhoWasList(100),
ids: make(map[string]*Identity),
}
@@ -123,15 +127,22 @@ func NewServer(config *Config) *Server {
},
)
// server clients gauge
// server registered (clients) gauge
server.metrics.NewGaugeFunc(
"server", "clients",
"server", "registered",
"Number of registered clients connected",
func() float64 {
return float64(server.clients.Count())
},
)
// server clients gauge (by secure/insecire)
server.metrics.NewGaugeVec(
"server", "clients",
"Number of registered clients connected (by secure/insecure)",
[]string{"secure"},
)
// server channels gauge
server.metrics.NewGaugeFunc(
"server", "channels",
@@ -192,12 +203,17 @@ func (server *Server) Shutdown() {
}
func (server *Server) Run() {
done := false
for !done {
for {
select {
case <-server.done:
return
case <-server.signals:
server.Shutdown()
done = true
// Give at least 1s for clients to see the shutdown
go func() {
time.Sleep(1 * time.Second)
server.done <- true
}()
case conn := <-server.newConns:
go NewClient(server, conn)
@@ -217,6 +233,12 @@ func (s *Server) acceptor(listener net.Listener) {
}
log.Debugf("%s accept: %s", s, conn.RemoteAddr())
if _, ok := conn.(*tls.Conn); ok {
s.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Inc()
} else {
s.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Inc()
}
s.connections.Inc()
s.newConns <- conn
}
@@ -603,10 +625,14 @@ func (msg *PrivMsgCommand) HandleServer(server *Server) {
}
}
func (client *Client) WhoisChannelsNames() []string {
func (client *Client) WhoisChannelsNames(target *Client) []string {
chstrs := make([]string, client.channels.Count())
index := 0
client.channels.Range(func(channel *Channel) bool {
if !CanSeeChannel(target, channel) {
return true
}
switch {
case channel.members.Get(client).Has(ChannelOperator):
chstrs[index] = "@" + channel.name.String()
@@ -813,7 +839,7 @@ func (msg *ListCommand) HandleServer(server *Server) {
if len(msg.channels) == 0 {
server.channels.Range(func(name Name, channel *Channel) bool {
if !client.flags[Operator] && channel.flags.Has(Private) {
if !CanSeeChannel(client, channel) {
return true
}
client.RplList(channel)
@@ -822,7 +848,7 @@ func (msg *ListCommand) HandleServer(server *Server) {
} else {
for _, chname := range msg.channels {
channel := server.channels.Get(chname)
if channel == nil || (!client.flags[Operator] && channel.flags.Has(Private)) {
if channel == nil || !CanSeeChannel(client, channel) {
client.ErrNoSuchChannel(chname)
continue
}

View File

@@ -9,7 +9,7 @@ var (
Package = "eris"
// Version release version
Version = "1.6.2"
Version = "1.6.3"
// Build will be overwritten automatically by the build system
Build = "dev"