-
Notifications
You must be signed in to change notification settings - Fork 29
/
server.go
148 lines (135 loc) · 3.6 KB
/
server.go
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package rcon
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
"github.com/golang/protobuf/proto"
"io/ioutil"
"log"
"net"
"strings"
)
type Server struct {
Clients map[string]*Client
TrustedKeys map[string]*rsa.PublicKey
ConnectionHandler func(net.Conn)
}
type Client struct {
Id string
Conn net.Conn
Token string
PubKey *rsa.PublicKey
}
func NewServer() (s *Server) {
s = new(Server)
s.Clients = map[string]*Client{}
s.TrustedKeys = map[string]*rsa.PublicKey{}
return
}
func (s *Server) Listen(address string) error {
listener, err := net.Listen("tcp", address)
if err != nil {
return err
}
defer listener.Close()
log.Print("Listening on ", address)
for {
conn, err := listener.Accept()
if err != nil {
log.Print("Could not accept connection: ", err)
} else {
go s.ConnectionHandler(conn)
}
}
}
func (s *Server) CloseConnection(conn net.Conn, id string) {
delete(s.Clients, id)
log.Printf("Connection to %v closed", id)
}
func (c *Client) Ok() {
reply := refproto.ControllerReply{}
reply.StatusCode = new(refproto.ControllerReply_StatusCode)
*reply.StatusCode = refproto.ControllerReply_OK
reply.Verification = new(refproto.ControllerReply_Verification)
if c.Token != "" {
reply.NextToken = new(string)
*reply.NextToken = c.Token
*reply.Verification = refproto.ControllerReply_VERIFIED
} else {
*reply.Verification = refproto.ControllerReply_UNVERIFIED
}
if err := sslconn.SendMessage(c.Conn, &reply); err != nil {
log.Print("Failed to send reply: ", err)
}
}
func (c *Client) Reject(reason string) {
log.Print("Reject connection: " + reason)
reply := refproto.ControllerReply{}
reply.StatusCode = new(refproto.ControllerReply_StatusCode)
*reply.StatusCode = refproto.ControllerReply_REJECTED
reply.Reason = new(string)
*reply.Reason = reason
if err := sslconn.SendMessage(c.Conn, &reply); err != nil {
log.Print("Failed to send reply: ", err)
}
}
func (s *Server) LoadTrustedKeys(trustedKeysDir string) {
files, err := ioutil.ReadDir(trustedKeysDir)
if err != nil {
log.Print("Could not read trusted keys: ", err)
return
}
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".pub.pem") {
pubKey := readPublicKey(trustedKeysDir + "/" + file.Name())
if pubKey != nil {
name := strings.Replace(file.Name(), ".pub.pem", "", 1)
s.TrustedKeys[name] = pubKey
log.Printf("Loaded public key for %v", name)
}
}
}
log.Printf("Loaded %v public keys", len(s.TrustedKeys))
}
func readPublicKey(filename string) *rsa.PublicKey {
b, err := ioutil.ReadFile(filename)
if err != nil {
log.Print("Could not find private key at ", filename)
return nil
}
p, rest := pem.Decode(b)
if p == nil {
log.Print("Could not decode public key. Remaining data: ", string(rest))
return nil
}
if p.Type != "PUBLIC KEY" {
log.Print("Public key type is wrong: ", p.Type)
return nil
}
publicKey, err := x509.ParsePKIXPublicKey(p.Bytes)
if err != nil {
log.Print("Failed to parse public key: ", err)
return nil
}
switch pub := publicKey.(type) {
case *rsa.PublicKey:
return pub
default:
log.Print("Unsupported public key: ", pub)
return nil
}
}
func VerifySignature(key *rsa.PublicKey, message proto.Message, signature []byte) error {
messageBytes, err := proto.Marshal(message)
if err != nil {
log.Fatal("Failed to marshal message: ", err)
}
hash := sha256.New()
hash.Write(messageBytes)
d := hash.Sum(nil)
return rsa.VerifyPKCS1v15(key, crypto.SHA256, d, signature)
}