-
Notifications
You must be signed in to change notification settings - Fork 80
/
client.go
275 lines (228 loc) · 9.67 KB
/
client.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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package osquery
import (
"context"
"time"
"github.com/osquery/osquery-go/gen/osquery"
"github.com/osquery/osquery-go/traces"
"github.com/osquery/osquery-go/transport"
"github.com/apache/thrift/lib/go/thrift"
"github.com/pkg/errors"
)
const (
defaultWaitTime = 200 * time.Millisecond
defaultMaxWaitTime = 1 * time.Minute
)
// ExtensionManagerClient is a wrapper for the osquery Thrift extensions API.
type ExtensionManagerClient struct {
client osquery.ExtensionManager
transport thrift.TTransport
waitTime time.Duration
maxWaitTime time.Duration
lock *locker
}
type ClientOption func(*ExtensionManagerClient)
// WaitTime sets the default amount of wait time for the osquery socket to free up. You can override this on a per
// call basis by setting a context deadline
func DefaultWaitTime(d time.Duration) ClientOption {
return func(c *ExtensionManagerClient) {
c.waitTime = d
}
}
// MaxWaitTime is the maximum amount of time something is allowed to wait for the osquery socket. This takes precedence
// over the context deadline.
func MaxWaitTime(d time.Duration) ClientOption {
return func(c *ExtensionManagerClient) {
c.maxWaitTime = d
}
}
// NewClient creates a new client communicating to osquery over the socket at
// the provided path. If resolving the address or connecting to the socket
// fails, this function will error.
func NewClient(path string, socketOpenTimeout time.Duration, opts ...ClientOption) (*ExtensionManagerClient, error) {
c := &ExtensionManagerClient{
waitTime: defaultWaitTime,
maxWaitTime: defaultMaxWaitTime,
}
for _, opt := range opts {
opt(c)
}
if c.waitTime > c.maxWaitTime {
return nil, errors.New("default wait time larger than max wait time")
}
c.lock = NewLocker(c.waitTime, c.maxWaitTime)
if c.client == nil {
trans, err := transport.Open(path, socketOpenTimeout)
if err != nil {
return nil, err
}
c.client = osquery.NewExtensionManagerClientFactory(
trans,
thrift.NewTBinaryProtocolFactoryDefault(),
)
}
return c, nil
}
// Close should be called to close the transport when use of the client is
// completed.
func (c *ExtensionManagerClient) Close() {
if c.transport != nil && c.transport.IsOpen() {
c.transport.Close()
}
}
// Ping requests metadata from the extension manager, using a new background context
func (c *ExtensionManagerClient) Ping() (*osquery.ExtensionStatus, error) {
return c.PingContext(context.Background())
}
// PingContext requests metadata from the extension manager.
func (c *ExtensionManagerClient) PingContext(ctx context.Context) (*osquery.ExtensionStatus, error) {
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.Ping(ctx)
}
// Call requests a call to an extension (or core) registry plugin, using a new background context
func (c *ExtensionManagerClient) Call(registry, item string, request osquery.ExtensionPluginRequest) (*osquery.ExtensionResponse, error) {
return c.CallContext(context.Background(), registry, item, request)
}
// CallContext requests a call to an extension (or core) registry plugin.
func (c *ExtensionManagerClient) CallContext(ctx context.Context, registry, item string, request osquery.ExtensionPluginRequest) (*osquery.ExtensionResponse, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.CallContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.Call(ctx, registry, item, request)
}
// Extensions requests the list of active registered extensions, using a new background context
func (c *ExtensionManagerClient) Extensions() (osquery.InternalExtensionList, error) {
return c.ExtensionsContext(context.Background())
}
// ExtensionsContext requests the list of active registered extensions.
func (c *ExtensionManagerClient) ExtensionsContext(ctx context.Context) (osquery.InternalExtensionList, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.ExtensionsContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.Extensions(ctx)
}
// RegisterExtension registers the extension plugins with the osquery process, using a new background context
func (c *ExtensionManagerClient) RegisterExtension(info *osquery.InternalExtensionInfo, registry osquery.ExtensionRegistry) (*osquery.ExtensionStatus, error) {
return c.RegisterExtensionContext(context.Background(), info, registry)
}
// RegisterExtensionContext registers the extension plugins with the osquery process.
func (c *ExtensionManagerClient) RegisterExtensionContext(ctx context.Context, info *osquery.InternalExtensionInfo, registry osquery.ExtensionRegistry) (*osquery.ExtensionStatus, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.RegisterExtensionContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.RegisterExtension(ctx, info, registry)
}
// DeregisterExtension de-registers the extension plugins with the osquery process, using a new background context
func (c *ExtensionManagerClient) DeregisterExtension(uuid osquery.ExtensionRouteUUID) (*osquery.ExtensionStatus, error) {
return c.DeregisterExtensionContext(context.Background(), uuid)
}
// DeregisterExtensionContext de-registers the extension plugins with the osquery process.
func (c *ExtensionManagerClient) DeregisterExtensionContext(ctx context.Context, uuid osquery.ExtensionRouteUUID) (*osquery.ExtensionStatus, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.DeregisterExtensionContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.DeregisterExtension(ctx, uuid)
}
// Options requests the list of bootstrap or configuration options, using a new background context.
func (c *ExtensionManagerClient) Options() (osquery.InternalOptionList, error) {
return c.OptionsContext(context.Background())
}
// OptionsContext requests the list of bootstrap or configuration options.
func (c *ExtensionManagerClient) OptionsContext(ctx context.Context) (osquery.InternalOptionList, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.OptionsContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.Options(ctx)
}
// Query requests a query to be run and returns the extension
// response, using a new background context. Consider using the
// QueryRow or QueryRows helpers for a more friendly interface.
func (c *ExtensionManagerClient) Query(sql string) (*osquery.ExtensionResponse, error) {
return c.QueryContext(context.Background(), sql)
}
// QueryContext requests a query to be run and returns the extension response.
// Consider using the QueryRow or QueryRows helpers for a more friendly
// interface.
func (c *ExtensionManagerClient) QueryContext(ctx context.Context, sql string) (*osquery.ExtensionResponse, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.QueryContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.Query(ctx, sql)
}
// QueryRows is a helper that executes the requested query and returns the
// results. It handles checking both the transport level errors and the osquery
// internal errors by returning a normal Go error type.
func (c *ExtensionManagerClient) QueryRows(sql string) ([]map[string]string, error) {
return c.QueryRowsContext(context.Background(), sql)
}
// QueryRowsContext is a helper that executes the requested query and returns the
// results. It handles checking both the transport level errors and the osquery
// internal errors by returning a normal Go error type.
func (c *ExtensionManagerClient) QueryRowsContext(ctx context.Context, sql string) ([]map[string]string, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.QueryRowsContext")
defer span.End()
res, err := c.QueryContext(ctx, sql)
if err != nil {
return nil, errors.Wrap(err, "transport error in query")
}
if res.Status == nil {
return nil, errors.New("query returned nil status")
}
if res.Status.Code != 0 {
return nil, errors.Errorf("query returned error: %s", res.Status.Message)
}
return res.Response, nil
}
// QueryRow behaves similarly to QueryRows, but it returns an error if the
// query does not return exactly one row.
func (c *ExtensionManagerClient) QueryRow(sql string) (map[string]string, error) {
return c.QueryRowContext(context.Background(), sql)
}
// QueryRowContext behaves similarly to QueryRows, but it returns an error if the
// query does not return exactly one row.
func (c *ExtensionManagerClient) QueryRowContext(ctx context.Context, sql string) (map[string]string, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.QueryRowContext")
defer span.End()
res, err := c.QueryRowsContext(ctx, sql)
if err != nil {
return nil, err
}
if len(res) != 1 {
return nil, errors.Errorf("expected 1 row, got %d", len(res))
}
return res[0], nil
}
// GetQueryColumns requests the columns returned by the parsed query, using a new background context.
func (c *ExtensionManagerClient) GetQueryColumns(sql string) (*osquery.ExtensionResponse, error) {
return c.GetQueryColumnsContext(context.Background(), sql)
}
// GetQueryColumnsContext requests the columns returned by the parsed query.
func (c *ExtensionManagerClient) GetQueryColumnsContext(ctx context.Context, sql string) (*osquery.ExtensionResponse, error) {
ctx, span := traces.StartSpan(ctx, "ExtensionManagerClient.GetQueryColumnsContext")
defer span.End()
if err := c.lock.Lock(ctx); err != nil {
return nil, err
}
defer c.lock.Unlock()
return c.client.GetQueryColumns(ctx, sql)
}