github.com/Uptycs/basequery-go@v0.8.0/client.go (about) 1 package osquery 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/Uptycs/basequery-go/gen/osquery" 8 "github.com/Uptycs/basequery-go/transport" 9 "github.com/pkg/errors" 10 11 "github.com/apache/thrift/lib/go/thrift" 12 ) 13 14 // ExtensionManager is the interface for thrift bindings supported by basequery 15 type ExtensionManager interface { 16 Close() 17 Ping() (*osquery.ExtensionStatus, error) 18 Call(registry, item string, req osquery.ExtensionPluginRequest) (*osquery.ExtensionResponse, error) 19 Extensions() (osquery.InternalExtensionList, error) 20 RegisterExtension(info *osquery.InternalExtensionInfo, registry osquery.ExtensionRegistry) (*osquery.ExtensionStatus, error) 21 Options() (osquery.InternalOptionList, error) 22 Query(sql string) (*osquery.ExtensionResponse, error) 23 GetQueryColumns(sql string) (*osquery.ExtensionResponse, error) 24 StreamEvents(name string, events osquery.ExtensionPluginResponse) (*osquery.ExtensionStatus, error) 25 GetNodeKey() (string, error) 26 } 27 28 // ExtensionManagerClient is a wrapper for the osquery Thrift extensions API. 29 type ExtensionManagerClient struct { 30 Client osquery.ExtensionManager 31 transport thrift.TTransport 32 } 33 34 // NewClient creates a new client communicating to osquery over the socket at 35 // the provided path. If resolving the address or connecting to the socket 36 // fails, this function will error. 37 func NewClient(path string, timeout time.Duration) (*ExtensionManagerClient, error) { 38 trans, err := transport.Open(path, timeout) 39 if err != nil { 40 return nil, err 41 } 42 43 client := osquery.NewExtensionManagerClientFactory( 44 trans, 45 thrift.NewTBinaryProtocolFactoryDefault(), 46 ) 47 48 return &ExtensionManagerClient{client, trans}, nil 49 } 50 51 // Close should be called to close the transport when use of the client is 52 // completed. 53 func (c *ExtensionManagerClient) Close() { 54 if c.transport != nil && c.transport.IsOpen() { 55 c.transport.Close() 56 } 57 } 58 59 // Ping requests metadata from the extension manager. 60 func (c *ExtensionManagerClient) Ping() (*osquery.ExtensionStatus, error) { 61 return c.Client.Ping(context.Background()) 62 } 63 64 // Call requests a call to an extension (or core) registry plugin. 65 func (c *ExtensionManagerClient) Call(registry, item string, request osquery.ExtensionPluginRequest) (*osquery.ExtensionResponse, error) { 66 return c.Client.Call(context.Background(), registry, item, request) 67 } 68 69 // Extensions requests the list of active registered extensions. 70 func (c *ExtensionManagerClient) Extensions() (osquery.InternalExtensionList, error) { 71 return c.Client.Extensions(context.Background()) 72 } 73 74 // RegisterExtension registers the extension plugins with the osquery process. 75 func (c *ExtensionManagerClient) RegisterExtension(info *osquery.InternalExtensionInfo, registry osquery.ExtensionRegistry) (*osquery.ExtensionStatus, error) { 76 return c.Client.RegisterExtension(context.Background(), info, registry) 77 } 78 79 // Options requests the list of bootstrap or configuration options. 80 func (c *ExtensionManagerClient) Options() (osquery.InternalOptionList, error) { 81 return c.Client.Options(context.Background()) 82 } 83 84 // Query requests a query to be run and returns the extension response. 85 // Consider using the QueryRow or QueryRows helpers for a more friendly 86 // interface. 87 func (c *ExtensionManagerClient) Query(sql string) (*osquery.ExtensionResponse, error) { 88 return c.Client.Query(context.Background(), sql) 89 } 90 91 // QueryRows is a helper that executes the requested query and returns the 92 // results. It handles checking both the transport level errors and the osquery 93 // internal errors by returning a normal Go error type. 94 func (c *ExtensionManagerClient) QueryRows(sql string) ([]map[string]string, error) { 95 res, err := c.Query(sql) 96 if err != nil { 97 return nil, errors.Wrap(err, "transport error in query") 98 } 99 if res.Status == nil { 100 return nil, errors.New("query returned nil status") 101 } 102 if res.Status.Code != 0 { 103 return nil, errors.Errorf("query returned error: %s", res.Status.Message) 104 } 105 return res.Response, nil 106 107 } 108 109 // QueryRow behaves similarly to QueryRows, but it returns an error if the 110 // query does not return exactly one row. 111 func (c *ExtensionManagerClient) QueryRow(sql string) (map[string]string, error) { 112 res, err := c.QueryRows(sql) 113 if err != nil { 114 return nil, err 115 } 116 if len(res) != 1 { 117 return nil, errors.Errorf("expected 1 row, got %d", len(res)) 118 } 119 return res[0], nil 120 } 121 122 // GetQueryColumns requests the columns returned by the parsed query. 123 func (c *ExtensionManagerClient) GetQueryColumns(sql string) (*osquery.ExtensionResponse, error) { 124 return c.Client.GetQueryColumns(context.Background(), sql) 125 } 126 127 // StreamEvents sends a batch of events for a event'ed table. 128 func (c *ExtensionManagerClient) StreamEvents(name string, events osquery.ExtensionPluginResponse) (*osquery.ExtensionStatus, error) { 129 return c.Client.StreamEvents(context.Background(), name, events) 130 } 131 132 // GetNodeKey returns TLS node key when enroll plugin is set to "tls". 133 func (c *ExtensionManagerClient) GetNodeKey() (string, error) { 134 return c.Client.GetNodeKey(context.Background()) 135 }