1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
// Copyright (c) 2011 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
// This file handles public server list registration
import (
"bytes"
"crypto/sha1"
"crypto/tls"
"encoding/hex"
"encoding/xml"
"io/ioutil"
"net/http"
)
type Register struct {
XMLName xml.Name `xml:"server"`
Version string `xml:"version"`
Release string `xml:"release"`
Name string `xml:"name"`
Host string `xml:"host"`
Password string `xml:"password"`
Port int `xml:"port"`
Url string `xml:"url"`
Digest string `xml:"digest"`
Users int `xml:"users"`
Channels int `xml:"channels"`
Location string `xml:"location"`
}
const registerUrl = "https://mumble.info/register.cgi"
// Determines whether a server is public by checking whether the
// config values required for public registration are set.
//
// 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 {
return false
}
if len(server.cfg.StringValue("RegisterHost")) == 0 {
return false
}
if len(server.cfg.StringValue("RegisterPassword")) == 0 {
return false
}
if len(server.cfg.StringValue("RegisterWebUrl")) == 0 {
return false
}
return true
}
// Perform a public server registration update.
//
// When a Mumble server connects to the master server
// for registration, it connects using its server certificate
// as a client certificate for authentication purposes.
func (server *Server) RegisterPublicServer() {
if !server.IsPublic() {
return
}
// Fetch the server's certificates and put them in a tls.Config.
// We need the certificate chain to be able to use it in our client
// certificate chain to the registration server, and we also need to
// include a digest of the leaf certiifcate in the registration XML document
// we send off to the server.
config := &tls.Config{}
for _, cert := range server.tlscfg.Certificates {
config.Certificates = append(config.Certificates, cert)
}
hasher := sha1.New()
hasher.Write(config.Certificates[0].Certificate[0])
digest := hex.EncodeToString(hasher.Sum(nil))
// 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"),
Port: server.CurrentPort(),
Digest: digest,
Users: len(server.clients),
Channels: len(server.Channels),
Version: "1.2.4",
Release: "Grumble Git",
}
buf := bytes.NewBuffer(nil)
err := xml.NewEncoder(buf).Encode(reg)
if err != nil {
server.Printf("register: unable to marshal xml: %v", err)
return
}
// Post registration XML data to server asynchronously in its own goroutine
go func() {
tr := &http.Transport{
TLSClientConfig: config,
}
client := &http.Client{Transport: tr}
r, err := client.Post(registerUrl, "text/xml", ioutil.NopCloser(buf))
if err != nil {
server.Printf("register: unable to post registration request: %v", err)
return
}
bodyBytes, err := ioutil.ReadAll(r.Body)
if err == nil {
registerMsg := string(bodyBytes)
if r.StatusCode == 200 {
server.Printf("register: %v", registerMsg)
} else {
server.Printf("register: (status %v) %v", r.StatusCode, registerMsg)
}
} else {
server.Printf("register: unable to read post response: %v", err)
return
}
}()
}
|