Implement flow file storage API, create HTP wrappers for it, use it in HTTP parsing.

remotes/origin/master-1.2.x
Victor Julien 15 years ago
parent a0ee6ade3e
commit 9d5d46c4bb

@ -62,6 +62,128 @@
#include "util-memcmp.h"
/**
* \brief Open the file with "filename" and pass the first chunk
* of data if any.
*
* \param f flow to store the file in
* \param filename name of the file
* \param filename_len length of the name
* \param data data chunk (if any)
* \param data_len length of the data portion
*
* \retval 0 ok
* \retval -1 error
*/
int HTPFileOpen(Flow *f, uint8_t *filename, uint16_t filename_len,
uint8_t *data, uint32_t data_len)
{
int retval = 0;
if (f == NULL) {
return -1;
}
SCMutexLock(&f->files_m);
{
if (f->files == NULL) {
f->files = FlowFileContainerAlloc();
if (f->files == NULL) {
retval = -1;
goto end;
}
}
if (FlowFileOpenFile(f->files, filename, filename_len,
data, data_len) == NULL)
{
retval = -1;
}
}
end:
SCMutexUnlock(&f->files_m);
return retval;
}
/**
* \brief Store a chunk of data in the flow
*
* \param f flow to store the file in
* \param data data chunk (if any)
* \param data_len length of the data portion
*
* \retval 0 ok
* \retval -1 error
*/
int HTPFileStoreChunk(Flow *f, uint8_t *data, uint32_t data_len) {
SCEnter();
int retval = 0;
if (f == NULL) {
SCReturnInt(-1);
}
SCMutexLock(&f->files_m);
{
if (f->files == NULL) {
SCLogDebug("no files in flow");
retval = -1;
goto end;
}
if (FlowFileAppendData(f->files, data, data_len) == -1)
{
SCLogDebug("appending data failed");
retval = -1;
}
}
end:
SCMutexUnlock(&f->files_m);
SCReturnInt(retval);
}
/**
* \brief Close the file in the flow
*
* \param f flow to store in
* \param data data chunk if any
* \param data_len length of the data portion
* \param flags flags to indicate events
*
* Currently on the FLOW_FILE_TRUNCATED flag is implemented, indicating
* that the file isn't complete but we're stopping storing it.
*
* \retval 0 ok
* \retval -1 error
*/
int HTPFileClose(Flow *f, uint8_t *data, uint32_t data_len, uint8_t flags) {
int retval = 0;
if (f == NULL) {
return -1;
}
SCMutexLock(&f->files_m);
{
if (f->files == NULL) {
retval = -1;
goto end;
}
if (FlowFileCloseFile(f->files, data, data_len, flags) == -1)
{
retval = -1;
}
}
end:
SCMutexUnlock(&f->files_m);
return retval;
}
#ifdef UNITTESTS
static int HTPFileParserTest01(void) {
int result = 0;
@ -456,7 +578,11 @@ static int HTPFileParserTest04(void) {
if (tx->request_method == NULL || memcmp(bstr_tocstr(tx->request_method), "POST", 4) != 0)
{
printf("expected method POST, got %s \n", bstr_tocstr(tx->request_method));
printf("expected method POST, got %s: ", bstr_tocstr(tx->request_method));
goto end;
}
if (f.files == NULL || f.files->tail == NULL || f.files->tail->state != FLOWFILE_STATE_CLOSED) {
goto end;
}

@ -25,6 +25,10 @@
#ifndef __APP_LAYER_HTP_FILE_H__
#define __APP_LAYER_HTP_FILE_H__
int HTPFileOpen(Flow *, uint8_t *, uint16_t, uint8_t *, uint32_t);
int HTPFileStoreChunk(Flow *, uint8_t *, uint32_t);
int HTPFileClose(Flow *, uint8_t *, uint32_t, uint8_t);
void HTPFileParserRegisterTests(void);
#endif /* __APP_LAYER_HTP_FILE_H__ */

@ -24,6 +24,7 @@
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
* \author Brian Rectanus <brectanu@gmail.com>
@ -780,84 +781,6 @@ static int HTTPParseContentTypeHeader(uint8_t *name, size_t name_len,
SCReturnInt(0);
}
static int HTTPStoreFileNameType(Flow *f, uint8_t *filename, size_t filename_len,
uint8_t *filetype, size_t filetype_len)
{
if (filename == NULL) {
SCReturnInt(-1);
}
int i = 0;
FlowFileContainer *ffc = NULL;
SCMutexLock(&f->files_m);
{
/* We have to add/update the file */
SCLogDebug("Adding file entry to flow");
if (f->files == NULL) {
ffc = FlowFileContainerAlloc();
if (ffc == NULL) {
goto end;
}
f->files = ffc;
} else {
/* TODO: else append chunk, update things...*/
ffc = f->files;
}
FlowFile *cur_file = FlowFileContainerRetrieve(ffc, ALPROTO_HTTP, filename,
filename_len);
SCLogDebug("cur_file %p", cur_file);
if (cur_file == NULL) {
cur_file = FlowFileAlloc();
if (cur_file == NULL) {
goto end;
}
cur_file->name = SCMalloc(filename_len);
if (cur_file->name == NULL) {
/** \todo remove cur_file */
goto end;
}
memcpy(cur_file->name, filename, filename_len);
cur_file->name_len = filename_len;
/* find the ext portion */
for (i = filename_len - 2; i >= 0 && filename[i] != '.'; i--);
SCLogDebug("i %d", i);
if (filename[i] == '.') {
cur_file->ext = &cur_file->name[++i];
cur_file->ext_len = filename_len - i;
#if 0
printf("EXT START: \n");
PrintRawDataFp(stdout, cur_file->ext, cur_file->ext_len);
printf("EXT END: \n");
#endif
}
if (filetype != NULL) {
cur_file->proto_type = SCMalloc(filetype_len);
if (cur_file->proto_type == NULL) {
goto end;
}
memcpy(cur_file->proto_type, filetype, filetype_len);
cur_file->proto_type_len = filetype_len;
}
cur_file->alproto = ALPROTO_HTTP;
SCLogDebug("Added");
FlowFileContainerAdd(ffc, cur_file);
/* cur_file->ext = get extension */
}
}
end:
SCMutexUnlock(&f->files_m);
SCReturnInt(0);
}
#define C_D_HDR "content-disposition:"
#define C_D_HDR_LEN 20
#define C_T_HDR "content-type:"
@ -1037,6 +960,7 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
uint8_t *filedata = chunks_buffer;
uint32_t filedata_len = 0;
uint8_t flags = 0;
if (header_start < form_end) {
filedata_len = header_start - filedata;
@ -1046,12 +970,18 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
filedata_len = form_end - filedata;
} else if (htud->flags & HTP_BODY_COMPLETE) {
filedata_len = chunks_buffer_len;
flags = FLOW_FILE_TRUNCATED;
}
printf("FILEDATA (final chunk) START: \n");
PrintRawDataFp(stdout, filedata, filedata_len);
printf("FILEDATA (final chunk) END: \n");
if (HTPFileClose(hstate->f, filedata, filedata_len, flags) == -1)
{
goto end;
}
htud->flags &=~ HTP_FILENAME_SET;
/* fall through */
@ -1066,6 +996,10 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
PrintRawDataFp(stdout, filedata, filedata_len);
printf("FILEDATA (part) END: \n");
if (HTPFileStoreChunk(hstate->f, filedata, filedata_len) == -1) {
goto end;
}
htud->body_parsed += filedata_len;
} else {
SCLogDebug("chunk too small to already process in part");
@ -1135,16 +1069,17 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
} /* while (header_len > 0) */
if (filename != NULL) {
uint8_t *filedata = NULL;
uint32_t filedata_len = 0;
SCLogDebug("we have a filename");
HTTPStoreFileNameType(hstate->f, filename, filename_len,
filetype, filetype_len);
htud->flags |= HTP_FILENAME_SET;
/* everything until the final boundary is the file */
if (form_end != NULL) {
uint8_t *filedata = header_end + 4;
uint32_t filedata_len = form_end - (header_end + 4 + 2);
filedata = header_end + 4;
filedata_len = form_end - (header_end + 4 + 2);
SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
//#if 0
@ -1159,6 +1094,11 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
SCLogDebug("offset %u", offset);
htud->body_parsed = offset;
}
if (HTPFileOpen(hstate->f, filename, filename_len,
filedata, filedata_len) == -1) {
goto end;
}
}
filename = NULL;

@ -44,6 +44,7 @@
#include "util-unittest-helper.h"
#include "util-spm-bm.h"
#include "util-print.h"
#include "util-memcmp.h"
#include "app-layer.h"
@ -118,16 +119,23 @@ int DetectFileextMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
DetectFileextData *fileext = (DetectFileextData *)m->ctx;
SCMutexLock(&f->files_m);
if (f->files != NULL && f->files->cnt > 0) {
FlowFile *file = f->files->start;
if (f->files != NULL) {
FlowFile *file = f->files->head;
for (; file != NULL; file = file->next) {
if (file->ext != NULL) {
//PrintRawDataFp(stdout, file->ext, file->ext_len);
if (file->state == FLOWFILE_STATE_NONE)
continue;
if (BoyerMooreNocase(fileext->ext, fileext->len, file->ext,
file->ext_len, fileext->bm_ctx->bmGs,
fileext->bm_ctx->bmBc) != NULL)
if (file->name == NULL)
continue;
if (file->name_len <= fileext->len)
continue;
int offset = file->name_len - fileext->len;
if (file->name[offset - 1] == '.' &&
SCMemcmp(file->name + offset, fileext->ext, fileext->len) == 0)
{
ret = 1;
SCLogDebug("File ext found");
@ -137,7 +145,6 @@ int DetectFileextMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
}
}
}
}
SCMutexUnlock(&f->files_m);
SCReturnInt(ret);

@ -117,12 +117,18 @@ int DetectFilenameMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f,
DetectFilenameData *filename = m->ctx;
SCMutexLock(&f->files_m);
if (f->files != NULL && f->files->cnt > 0) {
FlowFile *file = f->files->start;
if (f->files != NULL) {
FlowFile *file = f->files->head;
for (; file != NULL; file = file->next) {
if (file != NULL && file->name != NULL &&
BoyerMooreNocase(filename->name, filename->len, file->name,
file->name_len, filename->bm_ctx->bmGs, filename->bm_ctx->bmBc) != NULL)
if (file->state == FLOWFILE_STATE_NONE)
continue;
if (file->name == NULL)
continue;
if (BoyerMooreNocase(filename->name, filename->len, file->name,
file->name_len, filename->bm_ctx->bmGs,
filename->bm_ctx->bmBc) != NULL)
{
ret = 1;
SCLogDebug("File %s found", file->name);

@ -1,4 +1,4 @@
/* Copyright (C) 2007-2010 Open Information Security Foundation
/* Copyright (C) 2007-2011 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -18,6 +18,7 @@
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
*/
@ -30,6 +31,10 @@
#include "util-hash.h"
#include "util-debug.h"
#include "util-memcmp.h"
#include "util-print.h"
/* prototypes */
static void FlowFileFree(FlowFile *);
/**
* \brief allocate a FlowFileContainer
@ -43,9 +48,8 @@ FlowFileContainer *FlowFileContainerAlloc(void) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
return NULL;
}
memset(new, 0, sizeof(new));
new->start = new->end = NULL;
new->cnt = 0;
memset(new, 0, sizeof(FlowFileContainer));
new->head = new->tail = NULL;
return new;
}
@ -58,15 +62,13 @@ void FlowFileContainerRecycle(FlowFileContainer *ffc) {
if (ffc == NULL)
return;
FlowFile *cur = ffc->start;
FlowFile *cur = ffc->head;
FlowFile *next = NULL;
for (;cur != NULL && ffc->cnt > 0; cur = next) {
for (;cur != NULL; cur = next) {
next = cur->next;
FlowFileFree(cur);
ffc->cnt--;
}
ffc->start = ffc->end = NULL;
ffc->cnt = 0;
ffc->head = ffc->tail = NULL;
}
/**
@ -78,53 +80,116 @@ void FlowFileContainerFree(FlowFileContainer *ffc) {
if (ffc == NULL)
return;
FlowFile *ptr = ffc->start;
FlowFile *ptr = ffc->head;
FlowFile *next = NULL;
for (;ptr != NULL && ffc->cnt > 0; ptr = next) {
for (;ptr != NULL; ptr = next) {
next = ptr->next;
FlowFileFree(ptr);
ffc->cnt--;
}
ffc->start = ffc->end = NULL;
ffc->cnt = 0;
ffc->head = ffc->tail = NULL;
SCFree(ffc);
}
FlowFileChunk *FlowFileChunkAlloc(void) {
FlowFileChunk *new = SCMalloc(sizeof(FlowFileChunk));
/**
* \internal
*
* \brief allocate a FlowFileData chunk and set it up
*
* \param data data chunk to store in the FlowFileData
* \param data_len lenght of the data
*
* \retval new FlowFileData object
*/
static FlowFileData *FlowFileDataAlloc(uint8_t *data, uint32_t data_len) {
FlowFileData *new = SCMalloc(sizeof(FlowFileData));
if (new == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
return NULL;
}
memset(new, 0, sizeof(new));
memset(new, 0, sizeof(FlowFileData));
new->data = SCMalloc(data_len);
if (new->data == NULL) {
SCFree(new);
return NULL;
}
new->len = data_len;
memcpy(new->data, data, data_len);
new->next = NULL;
return new;
}
void FlowFileChunkFree(FlowFileChunk *ffc) {
if (ffc == NULL)
/**
* \internal
*
* \brief free a FlowFileData object
*
* \param ffd the flow file data object to free
*/
static void FlowFileDataFree(FlowFileData *ffd) {
if (ffd == NULL)
return;
//TODO: To implement
SCFree(ffc);
if (ffd->data != NULL) {
SCFree(ffd->data);
}
SCFree(ffd);
}
static int FlowFileAppendFlowFileData(FlowFileContainer *ffc, FlowFileData *ffd) {
SCEnter();
if (ffc == NULL) {
SCReturnInt(-1);
}
FlowFile *ff = ffc->tail;
if (ff == NULL) {
SCReturnInt(-1);
}
FlowFile *FlowFileAlloc(void) {
if (ff->chunks_tail == NULL) {
ff->chunks_head = ffd;
ff->chunks_tail = ffd;
} else {
ff->chunks_tail->next = ffd;
ff->chunks_tail = ffd;
}
SCReturnInt(0);
}
/**
* \brief Alloc a new FlowFile
*
* \param name character array containing the name (not a string)
* \param name_len length in bytes of the name
*
* \retval new FlowFile object or NULL on error
*/
static FlowFile *FlowFileAlloc(uint8_t *name, uint16_t name_len) {
FlowFile *new = SCMalloc(sizeof(FlowFile));
if (new == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating mem");
return NULL;
}
memset(new, 0, sizeof(new));
memset(new, 0, sizeof(FlowFile));
new->name = SCMalloc(name_len);
if (new->name == NULL) {
SCFree(new);
return NULL;
}
new->name_len = name_len;
memcpy(new->name, name, name_len);
new->state = FLOWFILE_STATE_EMPTY;
new->name = NULL;
new->ext = NULL;
new->next = NULL;
return new;
}
void FlowFileFree(FlowFile *ff) {
static void FlowFileFree(FlowFile *ff) {
if (ff == NULL)
return;
@ -134,37 +199,145 @@ void FlowFileFree(FlowFile *ff) {
}
void FlowFileContainerAdd(FlowFileContainer *ffc, FlowFile *ff) {
if (ffc->start == NULL) {
ffc->start = ffc->end = ff;
if (ffc->head == NULL) {
ffc->head = ffc->tail = ff;
} else {
ffc->end->next = ff;
ffc->end = ff;
ffc->tail->next = ff;
ffc->tail = ff;
}
ffc->cnt += 1;
}
FlowFile *FlowFileContainerRetrieve(FlowFileContainer *ffc, uint16_t alproto,
uint8_t *name, uint16_t name_len) //, uint8_t *type, uint16_t type_len)
/**
* \brief Open a new FlowFile
*
* \param ffc flow container
* \param name filename character array
* \param name_len filename len
* \param data initial data
* \param data_len initial data len
*
* \retval ff flowfile object
*
* \note filename is not a string, so it's not nul terminated.
*/
FlowFile *FlowFileOpenFile(FlowFileContainer *ffc, uint8_t *name,
uint16_t name_len, uint8_t *data, uint32_t data_len)
{
FlowFile *ptr = ffc->start;
SCEnter();
PrintRawDataFp(stdout, name, name_len);
FlowFile *ff = FlowFileAlloc(name, name_len);
if (ff == NULL) {
SCReturnPtr(NULL, "FlowFile");
}
ff->state = FLOWFILE_STATE_OPENED;
SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_OPENED");
if (ffc->cnt > 0) {
while (ptr != NULL) {
if (ptr->alproto == alproto &&
name_len == ptr->name_len &&
SCMemcmp(ptr->name, name, name_len) == 0)
if (data != NULL) {
PrintRawDataFp(stdout, data, data_len);
FlowFileData *ffd = FlowFileDataAlloc(data, data_len);
if (ffd == NULL) {
FlowFileFree(ff);
SCReturnPtr(NULL, "FlowFile");
}
/* append the data */
if (FlowFileAppendFlowFileData(ffc, ffd) < 0) {
FlowFileFree(ff);
FlowFileDataFree(ffd);
SCReturnPtr(NULL, "FlowFile");
}
}
FlowFileContainerAdd(ffc, ff);
SCReturnPtr(ff, "FlowFile");
}
/**
* \brief Close a FlowFile
*
* \param ffc the container
* \param data final data if any
* \param data_len data len if any
* \param flags flags
*
* \retval 0 ok
* \retval -1 error
*/
int FlowFileCloseFile(FlowFileContainer *ffc, uint8_t *data,
uint32_t data_len, uint8_t flags)
{
return ptr;
SCEnter();
if (ffc == NULL || ffc->tail == NULL) {
SCReturnInt(-1);
}
if (ffc->tail->state != FLOWFILE_STATE_OPENED) {
SCReturnInt(-1);
}
if (data != NULL) {
PrintRawDataFp(stdout, data, data_len);
FlowFileData *ffd = FlowFileDataAlloc(data, data_len);
if (ffd == NULL) {
SCReturnInt(-1);
}
ptr = ptr->next;
/* append the data */
if (FlowFileAppendFlowFileData(ffc, ffd) < 0) {
FlowFileDataFree(ffd);
SCReturnInt(-1);
}
}
return NULL;
if (flags & FLOW_FILE_TRUNCATED) {
ffc->tail->state = FLOWFILE_STATE_TRUNCATED;
SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_TRUNCATED");
} else {
ffc->tail->state = FLOWFILE_STATE_CLOSED;
SCLogDebug("flowfile state transitioned to FLOWFILE_STATE_CLOSED");
}
FlowFile *FlowFileAppendChunk(FlowFile *ff, FlowFileChunk *ffc) {
//TODO: To implement
return NULL;
SCReturnInt(0);
}
/**
* \brief Store a chunk of file data in the flow. The open "flowfile"
* will be used.
*
* \param ffc the container
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
*/
int FlowFileAppendData(FlowFileContainer *ffc, uint8_t *data, uint32_t data_len) {
SCEnter();
if (ffc == NULL || ffc->tail == NULL || data == NULL || data_len == 0) {
SCReturnInt(-1);
}
if (ffc->tail->state != FLOWFILE_STATE_OPENED) {
SCReturnInt(-1);
}
FlowFileData *ffd = FlowFileDataAlloc(data, data_len);
if (ffd == NULL) {
SCReturnInt(-1);
}
/* append the data */
if (FlowFileAppendFlowFileData(ffc, ffd) < 0) {
FlowFileDataFree(ffd);
SCReturnInt(-1);
}
SCReturnInt(0);
}

@ -28,48 +28,39 @@
#include "flow.h"
#include "util-hash.h"
typedef enum _FlowFileState {
FLOWFILE_STATE_EMPTY,
FLOWFILE_STATE_DATA,
FLOWFILE_STATE_COMPLETED,
FLOWFILE_STATE_STORING,
#define FLOW_FILE_TRUNCATED 0x01
typedef enum FlowFileState_ {
FLOWFILE_STATE_NONE = 0, /**< no state */
FLOWFILE_STATE_OPENED, /**< flow file is opened */
FLOWFILE_STATE_CLOSED, /**< flow file is completed,
there will be no more data. */
FLOWFILE_STATE_TRUNCATED, /**< flow file is not complete, but
there will be no more data. */
FLOWFILE_STATE_STORED, /**< all fully written to disk */
FLOWFILE_STATE_MAX
} FlowFileState;
typedef uint8_t FlowFileHash;
typedef struct _FlowFileChunk {
uint8_t *buf;
typedef struct FlowFileData_ {
uint8_t *data;
uint32_t len;
struct _FlowFileChunk *next;
} FlowFileChunk;
int stored; /* true if this chunk has been stored already
* false otherwise */
struct FlowFileData_ *next;
} FlowFileData;
typedef struct _FlowFile {
typedef struct FlowFile_ {
uint8_t *name;
uint16_t name_len;
uint8_t *ext; /* ptr to extension portion in "name" */
uint16_t ext_len;
uint8_t *real_type;
uint16_t real_type_len;
uint8_t *proto_type; /* The content type set at Content-Type in the MIME header (of http or smtp..) */
uint16_t proto_type_len;
uint16_t alproto;
uint32_t size;
uint8_t flags;
FlowFileState state;
FlowFileChunk *chunks_start;
FlowFileChunk *chunks_end;
uint32_t chunk_cnt;
struct _FlowFile *next;
FlowFileData *chunks_head;
FlowFileData *chunks_tail;
struct FlowFile_ *next;
} FlowFile;
typedef struct _FlowFileContainer {
FlowFile *start;
FlowFile *end;
uint32_t cnt;
typedef struct FlowFileContainer_ {
FlowFile *head;
FlowFile *tail;
} FlowFileContainer;
FlowFileContainer *FlowFileContainerAlloc();
@ -77,15 +68,47 @@ void FlowFileContainerFree(FlowFileContainer *);
void FlowFileContainerRecycle(FlowFileContainer *);
FlowFileChunk *FlowFileChunkAlloc();
void FlowFileChunkFree(FlowFileChunk *);
void FlowFileContainerAdd(FlowFileContainer *, FlowFile *);
FlowFile *FlowFileAlloc();
void FlowFileFree(FlowFile *);
/**
* \brief Open a new FlowFile
*
* \param ffc flow container
* \param name filename character array
* \param name_len filename len
* \param data initial data
* \param data_len initial data len
*
* \retval ff flowfile object
*
* \note filename is not a string, so it's not nul terminated.
*/
FlowFile *FlowFileOpenFile(FlowFileContainer *, uint8_t *name, uint16_t name_len,
uint8_t *data, uint32_t data_len);
/**
* \brief Close a FlowFile
*
* \param ffc the container
* \param data final data if any
* \param data_len data len if any
* \param flags flags
*
* \retval 0 ok
* \retval -1 error
*/
int FlowFileCloseFile(FlowFileContainer *, uint8_t *data, uint32_t data_len, uint8_t flags);
void FlowFileContainerAdd(FlowFileContainer *, FlowFile *);
//FlowFile *FlowFileContainerRetrieve(FlowFileContainer *, uint8_t *, uint16_t, uint8_t *);
FlowFile *FlowFileContainerRetrieve(FlowFileContainer *, uint16_t, uint8_t *, uint16_t);
FlowFile *FlowFileAppendChunk(FlowFile *, FlowFileChunk *);
/**
* \brief Store a chunk of file data in the flow. The open "flowfile"
* will be used.
*
* \param ffc the container
* \param data data chunk
* \param data_len data chunk len
*
* \retval 0 ok
* \retval -1 error
*/
int FlowFileAppendData(FlowFileContainer *, uint8_t *data, uint32_t data_len);
#endif /* __FLOW_FILE_H__ */

Loading…
Cancel
Save