forked from Velocidex/velociraptor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnetcat.go
152 lines (128 loc) · 3.48 KB
/
netcat.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
package networking
import (
"context"
"net"
"strings"
"time"
"github.com/Velocidex/ordereddict"
"www.velocidex.com/golang/velociraptor/acls"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
vfilter "www.velocidex.com/golang/vfilter"
"www.velocidex.com/golang/vfilter/arg_parser"
)
type NetcatPluginArgs struct {
Address string `vfilter:"required,field=address,doc=The address to connect to (can be a file in case of a unix domain socket)"`
AddressType string `vfilter:"optional,field=type,doc=Can be tcp or unix (default TCP)"`
Send string `vfilter:"optional,field=send,doc=Data to send before reading"`
Sep string `vfilter:"optional,field=sep,doc=The separator that will be used to split (default - line feed)"`
Chunk int `vfilter:"optional,field=chunk_size,doc=Read input with this chunk size (default 64kb)"`
Retry int `vfilter:"optional,field=retry,doc=Seconds to wait before retry - default 0 - do not retry"`
}
type NetcatPlugin struct{}
func (self *NetcatPlugin) Call(
ctx context.Context,
scope vfilter.Scope,
args *ordereddict.Dict) <-chan vfilter.Row {
output_chan := make(chan vfilter.Row)
go func() {
defer close(output_chan)
arg := &NetcatPluginArgs{}
err := arg_parser.ExtractArgsWithContext(ctx, scope, args, arg)
if err != nil {
scope.Log("netcat: %s", err)
return
}
err = vql_subsystem.CheckAccess(scope, acls.COLLECT_SERVER)
if err != nil {
scope.Log("netcat: %s", err)
return
}
for {
self.connectOnce(ctx, scope, arg, output_chan)
if arg.Retry == 0 {
break
}
time.Sleep(time.Duration(arg.Retry) * time.Second)
}
}()
return output_chan
}
func (self NetcatPlugin) connectOnce(
ctx context.Context, scope vfilter.Scope, arg *NetcatPluginArgs,
output_chan chan vfilter.Row) {
socket_type := arg.AddressType
switch socket_type {
case "":
socket_type = "tcp"
case "tcp", "unix", "udp":
default:
scope.Log("netcat: unsupported address type (%v)", arg.AddressType)
return
}
var d net.Dialer
conn, err := d.DialContext(ctx, socket_type, arg.Address)
if err != nil {
scope.Log("netcat: %s", err)
return
}
defer conn.Close()
if arg.Send != "" {
go func() {
_, err := conn.Write([]byte(arg.Send))
if err != nil {
scope.Log("netcat: %s", err)
}
}()
}
sep := arg.Sep
if sep == "" {
sep = "\n"
}
chunk_size := arg.Chunk
if chunk_size == 0 {
chunk_size = 64 * 1024
}
if chunk_size > 10*1024*1024 {
chunk_size = 10 * 1024 * 1024
}
buf := make([]byte, chunk_size)
offset := 0
for {
n, err := conn.Read(buf[offset:])
if err != nil {
return
}
lines := strings.Split(string(buf[:offset+n]), sep)
for idx, line := range lines {
if idx == len(lines)-1 {
// If last line is non empty the buffer does not
// end with a separator, copy the last line to the
// old buffer and start reading from there.
last_line := lines[idx]
if last_line != "" {
copy(buf, []byte(last_line))
offset = len(last_line)
}
continue
}
if line == "" {
continue
}
select {
case <-ctx.Done():
return
case output_chan <- ordereddict.NewDict().Set("Data", line):
}
}
}
}
func (self NetcatPlugin) Info(scope vfilter.Scope, type_map *vfilter.TypeMap) *vfilter.PluginInfo {
return &vfilter.PluginInfo{
Name: "netcat",
Doc: "Make a tcp connection and read data from a socket.",
ArgType: type_map.AddType(scope, &NetcatPluginArgs{}),
}
}
func init() {
vql_subsystem.RegisterPlugin(&NetcatPlugin{})
}