2020-11-05 04:45:16 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2020-11-05 04:45:16 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ListCarves lists the file carving sessions
|
2021-06-06 22:07:29 +00:00
|
|
|
func (c *Client) ListCarves(opt fleet.CarveListOptions) ([]*fleet.CarveMetadata, error) {
|
2021-02-10 20:13:11 +00:00
|
|
|
endpoint := "/api/v1/fleet/carves"
|
2020-11-05 04:45:16 +00:00
|
|
|
rawQuery := ""
|
|
|
|
if opt.Expired {
|
|
|
|
rawQuery = "expired=1"
|
|
|
|
}
|
|
|
|
response, err := c.AuthenticatedDo("GET", endpoint, rawQuery, nil)
|
|
|
|
if err != nil {
|
2021-02-10 20:13:11 +00:00
|
|
|
return nil, errors.Wrap(err, "GET /api/v1/fleet/carves")
|
2020-11-05 04:45:16 +00:00
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
return nil, errors.Errorf(
|
|
|
|
"list carves received status %d %s",
|
|
|
|
response.StatusCode,
|
|
|
|
extractServerErrorText(response.Body),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
var responseBody listCarvesResponse
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "decode get carves response")
|
|
|
|
}
|
|
|
|
if responseBody.Err != nil {
|
|
|
|
return nil, errors.Errorf("get carves: %s", responseBody.Err)
|
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
carves := []*fleet.CarveMetadata{}
|
2020-11-05 04:45:16 +00:00
|
|
|
for _, carve := range responseBody.Carves {
|
|
|
|
c := carve
|
|
|
|
carves = append(carves, &c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return carves, nil
|
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (c *Client) GetCarve(carveId int64) (*fleet.CarveMetadata, error) {
|
2021-02-10 20:13:11 +00:00
|
|
|
endpoint := fmt.Sprintf("/api/v1/fleet/carves/%d", carveId)
|
2020-11-05 04:45:16 +00:00
|
|
|
response, err := c.AuthenticatedDo("GET", endpoint, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "GET "+endpoint)
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
return nil, errors.Errorf(
|
|
|
|
"get carve received status %d %s",
|
|
|
|
response.StatusCode,
|
|
|
|
extractServerErrorText(response.Body),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
var responseBody getCarveResponse
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "decode carve response")
|
|
|
|
}
|
|
|
|
if responseBody.Err != nil {
|
|
|
|
return nil, errors.Errorf("get carve: %s", responseBody.Err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &responseBody.Carve, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getCarveBlock(carveId, blockId int64) ([]byte, error) {
|
|
|
|
path := fmt.Sprintf(
|
2021-02-10 20:13:11 +00:00
|
|
|
"/api/v1/fleet/carves/%d/block/%d",
|
2020-11-05 04:45:16 +00:00
|
|
|
carveId,
|
|
|
|
blockId,
|
|
|
|
)
|
|
|
|
response, err := c.AuthenticatedDo("GET", path, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "GET %s", path)
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
return nil, errors.Errorf(
|
|
|
|
"get carve block received status %d: %s",
|
|
|
|
response.StatusCode,
|
|
|
|
extractServerErrorText(response.Body),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
var responseBody getCarveBlockResponse
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "decode get carve block response")
|
|
|
|
}
|
|
|
|
if responseBody.Err != nil {
|
|
|
|
return nil, errors.Errorf("get carve block: %s", responseBody.Err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return responseBody.Data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type carveReader struct {
|
2021-06-06 22:07:29 +00:00
|
|
|
carve fleet.CarveMetadata
|
2020-11-05 04:45:16 +00:00
|
|
|
bytesRead int64
|
2020-12-16 17:16:55 +00:00
|
|
|
curBlock int64
|
|
|
|
buffer []byte
|
|
|
|
client *Client
|
2020-11-05 04:45:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func newCarveReader(carve fleet.CarveMetadata, client *Client) *carveReader {
|
2020-11-05 04:45:16 +00:00
|
|
|
return &carveReader{
|
2020-12-16 17:16:55 +00:00
|
|
|
carve: carve,
|
|
|
|
client: client,
|
2020-11-05 04:45:16 +00:00
|
|
|
bytesRead: 0,
|
|
|
|
curBlock: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 17:16:55 +00:00
|
|
|
func (r *carveReader) Read(p []byte) (n int, err error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
if len(p) == 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.bytesRead >= r.carve.CarveSize {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load data from API if necessary
|
|
|
|
if len(r.buffer) == 0 {
|
|
|
|
var err error
|
|
|
|
r.buffer, err = r.client.getCarveBlock(r.carve.ID, r.curBlock)
|
|
|
|
if err != nil {
|
|
|
|
return 0, errors.Wrapf(err, "get block %d", r.curBlock)
|
|
|
|
}
|
|
|
|
r.curBlock++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate length we can copy
|
|
|
|
copyLen := len(p)
|
|
|
|
if copyLen > len(r.buffer) {
|
|
|
|
copyLen = len(r.buffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform copy and clear copied contents from buffer
|
|
|
|
copy(p, r.buffer[:copyLen])
|
|
|
|
r.buffer = r.buffer[copyLen:]
|
|
|
|
|
|
|
|
r.bytesRead += int64(copyLen)
|
|
|
|
|
|
|
|
return copyLen, nil
|
|
|
|
}
|
|
|
|
|
2020-12-16 17:16:55 +00:00
|
|
|
// DownloadCarve creates a Reader downloading a carve (by ID)
|
2020-11-05 04:45:16 +00:00
|
|
|
func (c *Client) DownloadCarve(id int64) (io.Reader, error) {
|
2021-02-10 20:13:11 +00:00
|
|
|
path := fmt.Sprintf("/api/v1/fleet/carves/%d", id)
|
2020-11-05 04:45:16 +00:00
|
|
|
response, err := c.AuthenticatedDo("GET", path, "", nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "GET %s", path)
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
return nil, errors.Errorf(
|
|
|
|
"download carve received status %d: %s",
|
|
|
|
response.StatusCode,
|
|
|
|
extractServerErrorText(response.Body),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
var responseBody getCarveResponse
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "decode get carve by name response")
|
|
|
|
}
|
|
|
|
if responseBody.Err != nil {
|
|
|
|
return nil, errors.Errorf("get carve by name: %s", responseBody.Err)
|
|
|
|
}
|
|
|
|
|
|
|
|
reader := newCarveReader(responseBody.Carve, c)
|
|
|
|
|
|
|
|
return reader, nil
|
|
|
|
}
|