diff --git a/cmd/grumble/client.go b/cmd/grumble/client.go
index a2f535a..ca8f582 100644
--- a/cmd/grumble/client.go
+++ b/cmd/grumble/client.go
@@ -510,7 +510,7 @@ func (client *Client) tlsRecvLoop() {
Release: proto.String("Grumble"),
CryptoModes: cryptstate.SupportedModes(),
}
- if client.server.cfg.BoolValue("SendOSInfo") {
+ if client.server.cfg.BoolValue("sendversion") {
version.Os = proto.String(runtime.GOOS)
version.OsVersion = proto.String("(Unknown version)")
}
diff --git a/cmd/grumble/grumble.go b/cmd/grumble/grumble.go
index fcd5eb2..af6fe03 100644
--- a/cmd/grumble/grumble.go
+++ b/cmd/grumble/grumble.go
@@ -85,7 +85,7 @@ func main() {
if Args.LogPath != "" {
logFn = Args.LogPath
} else {
- logFn = config.PathValue("LogPath", Args.DataDir)
+ logFn = config.PathValue("logfile", Args.DataDir)
}
logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr)
if err != nil {
@@ -131,8 +131,8 @@ func main() {
// Check whether we should regenerate the default global keypair
// and corresponding certificate.
// These are used as the default certificate of all virtual servers.
- certFn := config.PathValue("CertPath", Args.DataDir)
- keyFn := config.PathValue("KeyPath", Args.DataDir)
+ certFn := config.PathValue("sslCert", Args.DataDir)
+ keyFn := config.PathValue("sslKey", Args.DataDir)
shouldRegen := false
if Args.RegenKeys {
shouldRegen = true
diff --git a/cmd/grumble/message.go b/cmd/grumble/message.go
index 417faba..3b04b1c 100644
--- a/cmd/grumble/message.go
+++ b/cmd/grumble/message.go
@@ -593,7 +593,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
return
}
- maxChannelUsers := server.cfg.IntValue("MaxChannelUsers")
+ maxChannelUsers := server.cfg.IntValue("usersperchannel")
if maxChannelUsers != 0 && len(dstChan.clients) >= maxChannelUsers {
client.sendPermissionDeniedFallback(mumbleproto.PermissionDenied_ChannelFull,
0x010201, "Channel is full")
@@ -653,7 +653,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
// Texture change
if userstate.Texture != nil {
- maximg := server.cfg.IntValue("MaxImageMessageLength")
+ maximg := server.cfg.IntValue("imagemessagelength")
if maximg > 0 && len(userstate.Texture) > maximg {
client.sendPermissionDeniedType(mumbleproto.PermissionDenied_TextTooLong)
return
diff --git a/cmd/grumble/register.go b/cmd/grumble/register.go
index 50015a8..fd3479e 100644
--- a/cmd/grumble/register.go
+++ b/cmd/grumble/register.go
@@ -39,16 +39,19 @@ const registerUrl = "https://mumble.info/register.cgi"
// This function is used to determine whether or not to periodically
// contact the master server list and update this server's metadata.
func (server *Server) IsPublic() bool {
- if len(server.cfg.StringValue("RegisterName")) == 0 {
+ if len(server.cfg.StringValue("registerName")) == 0 {
return false
}
- if len(server.cfg.StringValue("RegisterHost")) == 0 {
+ if len(server.cfg.StringValue("registerHostname")) == 0 {
return false
}
- if len(server.cfg.StringValue("RegisterPassword")) == 0 {
+ if len(server.cfg.StringValue("registerPassword")) == 0 {
return false
}
- if len(server.cfg.StringValue("RegisterWebUrl")) == 0 {
+ if len(server.cfg.StringValue("registerUrl")) == 0 {
+ return false
+ }
+ if !server.cfg.BoolValue("allowping") {
return false
}
return true
@@ -80,11 +83,11 @@ func (server *Server) RegisterPublicServer() {
// Render registration XML template
reg := Register{
- Name: server.cfg.StringValue("RegisterName"),
- Host: server.cfg.StringValue("RegisterHost"),
- Password: server.cfg.StringValue("RegisterPassword"),
- Url: server.cfg.StringValue("RegisterWebUrl"),
- Location: server.cfg.StringValue("RegisterLocation"),
+ Name: server.cfg.StringValue("registerName"),
+ Host: server.cfg.StringValue("registerHostname"),
+ Password: server.cfg.StringValue("registerPassword"),
+ Url: server.cfg.StringValue("registerUrl"),
+ Location: server.cfg.StringValue("registerLocation"),
Port: server.CurrentPort(),
Digest: digest,
Users: len(server.clients),
diff --git a/cmd/grumble/server.go b/cmd/grumble/server.go
index 672ff4f..0a8c1a0 100644
--- a/cmd/grumble/server.go
+++ b/cmd/grumble/server.go
@@ -178,6 +178,16 @@ func (server *Server) RootChannel() *Channel {
return root
}
+// Get a pointer to the default channel
+func (server *Server) DefaultChannel() *Channel {
+ channel, exists := server.Channels[server.cfg.IntValue("defaultchannel")]
+ if !exists {
+ return server.RootChannel()
+ }
+ return channel
+}
+
+// Set password as the new SuperUser password
func (server *Server) setConfigPassword(key, password string) {
saltBytes := make([]byte, 24)
_, err := rand.Read(saltBytes)
@@ -271,7 +281,14 @@ func (server *Server) handleIncomingClient(conn net.Conn) (err error) {
client.lf = &clientLogForwarder{client, server.Logger}
client.Logger = log.New(client.lf, "", 0)
- client.session = server.pool.Get()
+ client.session, err = server.pool.Get()
+ if err != nil {
+ // Server is full. Murmur just closes the connection here anyway,
+ // so don't bother sending a Reject_ServerFull
+ client.Printf("Server is full, rejecting %v", conn.RemoteAddr())
+ conn.Close()
+ return nil
+ }
client.Printf("New connection: %v (%v)", conn.RemoteAddr(), client.Session())
client.tcpaddr = addr.(*net.TCPAddr)
@@ -528,12 +545,13 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
client.user = user
}
}
- }
- if client.user == nil && server.hasServerPassword() {
- if auth.Password == nil || !server.CheckServerPassword(*auth.Password) {
- client.RejectAuth(mumbleproto.Reject_WrongServerPW, "Invalid server password")
- return
+ // Otherwise, the user is unregistered. If there is a server-wide password, they now need it.
+ if client.user == nil && server.hasServerPassword() {
+ if auth.Password == nil || !server.CheckServerPassword(*auth.Password) {
+ client.RejectAuth(mumbleproto.Reject_WrongServerPW, "Invalid server password")
+ return
+ }
}
}
@@ -618,8 +636,8 @@ func (server *Server) finishAuthenticate(client *Client) {
server.hclients[host] = append(server.hclients[host], client)
server.hmutex.Unlock()
- channel := server.RootChannel()
- if client.IsRegistered() {
+ channel := server.DefaultChannel()
+ if server.cfg.BoolValue("rememberchannel") && client.IsRegistered() {
lastChannel := server.Channels[client.user.LastChannelId]
if lastChannel != nil {
channel = lastChannel
@@ -675,8 +693,8 @@ func (server *Server) finishAuthenticate(client *Client) {
sync := &mumbleproto.ServerSync{}
sync.Session = proto.Uint32(client.Session())
- sync.MaxBandwidth = proto.Uint32(server.cfg.Uint32Value("MaxBandwidth"))
- sync.WelcomeText = proto.String(server.cfg.StringValue("WelcomeText"))
+ sync.MaxBandwidth = proto.Uint32(server.cfg.Uint32Value("bandwidth"))
+ sync.WelcomeText = proto.String(server.cfg.StringValue("welcometext"))
if client.IsSuperUser() {
sync.Permissions = proto.Uint64(uint64(acl.AllPermissions))
} else {
@@ -693,9 +711,9 @@ func (server *Server) finishAuthenticate(client *Client) {
}
err := client.sendMessage(&mumbleproto.ServerConfig{
- AllowHtml: proto.Bool(server.cfg.BoolValue("AllowHTML")),
- MessageLength: proto.Uint32(server.cfg.Uint32Value("MaxTextMessageLength")),
- ImageMessageLength: proto.Uint32(server.cfg.Uint32Value("MaxImageMessageLength")),
+ AllowHtml: proto.Bool(server.cfg.BoolValue("allowhtml")),
+ MessageLength: proto.Uint32(server.cfg.Uint32Value("textmessagelength")),
+ ImageMessageLength: proto.Uint32(server.cfg.Uint32Value("imagemessagelength")),
})
if err != nil {
client.Panicf("%v", err)
@@ -992,6 +1010,9 @@ func (server *Server) udpListenLoop() {
// Length 12 is for ping datagrams from the ConnectDialog.
if nread == 12 {
+ if !server.cfg.BoolValue("allowping") {
+ return
+ }
readbuf := bytes.NewBuffer(buf)
var (
tmp32 uint32
@@ -1001,11 +1022,11 @@ func (server *Server) udpListenLoop() {
_ = binary.Read(readbuf, binary.BigEndian, &rand)
buffer := bytes.NewBuffer(make([]byte, 0, 24))
- _ = binary.Write(buffer, binary.BigEndian, uint32((1<<16)|(2<<8)|2))
+ _ = binary.Write(buffer, binary.BigEndian, uint32((1<<16)|(2<<8)|4))
_ = binary.Write(buffer, binary.BigEndian, rand)
_ = binary.Write(buffer, binary.BigEndian, uint32(len(server.clients)))
- _ = binary.Write(buffer, binary.BigEndian, server.cfg.Uint32Value("MaxUsers"))
- _ = binary.Write(buffer, binary.BigEndian, server.cfg.Uint32Value("MaxBandwidth"))
+ _ = binary.Write(buffer, binary.BigEndian, server.cfg.Uint32Value("users"))
+ _ = binary.Write(buffer, binary.BigEndian, server.cfg.Uint32Value("bandwidth"))
err = server.SendUDP(buffer.Bytes(), udpaddr)
if err != nil {
@@ -1283,9 +1304,9 @@ func (server *Server) IsCertHashBanned(hash string) bool {
// Filter incoming text according to the server's current rules.
func (server *Server) FilterText(text string) (filtered string, err error) {
options := &htmlfilter.Options{
- StripHTML: !server.cfg.BoolValue("AllowHTML"),
- MaxTextMessageLength: server.cfg.IntValue("MaxTextMessageLength"),
- MaxImageMessageLength: server.cfg.IntValue("MaxImageMessageLength"),
+ StripHTML: !server.cfg.BoolValue("allowhtml"),
+ MaxTextMessageLength: server.cfg.IntValue("textmessagelength"),
+ MaxImageMessageLength: server.cfg.IntValue("imagemessagelength"),
}
return htmlfilter.Filter(text, options)
}
@@ -1339,7 +1360,7 @@ func isTimeout(err error) bool {
// Initialize the per-launch data
func (server *Server) initPerLaunchData() {
- server.pool = sessionpool.New()
+ server.pool = sessionpool.New(server.cfg.Uint32Value("users"))
server.clients = make(map[uint32]*Client)
server.hclients = make(map[string][]*Client)
server.hpclients = make(map[string]*Client)
@@ -1368,7 +1389,7 @@ func (server *Server) cleanPerLaunchData() {
// Port returns the port the native server will listen on when it is
// started.
func (server *Server) Port() int {
- port := server.cfg.IntValue("Port")
+ port := server.cfg.IntValue("port")
if port == 0 {
return DefaultPort + int(server.Id) - 1
}
@@ -1378,13 +1399,13 @@ func (server *Server) Port() int {
// ListenWebPort returns true if we should listen to the
// web port, otherwise false
func (server *Server) ListenWebPort() bool {
- return !server.cfg.BoolValue("NoWebServer")
+ return !server.cfg.BoolValue("nowebserver")
}
// WebPort returns the port the web server will listen on when it is
// started.
func (server *Server) WebPort() int {
- port := server.cfg.IntValue("WebPort")
+ port := server.cfg.IntValue("webport")
if port == 0 {
return DefaultWebPort + int(server.Id) - 1
}
@@ -1406,7 +1427,7 @@ func (server *Server) CurrentPort() int {
// it is started. This must be an IP address, either IPv4
// or IPv6.
func (server *Server) HostAddress() string {
- host := server.cfg.StringValue("Address")
+ host := server.cfg.StringValue("host")
if host == "" {
return "0.0.0.0"
}
@@ -1449,8 +1470,8 @@ func (server *Server) Start() (err error) {
*/
// Wrap a TLS listener around the TCP connection
- certFn := server.cfg.PathValue("CertPath", Args.DataDir)
- keyFn := server.cfg.PathValue("KeyPath", Args.DataDir)
+ certFn := server.cfg.PathValue("sslCert", Args.DataDir)
+ keyFn := server.cfg.PathValue("sslKey", Args.DataDir)
cert, err := tls.LoadX509KeyPair(certFn, keyFn)
if err != nil {
return err
@@ -1459,6 +1480,15 @@ func (server *Server) Start() (err error) {
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequestClientCert,
}
+ ciphersstr := server.cfg.StringValue("sslCiphers")
+ if ciphersstr != "" {
+ var invalid []string
+ server.tlscfg.CipherSuites, invalid = serverconf.ParseCipherlist(ciphersstr)
+ for _, cipher := range invalid {
+ log.Printf("Ignoring invalid or unsupported cipher \"%v\"", cipher)
+ }
+ server.tlscfg.PreferServerCipherSuites = true
+ }
server.tlsl = tls.NewListener(server.tcpl, server.tlscfg)
if shouldListenWeb {
diff --git a/pkg/serverconf/cipherlist.go b/pkg/serverconf/cipherlist.go
new file mode 100644
index 0000000..01c518d
--- /dev/null
+++ b/pkg/serverconf/cipherlist.go
@@ -0,0 +1,77 @@
+package serverconf
+
+import (
+ "crypto/tls"
+ "strings"
+)
+
+var cipherLookup = map[string]uint16{
+ // RFC
+ "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ // These are the actual names per RFC 7905
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+
+ // OpenSSL
+ "RC4-SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
+ "DES-CBC3-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ "AES128-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ "AES256-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ "AES128-SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ "AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ "AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ "ECDHE-ECDSA-RC4-SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ "ECDHE-ECDSA-AES128-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ "ECDHE-ECDSA-AES256-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ "ECDHE-RSA-RC4-SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ "ECDHE-RSA-DES-CBC3-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ "ECDHE-RSA-AES128-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ "ECDHE-RSA-AES256-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ "ECDHE-ECDSA-AES128-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ "ECDHE-RSA-AES128-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ "ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ "ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ "ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ "ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ "ECDHE-RSA-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ "ECDHE-ECDSA-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+}
+
+// ParseCipherlist parses a list of cipher suites separated by colons.
+// It supports both RFC and OpenSSL names, but does not support OpenSSL
+// cipher strings representing categories of cipher suites.
+func ParseCipherlist(list string) (ciphers []uint16, invalid []string) {
+ strciphers := strings.Split(list, ":")
+ ciphers = make([]uint16, 0, len(strciphers))
+ invalid = make([]string, 0)
+ for _, v := range strciphers {
+ c, ok := cipherLookup[v]
+ if ok {
+ ciphers = append(ciphers, c)
+ } else {
+ invalid = append(invalid, v)
+ }
+ }
+ return
+}
diff --git a/pkg/serverconf/config.go b/pkg/serverconf/config.go
index 258b04f..78d9095 100644
--- a/pkg/serverconf/config.go
+++ b/pkg/serverconf/config.go
@@ -11,19 +11,20 @@ import (
)
var defaultCfg = map[string]string{
- "MaxBandwidth": "72000",
- "MaxUsers": "1000",
- "MaxUsersPerChannel": "0",
- "MaxTextMessageLength": "5000",
- "MaxImageMessageLength": "131072",
- "AllowHTML": "true",
- "DefaultChannel": "0",
- "RememberChannel": "true",
- "WelcomeText": "Welcome to this server running Grumble.",
- "SendVersion": "true",
- "LogPath": "grumble.log",
- "CertPath": "cert.pem",
- "KeyPath": "key.pem",
+ "bandwidth": "72000",
+ "users": "1000",
+ "usersperchannel": "0",
+ "textmessagelength": "5000",
+ "imagemessagelength": "131072",
+ "allowhtml": "true",
+ "defaultchannel": "0",
+ "rememberchannel": "true",
+ "welcometext": "Welcome to this server running Grumble.",
+ "sendversion": "true",
+ "allowping": "true",
+ "logfile": "grumble.log",
+ "sslCert": "cert.pem",
+ "sslKey": "key.pem",
}
type Config struct {
diff --git a/pkg/serverconf/config_test.go b/pkg/serverconf/config_test.go
index 17054cf..dcbf846 100644
--- a/pkg/serverconf/config_test.go
+++ b/pkg/serverconf/config_test.go
@@ -26,7 +26,7 @@ func TestFloatAsInt(t *testing.T) {
func TestDefaultValue(t *testing.T) {
cfg := New(nil, nil)
- if cfg.IntValue("MaxBandwidth") != 72000 {
+ if cfg.IntValue("bandwidth") != 72000 {
t.Errorf("Expected 72000")
}
}
diff --git a/pkg/serverconf/file.go b/pkg/serverconf/file.go
index 2fc04eb..49c3189 100644
--- a/pkg/serverconf/file.go
+++ b/pkg/serverconf/file.go
@@ -37,16 +37,16 @@ func (c *ConfigFile) ServerConfig(id int64, persistentMap map[string]string) *Co
// Some server specific values from the global config must be offset.
// These are read differently by the server as well.
- if v, ok := m["Port"]; ok {
+ if v, ok := m["port"]; ok {
i, err := strconv.ParseInt(v, 10, 64)
if err == nil {
- m["Port"] = strconv.FormatInt(i+id-1, 10)
+ m["port"] = strconv.FormatInt(i+id-1, 10)
}
}
- if v, ok := m["WebPort"]; ok {
+ if v, ok := m["webport"]; ok {
i, err := strconv.ParseInt(v, 10, 64)
if err == nil {
- m["WebPort"] = strconv.FormatInt(i+id-1, 10)
+ m["webport"] = strconv.FormatInt(i+id-1, 10)
}
}
diff --git a/pkg/serverconf/file_ini.go b/pkg/serverconf/file_ini.go
index 8709b30..e4db06e 100644
--- a/pkg/serverconf/file_ini.go
+++ b/pkg/serverconf/file_ini.go
@@ -21,7 +21,6 @@ func (f *inicfg) GlobalMap() map[string]string {
return f.file.Section("").KeysHash()
}
-// todo(rubenseyer): not all of these even work.. make sure to implement them
var DefaultConfigFile = `# Grumble configuration file.
#
# The commented out settings represent the defaults.
@@ -29,55 +28,63 @@ var DefaultConfigFile = `# Grumble configuration file.
# Make sure to enclose values containing # or ; in double quotes or backticks.
# Address to bind the listeners to.
-#Address = 0.0.0.0
+#host = 0.0.0.0
-# Port is the port to bind the native Mumble protocol to.
-# WebPort is the port to bind the WebSocket Mumble protocol to.
-# They are incremented for each virtual server.
-#Port = 64738
-#WebPort = 443
+# port is the port to bind the native Mumble protocol to.
+# webport is the port to bind the WebSocket Mumble protocol to.
+# They are incremented for each virtual server (if set globally).
+#port = 64738
+#webport = 443
+
+# Whether to disable web server.
+#nowebserver
# "Message of the day" HTML string sent to connecting clients.
-#WelcomeText = "Welcome to this server running Grumble."
+#welcometext = "Welcome to this server running Grumble."
+
+# Password to join the server.
+#serverpassword =
# Maximum bandwidth (in bits per second) per client for voice.
# Grumble does not yet enforce this limit, but some clients nicely follow it.
-#MaxBandwidth = 72000
+#bandwidth = 72000
# Maximum number of concurrent clients.
-#MaxUsers = 1000
-#MaxUsersPerChannel = 0
+#users = 1000
+#usersperchannel = 0
-#MaxTextMessageLength = 5000
-#MaxImageMessageLength = 131072
-#AllowHTML
+#textmessagelength = 5000
+#imagemessagelength = 131072
+#allowhtml
-# DefaultChannel is the channel (by ID) new users join.
-# The root channel is the default.
-#DefaultChannel = 0
+# The default channel is the channel (by ID) new users join.
+# The root channel (ID = 0) is the default.
+#defaultchannel = 0
# Whether users will rejoin the last channel they were in.
-#RememberChannel
+#rememberchannel
+
+# Whether to include server OS info in ping response.
+#sendversion
-# Whether to include server version and server os in ping response.
-#SendVersion
-#SendOSInfo
+# Whether to respond to pings from the Connect dialog.
+#allowping
# Path to the log file (relative to the data directory).
-#LogPath = grumble.log
+#logfile = grumble.log
# Path to TLS certificate and key (relative to the data directory).
-# The certificate needs to have the entire chain concatenated to be validate.
+# The certificate needs to have the entire chain concatenated to be valid.
# If these paths do not exist, Grumble will autogenerate a certificate.
-#CertPath = cert.pem
-#KeyPath = key.pem
+#sslCert = cert.pem
+#sslKey = key.pem
# Options for public server registration.
# All of these have to be set to make the server public.
-# RegisterName additionally sets the name of the root channel.
-# RegisterPassword is a simple, arbitrary secret to guard your registration. Don't lose it.
-#RegisterName =
-#RegisterHost =
-#RegisterPassword =
-#RegisterWebUrl =
+# registerName additionally sets the name of the root channel.
+# registerPassword is a simple, arbitrary secret to guard your registration. Don't lose it.
+#registerName =
+#registerHostname =
+#registerPassword =
+#registerUrl =
`
diff --git a/pkg/sessionpool/sessionpool.go b/pkg/sessionpool/sessionpool.go
index 3e6c2ad..8bd524f 100644
--- a/pkg/sessionpool/sessionpool.go
+++ b/pkg/sessionpool/sessionpool.go
@@ -6,6 +6,7 @@
package sessionpool
import (
+ "errors"
"math"
"sync"
)
@@ -17,11 +18,17 @@ type SessionPool struct {
used map[uint32]bool
unused []uint32
cur uint32
+ max uint32
}
// Create a new SessionPool container.
-func New() (pool *SessionPool) {
+func New(max uint32) (pool *SessionPool) {
pool = new(SessionPool)
+ if max == 0 {
+ pool.max = math.MaxUint32
+ } else {
+ pool.max = max
+ }
return
}
@@ -41,7 +48,7 @@ func (pool *SessionPool) EnableUseTracking() {
// Get a new session ID from the SessionPool.
// Must be reclaimed using Reclaim() when done using it.
-func (pool *SessionPool) Get() (id uint32) {
+func (pool *SessionPool) Get() (id uint32, err error) {
pool.mutex.Lock()
defer pool.mutex.Unlock()
@@ -60,11 +67,12 @@ func (pool *SessionPool) Get() (id uint32) {
return
}
- // Check for depletion. If cur is MaxUint32,
+ // Check for depletion. If cur is max,
// there aren't any session IDs left, since the
- // increment below would overflow us back to 0.
- if pool.cur == math.MaxUint32 {
- panic("SessionPool depleted")
+ // increment below would return an out of range ID.
+ if pool.cur == pool.max {
+ err = errors.New("depleted session pool")
+ return
}
// Increment the next session id and return it.
diff --git a/pkg/sessionpool/sessionpool_test.go b/pkg/sessionpool/sessionpool_test.go
index 1f0b2c0..da9e8b1 100644
--- a/pkg/sessionpool/sessionpool_test.go
+++ b/pkg/sessionpool/sessionpool_test.go
@@ -6,35 +6,41 @@ import (
)
func TestReclaim(t *testing.T) {
- pool := New()
- id := pool.Get()
+ pool := New(2)
+ id, err := pool.Get()
+ if err != nil {
+ t.Errorf("Expected no error: %v", err)
+ }
if id != 1 {
t.Errorf("Got %v, expected 1 (first time)", id)
}
pool.Reclaim(1)
- id = pool.Get()
+ id, err = pool.Get()
+ if err != nil {
+ t.Errorf("Expected no error: %v", err)
+ }
if id != 1 {
t.Errorf("Got %v, expected 1 (second time)", id)
}
- id = pool.Get()
+ id, err = pool.Get()
+ if err != nil {
+ t.Errorf("Expected no error: %v", err)
+ }
if id != 2 {
t.Errorf("Got %v, expected 2", id)
}
}
func TestDepletion(t *testing.T) {
- defer func() {
- r := recover()
- if r != "SessionPool depleted" {
- t.Errorf("Expected depletion panic")
- }
- }()
- pool := New()
+ pool := New(0)
pool.cur = math.MaxUint32
- pool.Get()
+ _, err := pool.Get()
+ if err == nil {
+ t.Errorf("Expected depletion error")
+ }
}
func TestUseTracking(t *testing.T) {
@@ -45,7 +51,7 @@ func TestUseTracking(t *testing.T) {
}
}()
- pool := New()
+ pool := New(0)
pool.EnableUseTracking()
pool.Reclaim(42)
}