mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -08:00
Minor features for parity with murmur.ini
MaxUsers: modifies existing sessionpool similar to Murmur MaxUsersPerChannel: already implemented, inconsistent name AllowPing: affects registration, too DefaultChannel RememberChannel ServerPassword SendOSInfo: already implemented, inconsistent name Config keys are renamed to conform to murmur.ini
This commit is contained in:
parent
693dd6f4e8
commit
ae41a612ba
12 changed files with 241 additions and 109 deletions
|
|
@ -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)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
77
pkg/serverconf/cipherlist.go
Normal file
77
pkg/serverconf/cipherlist.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 <b>Grumble</b>.",
|
||||
"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 <b>Grumble</b>.",
|
||||
"sendversion": "true",
|
||||
"allowping": "true",
|
||||
"logfile": "grumble.log",
|
||||
"sslCert": "cert.pem",
|
||||
"sslKey": "key.pem",
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <b>Grumble</b>."
|
||||
#welcometext = "Welcome to this server running <b>Grumble</b>."
|
||||
|
||||
# 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 version and server os in ping response.
|
||||
#SendVersion
|
||||
#SendOSInfo
|
||||
# Whether to include server OS info in ping response.
|
||||
#sendversion
|
||||
|
||||
# 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 =
|
||||
`
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue