-
Notifications
You must be signed in to change notification settings - Fork 2
/
herots.go
210 lines (177 loc) · 4.31 KB
/
herots.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
HEROTS
HERald Of The Swarm
Package for fast TLS server creation.
*/
package herots
import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"net"
"os"
"strconv"
)
type Server struct {
options *Options
certs struct {
Cert tls.Certificate
pool struct {
IsSet bool
Pool *x509.CertPool
}
}
listener net.Listener
messagesDst io.Writer
}
// predefined errors messages
const (
LoadKeyPairError = "herots: load key pair error"
LoadClientCaCertError = "herots srv: load client CA cert error"
StartServerError = "herots srv: start tls server error"
NoKeyPairLoad = "herots: no load key pair (use LoadKeyPair func)"
AcceptConnError = "herots srv: connection accept error"
)
/*
A Options structure is used to configure a TLS server.
*/
type Options struct {
// Server host.
// By default server use "127.0.0.1".
Host string
// Server port.
// By default server use "9000".
Port int
// Message level provides the opportunity to choose
// what print to the output.
// 0 - no messages
// 1 - errors
// 2 - info messages
// 3 - all ("1" + "2")
// By default server use "2".
MessageLevel int
// See http://golang.org/pkg/crypto/tls/#ClientAuthType
// By default server use tls.RequireAnyClientCert
TLSAuthType tls.ClientAuthType
}
/*
Return Server struct with predefined options.
*/
func NewServer() *Server {
s := &Server{
options: &Options{
Host: "127.0.0.1",
Port: 9000,
MessageLevel: 0,
TLSAuthType: tls.RequireAnyClientCert,
},
}
s.messagesDst = os.Stdout // send msg to stdout by default
return s
}
// func for print messages
func (h *Server) msg(m string, lvl int) {
if h.options.MessageLevel == 0 {
return
}
if h.options.MessageLevel == 3 || h.options.MessageLevel == lvl {
fmt.Fprintf(h.messagesDst, "herots srv: %s\n", m)
}
}
/*
Provides the opportunity to choose own destination for
herots messages (errors, info, etc).
By default server use os.Stdout.
*/
func (h *Server) SetMessagesDst(dst io.Writer) {
h.messagesDst = dst
}
/*
Set herots server options (*Options).
*/
func (h *Server) Config(o *Options) {
// check mandatory options
if o.Port == 0 {
h.msg("port can't be '0'", 2)
h.msg("set port by default (9000)", 2)
o.Port = 9000
}
h.options = o
}
/*
Func for load certificate and private key pair.
Public/private key pair require as PEM encoded data.
*/
func (h *Server) LoadKeyPair(cert, key []byte) error {
// create cert pool
h.certs.pool.Pool = x509.NewCertPool()
// load keypair
c, err := tls.X509KeyPair(cert, key)
if err != nil {
return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
}
h.certs.Cert = c
// add cert to pool
pemData, _ := pem.Decode(cert)
ca, err := x509.ParseCertificate(pemData.Bytes)
if err != nil {
return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
}
h.certs.pool.Pool.AddCert(ca)
h.certs.pool.IsSet = true
h.msg("load key pair ok", 2)
return nil
}
/*
Add client CA certificate to x509.CertPool (tls.Config.ClientCAs).
By default server add cert from server public/private key pair (LoadKeyPair)
to cert pool.
*/
func (h *Server) AddClientCACert(cert []byte) error {
pemData, _ := pem.Decode(cert)
ca, err := x509.ParseCertificate(pemData.Bytes)
if err != nil {
return fmt.Errorf("%s: %v\n", LoadClientCaCertError, err)
}
h.certs.pool.Pool.AddCert(ca)
h.msg("load client CA cert ok", 2)
return nil
}
/*
Accept and return connection to server.
*/
func (h *Server) Accept() (net.Conn, error) {
conn, err := h.listener.Accept()
if err != nil {
h.msg("accept conn error: "+err.Error(), 1)
return conn, fmt.Errorf("%s: %v\n", AcceptConnError, err)
}
h.msg("accepted conn from "+conn.RemoteAddr().String(), 2)
return conn, nil
}
/*
Start server.
*/
func (h *Server) Start() error {
// load keypair check
if len(h.certs.Cert.Certificate) == 0 {
return fmt.Errorf("%s\n", NoKeyPairLoad)
}
config := tls.Config{
ClientAuth: h.options.TLSAuthType,
Certificates: []tls.Certificate{h.certs.Cert},
ClientCAs: h.certs.pool.Pool,
Rand: rand.Reader,
}
service := h.options.Host + ":" + strconv.Itoa(h.options.Port)
listener, err := tls.Listen("tcp", service, &config)
if err != nil {
return fmt.Errorf("%s: %v\n", StartServerError, err)
}
h.listener = listener
h.msg("listening on "+service, 2)
return nil
}