-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
137 lines (111 loc) · 3.6 KB
/
utils.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
package websocket
import (
"bufio"
"crypto/sha1"
"encoding/base64"
"encoding/binary"
"io"
"math/rand"
"strings"
"time"
)
// wsAcceptSalt is the GUID used by the WebSocket protocol to generate the
// value for the "Sec-Websocket-Accept" response HTTP Header field.
const wsAcceptSalt string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
// makeAcceptKey is used to generate the Accept Key which is then sent to the
// client using the 'Sec-Websocket-Accept' Response Header Field. This is used
// to prevent an attacker from ticking the server.
//
// Ref Spec: https://tools.ietf.org/html/rfc6455#section-1.3
func makeAcceptKey(k string) string {
h := sha1.New()
io.WriteString(h, k+wsAcceptSalt)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// readFromBuffer reads from the buffer (b) provided the number of specified
// bytes (l).
func readFromBuffer(b *bufio.Reader, l uint64) ([]byte, error) {
p := make([]byte, l)
// If the number of buffered bytes will accommodate the number of bytes
// requested, read once and return the read bytes.
if uint64(b.Buffered()) >= l {
_, err := b.Read(p)
return p, err
}
// If the user requires more bytes than there is buffered, the buffer will
// be read from multiple times.
// Total number of bytes read from buffer.
n := 0
for {
// Temporary slice of bytes.
t := make([]byte, l)
// Read from buffer and put read bytes in temporary slice of bytes.
i, err := b.Read(t)
if err != nil {
return nil, err
}
// Append bytes to the slice of bytes to be returned.
p = append(p[:n], t[:i]...)
// Increment the total number of bytes with the bytes read.
n += i
// If the total number of bytes is the same as the number of bytes
// requested, stop read operation and read bytes.
if uint64(n) == l {
break
}
}
return p, nil
}
// stringExists is a utility function used to check whether a slice of string
// ('l') contains a particular value ('k'). If it does, its position will be
// returned otherwise '-1' is returned.
func stringExists(l []string, k string) int {
for i, v := range l {
if k == v {
return i
}
}
return -1
}
// headerToSlice is used to turn the values of a multi value HTTP Header field
// to a slice of string.
//
// From RFC2616: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
func headerToSlice(v string) []string {
l := strings.Split(v, ",")
for i, v := range l {
l[i] = strings.Trim(v, " ")
}
return l
}
// randomByteSlice is used to generate a byte slice of random 32 bit integers.
func randomByteSlice(i int) []byte {
// Slice of bytes which will grow to be 16 bytes in length once the
// operation is ready. This slice will then be used to generate the key to
// be sent with the clients opening handshake using the Sec-Websocket-Key
// Header.
var b []byte
// Set seed.
rand.Seed(time.Now().UnixNano())
// The challenge key must be 16 bytes in length.
for l := 0; l < i; l++ {
// Temp slice
t := make([]byte, 4)
// Generate a random 32bit number and store its binary value in 't'.
binary.BigEndian.PutUint32(t, rand.Uint32())
// Finally append the random generated number to 'b'.
b = append(b, t...)
}
return b
}
// closeErrorExist returns whether the error number provided as an argument is
// a valid error number or not.
func closeErrorExist(i int) bool {
switch i {
case CloseNormalClosure, CloseGoingAway, CloseProtocolError, CloseUnsupportedData, CloseNoStatusReceived, CloseAbnormalClosure, CloseInvalidFramePayloadData, ClosePolicyViolation, CloseMessageTooBig, CloseMandatoryExtension, CloseInternalServerErr, CloseTLSHandshake:
{
return true
}
}
return false
}