|
|
|
/* Copyright (C) 2007-2013 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
|
|
|
|
* Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* version 2 along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
*
|
|
|
|
* \author Victor Julien <victor@inliniac.net>
|
|
|
|
*
|
|
|
|
* A simple application layer (L7) protocol detector. It works by allowing
|
|
|
|
* developers to set a series of patterns that if exactly matching indicate
|
|
|
|
* that the session is a certain protocol.
|
|
|
|
*
|
|
|
|
* \todo More advanced detection methods, regex maybe.
|
|
|
|
* \todo Fall back to port based classification if other detection fails.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "decode.h"
|
|
|
|
#include "threads.h"
|
|
|
|
#include "threadvars.h"
|
|
|
|
#include "tm-threads.h"
|
|
|
|
|
|
|
|
#include "detect.h"
|
|
|
|
#include "detect-parse.h"
|
|
|
|
#include "detect-engine.h"
|
|
|
|
#include "detect-content.h"
|
|
|
|
#include "detect-engine-mpm.h"
|
|
|
|
#include "detect-engine-state.h"
|
|
|
|
|
|
|
|
#include "util-print.h"
|
|
|
|
#include "util-pool.h"
|
|
|
|
#include "util-unittest.h"
|
|
|
|
#include "util-unittest-helper.h"
|
|
|
|
|
|
|
|
#include "flow.h"
|
|
|
|
#include "flow-util.h"
|
|
|
|
|
|
|
|
#include "stream-tcp-private.h"
|
|
|
|
#include "stream-tcp-reassemble.h"
|
|
|
|
#include "stream-tcp.h"
|
|
|
|
#include "stream.h"
|
|
|
|
|
|
|
|
#include "app-layer-protos.h"
|
|
|
|
#include "app-layer-parser.h"
|
|
|
|
#include "app-layer-detect-proto.h"
|
|
|
|
|
|
|
|
#include "util-spm.h"
|
|
|
|
#include "util-cuda.h"
|
|
|
|
#include "util-debug.h"
|
|
|
|
|
|
|
|
#define INSPECT_BYTES 32
|
|
|
|
|
|
|
|
/** global app layer detection context */
|
|
|
|
AlpProtoDetectCtx alp_proto_ctx;
|
|
|
|
|
|
|
|
/** \brief Initialize the app layer proto detection */
|
|
|
|
void AlpProtoInit(AlpProtoDetectCtx *ctx) {
|
|
|
|
memset(ctx, 0x00, sizeof(AlpProtoDetectCtx));
|
|
|
|
|
|
|
|
MpmInitCtx(&ctx->toserver.mpm_ctx, MPM_B2G);
|
|
|
|
MpmInitCtx(&ctx->toclient.mpm_ctx, MPM_B2G);
|
|
|
|
|
|
|
|
memset(&ctx->toserver.map, 0x00, sizeof(ctx->toserver.map));
|
|
|
|
memset(&ctx->toclient.map, 0x00, sizeof(ctx->toclient.map));
|
|
|
|
|
|
|
|
ctx->toserver.id = 0;
|
|
|
|
ctx->toclient.id = 0;
|
|
|
|
ctx->toclient.min_len = INSPECT_BYTES;
|
|
|
|
ctx->toserver.min_len = INSPECT_BYTES;
|
|
|
|
|
|
|
|
ctx->mpm_pattern_id_store = MpmPatternIdTableInitHash();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Turn a proto detection into a AlpProtoSignature and store it
|
|
|
|
* in the ctx.
|
|
|
|
*
|
|
|
|
* \param ctx the contex
|
|
|
|
* \param co the content match
|
|
|
|
* \param proto the proto id
|
|
|
|
* \initonly
|
|
|
|
*/
|
|
|
|
static void AlpProtoAddSignature(AlpProtoDetectCtx *ctx, DetectContentData *co, uint16_t ip_proto, uint16_t proto) {
|
|
|
|
AlpProtoSignature *s = SCMalloc(sizeof(AlpProtoSignature));
|
|
|
|
if (unlikely(s == NULL)) {
|
|
|
|
SCLogError(SC_ERR_FATAL, "Error allocating memory. Signature not loaded. Not enough memory so.. exiting..");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
memset(s, 0x00, sizeof(AlpProtoSignature));
|
|
|
|
|
|
|
|
s->ip_proto = ip_proto;
|
|
|
|
s->proto = proto;
|
|
|
|
s->co = co;
|
|
|
|
|
|
|
|
if (ctx->head == NULL) {
|
|
|
|
ctx->head = s;
|
|
|
|
} else {
|
|
|
|
s->next = ctx->head;
|
|
|
|
ctx->head = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->sigs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
/** \brief free a AlpProtoSignature, recursively free any next sig */
|
|
|
|
static void AlpProtoFreeSignature(AlpProtoSignature *s) {
|
|
|
|
if (s == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DetectContentFree(s->co);
|
|
|
|
s->co = NULL;
|
|
|
|
s->proto = 0;
|
|
|
|
|
|
|
|
AlpProtoSignature *next_s = s->next;
|
|
|
|
|
|
|
|
SCFree(s);
|
|
|
|
|
|
|
|
AlpProtoFreeSignature(next_s);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Match a AlpProtoSignature against a buffer
|
|
|
|
*
|
|
|
|
* \param s signature
|
|
|
|
* \param buf pointer to buffer
|
|
|
|
* \param buflen length of the buffer
|
|
|
|
* \param ip_proto packet's ip_proto
|
|
|
|
*
|
|
|
|
* \retval proto the detected proto or ALPROTO_UNKNOWN if no match
|
|
|
|
*/
|
|
|
|
static uint16_t AlpProtoMatchSignature(AlpProtoSignature *s, uint8_t *buf,
|
|
|
|
uint16_t buflen, uint16_t ip_proto)
|
|
|
|
{
|
|
|
|
SCEnter();
|
|
|
|
uint16_t proto = ALPROTO_UNKNOWN;
|
|
|
|
|
|
|
|
if (s->ip_proto != ip_proto) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->co->offset > buflen) {
|
|
|
|
SCLogDebug("s->co->offset (%"PRIu16") > buflen (%"PRIu16")",
|
|
|
|
s->co->offset, buflen);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->co->depth > buflen) {
|
|
|
|
SCLogDebug("s->co->depth (%"PRIu16") > buflen (%"PRIu16")",
|
|
|
|
s->co->depth, buflen);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *sbuf = buf + s->co->offset;
|
|
|
|
uint16_t sbuflen = s->co->depth - s->co->offset;
|
|
|
|
SCLogDebug("s->co->offset (%"PRIu16") s->co->depth (%"PRIu16")",
|
|
|
|
s->co->offset, s->co->depth);
|
|
|
|
|
|
|
|
uint8_t *found = SpmSearch(sbuf, sbuflen, s->co->content, s->co->content_len);
|
|
|
|
if (found != NULL) {
|
|
|
|
proto = s->proto;
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
SCReturnInt(proto);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Add a proto detection string to the detection ctx.
|
|
|
|
*
|
|
|
|
* \param ctx The detection ctx
|
|
|
|
* \param ip_proto The IP proto (TCP, UDP, etc)
|
|
|
|
* \param al_proto Application layer proto
|
|
|
|
* \param content A content string in the 'content:"some|20|string"' format.
|
|
|
|
* \param depth Depth setting for the content. E.g. 4 means that the content has to match in the first 4 bytes of the stream.
|
|
|
|
* \param offset Offset setting for the content. E.g. 4 mean that the content has to match after the first 4 bytes of the stream.
|
|
|
|
* \param flags Set STREAM_TOCLIENT or STREAM_TOSERVER for the direction in which to try to match the content.
|
|
|
|
*/
|
|
|
|
void AlpProtoAdd(AlpProtoDetectCtx *ctx, char *name, uint16_t ip_proto, uint16_t al_proto, char *content, uint16_t depth, uint16_t offset, uint8_t flags)
|
|
|
|
{
|
|
|
|
if (al_proto_table[al_proto].name != NULL) {
|
|
|
|
BUG_ON(strcmp(al_proto_table[al_proto].name, name) != 0);
|
|
|
|
} else {
|
|
|
|
al_proto_table[al_proto].name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
DetectContentData *cd = DetectContentParseEncloseQuotes(content);
|
|
|
|
if (cd == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cd->depth = depth;
|
|
|
|
cd->offset = offset;
|
|
|
|
|
|
|
|
cd->id = DetectContentGetId(ctx->mpm_pattern_id_store, cd);
|
|
|
|
|
|
|
|
//PrintRawDataFp(stdout,cd->content,cd->content_len);
|
|
|
|
SCLogDebug("cd->depth %"PRIu16" and cd->offset %"PRIu16" cd->id %"PRIu32"",
|
|
|
|
cd->depth, cd->offset, cd->id);
|
|
|
|
|
|
|
|
AlpProtoDetectDirection *dir;
|
|
|
|
if (flags & STREAM_TOCLIENT) {
|
|
|
|
dir = &ctx->toclient;
|
|
|
|
} else {
|
|
|
|
dir = &ctx->toserver;
|
|
|
|
}
|
|
|
|
|
|
|
|
mpm_table[dir->mpm_ctx.mpm_type].AddPattern(&dir->mpm_ctx, cd->content, cd->content_len,
|
|
|
|
cd->offset, cd->depth, cd->id, cd->id, 0);
|
|
|
|
BUG_ON(dir->id == ALP_DETECT_MAX);
|
|
|
|
dir->map[dir->id] = al_proto;
|
|
|
|
dir->id++;
|
|
|
|
|
|
|
|
if (depth > dir->max_len)
|
|
|
|
dir->max_len = depth;
|
|
|
|
|
|
|
|
/* set the min_len for the stream engine to set the min smsg size for app
|
|
|
|
layer*/
|
|
|
|
if (depth < dir->min_len)
|
|
|
|
dir->min_len = depth;
|
|
|
|
|
|
|
|
/* finally turn into a signature and add to the ctx */
|
|
|
|
AlpProtoAddSignature(ctx, cd, ip_proto, al_proto);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
void AlpProtoTestDestroy(AlpProtoDetectCtx *ctx) {
|
|
|
|
mpm_table[ctx->toserver.mpm_ctx.mpm_type].DestroyCtx(&ctx->toserver.mpm_ctx);
|
|
|
|
mpm_table[ctx->toclient.mpm_ctx.mpm_type].DestroyCtx(&ctx->toclient.mpm_ctx);
|
|
|
|
AlpProtoFreeSignature(ctx->head);
|
|
|
|
AppLayerFreeProbingParsers(ctx->probing_parsers);
|
|
|
|
ctx->probing_parsers = NULL;
|
|
|
|
AppLayerFreeProbingParsersInfo(ctx->probing_parsers_info);
|
|
|
|
ctx->probing_parsers_info = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void AlpProtoDestroy() {
|
|
|
|
SCEnter();
|
|
|
|
mpm_table[alp_proto_ctx.toserver.mpm_ctx.mpm_type].DestroyCtx(&alp_proto_ctx.toserver.mpm_ctx);
|
|
|
|
mpm_table[alp_proto_ctx.toclient.mpm_ctx.mpm_type].DestroyCtx(&alp_proto_ctx.toclient.mpm_ctx);
|
|
|
|
MpmPatternIdTableFreeHash(alp_proto_ctx.mpm_pattern_id_store);
|
|
|
|
AppLayerFreeProbingParsers(alp_proto_ctx.probing_parsers);
|
|
|
|
alp_proto_ctx.probing_parsers = NULL;
|
|
|
|
AppLayerFreeProbingParsersInfo(alp_proto_ctx.probing_parsers_info);
|
|
|
|
alp_proto_ctx.probing_parsers_info = NULL;
|
|
|
|
SCReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlpProtoFinalizeThread(AlpProtoDetectCtx *ctx, AlpProtoDetectThreadCtx *tctx) {
|
|
|
|
uint32_t sig_maxid = 0;
|
|
|
|
uint32_t pat_maxid = ctx->mpm_pattern_id_store ? ctx->mpm_pattern_id_store->max_id : 0;
|
|
|
|
|
|
|
|
memset(tctx, 0x00, sizeof(AlpProtoDetectThreadCtx));
|
|
|
|
|
|
|
|
if (ctx->toclient.id > 0) {
|
|
|
|
//sig_maxid = ctx->toclient.id;
|
|
|
|
mpm_table[ctx->toclient.mpm_ctx.mpm_type].InitThreadCtx(&ctx->toclient.mpm_ctx, &tctx->toclient.mpm_ctx, sig_maxid);
|
|
|
|
PmqSetup(&tctx->toclient.pmq, sig_maxid, pat_maxid);
|
|
|
|
}
|
|
|
|
if (ctx->toserver.id > 0) {
|
|
|
|
//sig_maxid = ctx->toserver.id;
|
|
|
|
mpm_table[ctx->toserver.mpm_ctx.mpm_type].InitThreadCtx(&ctx->toserver.mpm_ctx, &tctx->toserver.mpm_ctx, sig_maxid);
|
|
|
|
PmqSetup(&tctx->toserver.pmq, sig_maxid, pat_maxid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < ALPROTO_MAX; i++) {
|
|
|
|
tctx->alproto_local_storage[i] = AppLayerGetProtocolParserLocalStorage(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlpProtoDeFinalize2Thread(AlpProtoDetectThreadCtx *tctx) {
|
|
|
|
if (alp_proto_ctx.toclient.id > 0) {
|
|
|
|
mpm_table[alp_proto_ctx.toclient.mpm_ctx.mpm_type].DestroyThreadCtx
|
|
|
|
(&alp_proto_ctx.toclient.mpm_ctx, &tctx->toclient.mpm_ctx);
|
|
|
|
PmqFree(&tctx->toclient.pmq);
|
|
|
|
}
|
|
|
|
if (alp_proto_ctx.toserver.id > 0) {
|
|
|
|
mpm_table[alp_proto_ctx.toserver.mpm_ctx.mpm_type].DestroyThreadCtx
|
|
|
|
(&alp_proto_ctx.toserver.mpm_ctx, &tctx->toserver.mpm_ctx);
|
|
|
|
PmqFree(&tctx->toserver.pmq);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
/** \brief to be called by ReassemblyThreadInit
|
|
|
|
* \todo this is a hack, we need a proper place to store the global ctx */
|
|
|
|
void AlpProtoFinalize2Thread(AlpProtoDetectThreadCtx *tctx) {
|
|
|
|
return AlpProtoFinalizeThread(&alp_proto_ctx, tctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlpProtoFinalizeGlobal(AlpProtoDetectCtx *ctx) {
|
|
|
|
if (ctx == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mpm_table[ctx->toclient.mpm_ctx.mpm_type].Prepare(&ctx->toclient.mpm_ctx);
|
|
|
|
mpm_table[ctx->toserver.mpm_ctx.mpm_type].Prepare(&ctx->toserver.mpm_ctx);
|
|
|
|
|
|
|
|
/* allocate and initialize the mapping between pattern id and signature */
|
|
|
|
ctx->map = (AlpProtoSignature **)SCMalloc(ctx->sigs * sizeof(AlpProtoSignature *));
|
|
|
|
if (ctx->map == NULL) {
|
|
|
|
SCLogError(SC_ERR_MEM_ALLOC, "%s", strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(ctx->map, 0x00, ctx->sigs * sizeof(AlpProtoSignature *));
|
|
|
|
|
|
|
|
AlpProtoSignature *s = ctx->head;
|
|
|
|
AlpProtoSignature *temp = NULL;
|
|
|
|
for ( ; s != NULL; s = s->next) {
|
|
|
|
BUG_ON(s->co == NULL);
|
|
|
|
|
|
|
|
if (ctx->map[s->co->id] == NULL) {
|
|
|
|
ctx->map[s->co->id] = s;
|
|
|
|
} else {
|
|
|
|
temp = ctx->map[s->co->id];
|
|
|
|
while (temp->map_next != NULL)
|
|
|
|
temp = temp->map_next;
|
|
|
|
temp->map_next = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppLayerDetectProtoThreadInit(void) {
|
|
|
|
AlpProtoInit(&alp_proto_ctx);
|
|
|
|
RegisterAppLayerParsers();
|
|
|
|
AlpProtoFinalizeGlobal(&alp_proto_ctx);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Get the app layer proto based on a buffer using a Patter matcher
|
|
|
|
* parser.
|
|
|
|
*
|
|
|
|
* \param ctx Global app layer detection context
|
|
|
|
* \param tctx Thread app layer detection context
|
|
|
|
* \param buf Pointer to the buffer to inspect
|
|
|
|
* \param buflen Lenght of the buffer
|
|
|
|
* \param flags Flags.
|
|
|
|
*
|
|
|
|
* \retval proto App Layer proto, or ALPROTO_UNKNOWN if unknown
|
|
|
|
*/
|
|
|
|
uint16_t AppLayerDetectGetProtoPMParser(AlpProtoDetectCtx *ctx,
|
|
|
|
AlpProtoDetectThreadCtx *tctx,
|
|
|
|
uint8_t *buf, uint16_t buflen,
|
|
|
|
uint8_t flags, uint8_t ipproto) {
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
AlpProtoDetectDirection *dir;
|
|
|
|
AlpProtoDetectDirectionThread *tdir;
|
|
|
|
|
|
|
|
if (flags & STREAM_TOSERVER) {
|
|
|
|
dir = &ctx->toserver;
|
|
|
|
tdir = &tctx->toserver;
|
|
|
|
} else {
|
|
|
|
dir = &ctx->toclient;
|
|
|
|
tdir = &tctx->toclient;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir->id == 0) {
|
|
|
|
SCReturnUInt(ALPROTO_UNKNOWN);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see if we can limit the data we inspect */
|
|
|
|
uint16_t searchlen = buflen;
|
|
|
|
if (searchlen > dir->max_len)
|
|
|
|
searchlen = dir->max_len;
|
|
|
|
|
|
|
|
uint16_t proto = ALPROTO_UNKNOWN;
|
|
|
|
uint32_t cnt = 0;
|
|
|
|
|
|
|
|
/* do the mpm search */
|
|
|
|
cnt = mpm_table[dir->mpm_ctx.mpm_type].Search(&dir->mpm_ctx,
|
|
|
|
&tdir->mpm_ctx,
|
|
|
|
&tdir->pmq, buf,
|
|
|
|
searchlen);
|
|
|
|
SCLogDebug("search cnt %" PRIu32 "", cnt);
|
|
|
|
if (cnt == 0) {
|
|
|
|
proto = ALPROTO_UNKNOWN;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We just work with the first match */
|
|
|
|
uint16_t patid = tdir->pmq.pattern_id_array[0];
|
|
|
|
SCLogDebug("array count is %"PRIu32" patid %"PRIu16"",
|
|
|
|
tdir->pmq.pattern_id_array_cnt, patid);
|
|
|
|
|
|
|
|
AlpProtoSignature *s = ctx->map[patid];
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
uint8_t s_cnt = 1;
|
|
|
|
|
|
|
|
while (proto == ALPROTO_UNKNOWN && s != NULL) {
|
|
|
|
proto = AlpProtoMatchSignature(s, buf, buflen, ipproto);
|
|
|
|
|
|
|
|
s = s->map_next;
|
|
|
|
if (s == NULL && s_cnt < tdir->pmq.pattern_id_array_cnt) {
|
|
|
|
patid = tdir->pmq.pattern_id_array[s_cnt];
|
|
|
|
s = ctx->map[patid];
|
|
|
|
s_cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
PmqReset(&tdir->pmq);
|
|
|
|
|
|
|
|
if (mpm_table[dir->mpm_ctx.mpm_type].Cleanup != NULL) {
|
|
|
|
mpm_table[dir->mpm_ctx.mpm_type].Cleanup(&tdir->mpm_ctx);
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
printf("AppLayerDetectGetProto: returning %" PRIu16 " (%s): ", proto, flags & STREAM_TOCLIENT ? "TOCLIENT" : "TOSERVER");
|
|
|
|
switch (proto) {
|
|
|
|
case ALPROTO_HTTP:
|
|
|
|
printf("HTTP: ");
|
|
|
|
/* print the first 32 bytes */
|
|
|
|
if (buflen > 0) {
|
|
|
|
PrintRawUriFp(stdout,buf,(buflen>32)?32:buflen);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_FTP:
|
|
|
|
printf("FTP\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_SSL:
|
|
|
|
printf("SSL\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_SSH:
|
|
|
|
printf("SSH\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_TLS:
|
|
|
|
printf("TLS\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_IMAP:
|
|
|
|
printf("IMAP\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_SMTP:
|
|
|
|
printf("SMTP\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_JABBER:
|
|
|
|
printf("JABBER\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_MSN:
|
|
|
|
printf("MSN\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_SMB:
|
|
|
|
printf("SMB\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_SMB2:
|
|
|
|
printf("SMB2\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_DCERPC:
|
|
|
|
printf("DCERPC\n");
|
|
|
|
break;
|
|
|
|
case ALPROTO_UNKNOWN:
|
|
|
|
default:
|
|
|
|
printf("UNKNOWN (%u): cnt was %u (", proto, cnt);
|
|
|
|
/* print the first 32 bytes */
|
|
|
|
if (buflen > 0) {
|
|
|
|
PrintRawUriFp(stdout,buf,(buflen>32)?32:buflen);
|
|
|
|
}
|
|
|
|
printf(")\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
SCReturnUInt(proto);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Call the probing parser if it exists for this src or dst port.
|
|
|
|
*/
|
|
|
|
uint16_t AppLayerDetectGetProtoProbingParser(AlpProtoDetectCtx *ctx, Flow *f,
|
|
|
|
uint8_t *buf, uint32_t buflen,
|
|
|
|
uint8_t flags, uint8_t ipproto)
|
|
|
|
{
|
|
|
|
AppLayerProbingParserElement *pe = NULL;
|
|
|
|
AppLayerProbingParser *probing_parsers = ctx->probing_parsers;
|
|
|
|
AppLayerProbingParser *pp = NULL;
|
|
|
|
uint32_t *al_proto_masks;
|
|
|
|
|
|
|
|
if (flags & STREAM_TOSERVER) {
|
|
|
|
pp = AppLayerGetProbingParsers(probing_parsers, ipproto, f->dp);
|
|
|
|
al_proto_masks = &f->probing_parser_toserver_al_proto_masks;
|
|
|
|
if (pp == NULL) {
|
|
|
|
SCLogDebug("toserver-No probing parser registered for port %"PRIu16,
|
|
|
|
f->dp);
|
|
|
|
if (f->flags & FLOW_TS_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TS_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TS_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
pe = pp->toserver;
|
|
|
|
} else {
|
|
|
|
pp = AppLayerGetProbingParsers(probing_parsers, ipproto, f->sp);
|
|
|
|
al_proto_masks = &f->probing_parser_toclient_al_proto_masks;
|
|
|
|
if (pp == NULL) {
|
|
|
|
SCLogDebug("toclient-No probing parser registered for port %"PRIu16,
|
|
|
|
f->sp);
|
|
|
|
if (f->flags & FLOW_TC_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TC_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TC_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
pe = pp->toclient;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (pe != NULL) {
|
|
|
|
if ((buflen < pe->min_depth) ||
|
|
|
|
(al_proto_masks[0] & pe->al_proto_mask)) {
|
|
|
|
pe = pe->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int alproto = pe->ProbingParser(buf, buflen);
|
|
|
|
if (alproto != ALPROTO_UNKNOWN && alproto != ALPROTO_FAILED)
|
|
|
|
return alproto;
|
|
|
|
if (alproto == ALPROTO_FAILED ||
|
|
|
|
(pe->max_depth != 0 && buflen > pe->max_depth)) {
|
|
|
|
al_proto_masks[0] |= pe->al_proto_mask;
|
|
|
|
}
|
|
|
|
pe = pe->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & STREAM_TOSERVER) {
|
|
|
|
if (al_proto_masks[0] == pp->toserver_al_proto_mask) {
|
|
|
|
if (f->flags & FLOW_TS_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TS_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TS_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (al_proto_masks[0] == pp->toclient_al_proto_mask) {
|
|
|
|
if (f->flags & FLOW_TC_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TC_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TC_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Get the app layer proto.
|
|
|
|
*
|
|
|
|
* \param ctx Global app layer detection context.
|
|
|
|
* \param tctx Thread app layer detection context.
|
|
|
|
* \param f Pointer to the flow.
|
|
|
|
* \param buf Pointer to the buffer to inspect.
|
|
|
|
* \param buflen Lenght of the buffer.
|
|
|
|
* \param flags Flags.
|
|
|
|
*
|
|
|
|
* \retval proto App Layer proto, or ALPROTO_UNKNOWN if unknown
|
|
|
|
*/
|
|
|
|
uint16_t AppLayerDetectGetProto(AlpProtoDetectCtx *ctx,
|
|
|
|
AlpProtoDetectThreadCtx *tctx, Flow *f,
|
|
|
|
uint8_t *buf, uint32_t buflen,
|
|
|
|
uint8_t flags, uint8_t ipproto)
|
|
|
|
{
|
|
|
|
uint16_t alproto = ALPROTO_UNKNOWN;
|
|
|
|
|
|
|
|
if (flags & STREAM_TOSERVER) {
|
|
|
|
if (buflen >= alp_proto_ctx.toserver.max_len) {
|
|
|
|
if (f->flags & FLOW_TS_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
/* the PM parser has already tried and failed. Now it is
|
|
|
|
* upto the probing parser */
|
|
|
|
;
|
|
|
|
} else {
|
|
|
|
alproto = AppLayerDetectGetProtoPMParser(ctx, tctx, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
if (alproto != ALPROTO_UNKNOWN)
|
|
|
|
return alproto;
|
|
|
|
/* the alproto hasn't been detected at this point */
|
|
|
|
if (f->flags & FLOW_TS_PP_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TS_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TS_PM_ALPROTO_DETECT_DONE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
alproto = AppLayerDetectGetProtoPMParser(ctx, tctx, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
if (alproto != ALPROTO_UNKNOWN)
|
|
|
|
return alproto;
|
|
|
|
}
|
|
|
|
/* If we have reached here, the PM parser has failed to detect the
|
|
|
|
* alproto */
|
|
|
|
return AppLayerDetectGetProtoProbingParser(ctx, f, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
|
|
|
|
/* STREAM_TOCLIENT */
|
|
|
|
} else {
|
|
|
|
if (buflen >= alp_proto_ctx.toclient.max_len) {
|
|
|
|
if (f->flags & FLOW_TC_PM_ALPROTO_DETECT_DONE) {
|
|
|
|
;
|
|
|
|
} else {
|
|
|
|
alproto = AppLayerDetectGetProtoPMParser(ctx, tctx, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
if (alproto != ALPROTO_UNKNOWN)
|
|
|
|
return alproto;
|
|
|
|
if (f->flags & FLOW_TC_PP_ALPROTO_DETECT_DONE) {
|
|
|
|
f->flags |= FLOW_TC_PM_PP_ALPROTO_DETECT_DONE;
|
|
|
|
return ALPROTO_UNKNOWN;
|
|
|
|
}
|
|
|
|
f->flags |= FLOW_TC_PM_ALPROTO_DETECT_DONE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
alproto = AppLayerDetectGetProtoPMParser(ctx, tctx, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
if (alproto != ALPROTO_UNKNOWN)
|
|
|
|
return alproto;
|
|
|
|
}
|
|
|
|
return AppLayerDetectGetProtoProbingParser(ctx, f, buf, buflen,
|
|
|
|
flags, ipproto);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* VJ Originally I thought of having separate app layer
|
|
|
|
* handling threads, leaving this here in case we'll revisit that */
|
|
|
|
#if 0
|
|
|
|
void *AppLayerDetectProtoThread(void *td)
|
|
|
|
{
|
|
|
|
ThreadVars *tv = (ThreadVars *)td;
|
|
|
|
char run = TRUE;
|
|
|
|
|
|
|
|
/* get the stream msg queue for this thread */
|
|
|
|
StreamMsgQueue *stream_q = StreamMsgQueueGetByPort(0);
|
|
|
|
|
|
|
|
TmThreadsSetFlag(tv, THV_INIT_DONE);
|
|
|
|
|
|
|
|
/* main loop */
|
|
|
|
while(run) {
|
|
|
|
if (TmThreadsCheckFlag(tv, THV_PAUSE)) {
|
|
|
|
TmThreadsSetFlag(tv, THV_PAUSED);
|
|
|
|
TmThreadTestThreadUnPaused(tv);
|
|
|
|
TmThreadsUnsetFlag(tv, THV_PAUSED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* grab a msg, can return NULL on signals */
|
|
|
|
StreamMsg *smsg = StreamMsgGetFromQueue(stream_q);
|
|
|
|
if (smsg != NULL) {
|
|
|
|
AppLayerHandleMsg(smsg, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TmThreadsCheckFlag(tv, THV_KILL)) {
|
|
|
|
SCPerfSyncCounters(tv, 0);
|
|
|
|
run = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_exit((void *) 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppLayerDetectProtoThreadSpawn()
|
|
|
|
{
|
|
|
|
ThreadVars *tv_applayerdetect = NULL;
|
|
|
|
|
|
|
|
tv_applayerdetect = TmThreadCreateMgmtThread("AppLayerDetectProtoThread",
|
|
|
|
AppLayerDetectProtoThread, 0);
|
|
|
|
if (tv_applayerdetect == NULL) {
|
|
|
|
printf("ERROR: TmThreadsCreate failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (TmThreadSpawn(tv_applayerdetect) != TM_ECODE_OK) {
|
|
|
|
printf("ERROR: TmThreadSpawn failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("AppLayerDetectProtoThread thread created\n");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
|
|
|
|
int AlpDetectTest01(void) {
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = SCStrdup("GET");
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOSERVER);
|
|
|
|
if (ctx.toserver.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest02(void) {
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = SCStrdup("220 ");
|
|
|
|
AlpProtoAdd(&ctx, "ftp", IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 2) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_FTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest03(void) {
|
|
|
|
uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = SCStrdup("220 ");
|
|
|
|
AlpProtoAdd(&ctx, "ftp", IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 2) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_FTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint32_t cnt = mpm_table[ctx.toclient.mpm_ctx.mpm_type].Search(&ctx.toclient.mpm_ctx, &tctx.toclient.mpm_ctx, NULL, l7data, sizeof(l7data));
|
|
|
|
if (cnt != 1) {
|
|
|
|
printf("cnt %u != 1: ", cnt);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest04(void) {
|
|
|
|
uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n";
|
|
|
|
char *buf = SCStrdup("200 ");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint32_t cnt = mpm_table[ctx.toclient.mpm_ctx.mpm_type].Search(&ctx.toclient.mpm_ctx, &tctx.toclient.mpm_ctx, &tctx.toclient.pmq, l7data, sizeof(l7data));
|
|
|
|
if (cnt != 1) {
|
|
|
|
printf("cnt %u != 1: ", cnt);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest05(void) {
|
|
|
|
uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n<HTML><BODY>Blahblah</BODY></HTML>";
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = SCStrdup("220 ");
|
|
|
|
AlpProtoAdd(&ctx, "ftp", IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 2) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_FTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest06(void) {
|
|
|
|
uint8_t l7data[] = "220 Welcome to the OISF FTP server\r\n";
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = SCStrdup("220 ");
|
|
|
|
AlpProtoAdd(&ctx, "ftp", IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 2) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_FTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_FTP) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_FTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest07(void) {
|
|
|
|
uint8_t l7data[] = "220 Welcome to the OISF HTTP/FTP server\r\n";
|
|
|
|
char *buf = SCStrdup("HTTP");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_HTTP) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_UNKNOWN) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_UNKNOWN);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest08(void) {
|
|
|
|
uint8_t l7data[] = "\x00\x00\x00\x85" // NBSS
|
|
|
|
"\xff\x53\x4d\x42\x72\x00\x00\x00" // SMB
|
|
|
|
"\x00\x18\x53\xc8\x00\x00\x00\x00"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
"\x00\x00\xff\xfe\x00\x00\x00\x00"
|
|
|
|
"\x00" // WordCount
|
|
|
|
"\x62\x00" // ByteCount
|
|
|
|
"\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20"
|
|
|
|
"\x31\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x57\x69\x6e\x64\x6f\x77\x73"
|
|
|
|
"\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00\x02\x4c"
|
|
|
|
"\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54"
|
|
|
|
"\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00";
|
|
|
|
char *buf = SCStrdup("|ff|SMB");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "smb", IPPROTO_TCP, ALPROTO_SMB, buf, 8, 4, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_SMB) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_SMB) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_SMB);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest09(void) {
|
|
|
|
uint8_t l7data[] =
|
|
|
|
"\x00\x00\x00\x66" // NBSS
|
|
|
|
"\xfe\x53\x4d\x42\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00" // SMB2
|
|
|
|
"\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
"\x24\x00\x01\x00x00\x00\x00\x00\x00\x00\x0\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02";
|
|
|
|
|
|
|
|
char *buf = SCStrdup("|fe|SMB");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "smb2", IPPROTO_TCP, ALPROTO_SMB2, buf, 8, 4, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_SMB2) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_SMB2) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_SMB2);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AlpDetectTest10(void) {
|
|
|
|
uint8_t l7data[] = "\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00"
|
|
|
|
"\x00\x00\x00\x00\xd0\x16\xd0\x16\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
|
|
|
|
"\x01\x00\xb8\x4a\x9f\x4d\x1c\x7d\xcf\x11\x86\x1e\x00\x20\xaf\x6e\x7c\x57"
|
|
|
|
"\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10"
|
|
|
|
"\x48\x60\x02\x00\x00\x00";
|
|
|
|
char *buf = SCStrdup("|05 00|");
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "dcerpc", IPPROTO_TCP, ALPROTO_DCERPC, buf, 4, 0, STREAM_TOCLIENT);
|
|
|
|
SCFree(buf);
|
|
|
|
|
|
|
|
if (ctx.toclient.id != 1) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toclient.map[ctx.toclient.id - 1] != ALPROTO_DCERPC) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data,sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_DCERPC) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_DCERPC);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test why we still get http for connect... obviously because we also match on the reply, duh */
|
|
|
|
int AlpDetectTest11(void) {
|
|
|
|
uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
|
|
|
|
uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
|
|
|
|
|
|
|
|
if (ctx.toserver.id != 6) {
|
|
|
|
printf("ctx.toserver.id %u != 6: ", ctx.toserver.id);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toserver.map[ctx.toserver.id - 1] != ALPROTO_HTTP) {
|
|
|
|
printf("ctx.toserver.id %u != %u: ", ctx.toserver.map[ctx.toserver.id - 1],ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data, sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto == ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " == %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data_resp, sizeof(l7data_resp), STREAM_TOSERVER, IPPROTO_TCP);
|
|
|
|
if (proto != ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test AlpProtoSignature test */
|
|
|
|
int AlpDetectTest12(void) {
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_TCP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
|
|
|
|
if (ctx.head == NULL) {
|
|
|
|
printf("ctx.head == NULL: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.head->proto != ALPROTO_HTTP) {
|
|
|
|
printf("ctx.head->proto != ALPROTO_HTTP: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.sigs != 1) {
|
|
|
|
printf("ctx.sigs %"PRIu16", expected 1: ", ctx.sigs);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.map == NULL) {
|
|
|
|
printf("no mapping: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.map[ctx.head->co->id] != ctx.head) {
|
|
|
|
printf("wrong sig: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = 1;
|
|
|
|
end:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \test What about if we add some sigs only for udp but call for tcp?
|
|
|
|
* It should not detect any proto
|
|
|
|
*/
|
|
|
|
int AlpDetectTest13(void) {
|
|
|
|
uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
|
|
|
|
uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
|
|
|
|
|
|
|
|
if (ctx.toserver.id != 6) {
|
|
|
|
printf("ctx.toserver.id %u != 6: ", ctx.toserver.id);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toserver.map[ctx.toserver.id - 1] != ALPROTO_HTTP) {
|
|
|
|
printf("ctx.toserver.id %u != %u: ", ctx.toserver.map[ctx.toserver.id - 1],ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data, sizeof(l7data), STREAM_TOCLIENT, IPPROTO_TCP);
|
|
|
|
if (proto == ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " == %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data_resp, sizeof(l7data_resp), STREAM_TOSERVER, IPPROTO_TCP);
|
|
|
|
if (proto == ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \test What about if we add some sigs only for udp calling it for UDP?
|
|
|
|
* It should detect ALPROTO_HTTP (over udp). This is just a check
|
|
|
|
* to ensure that TCP/UDP differences work correctly.
|
|
|
|
*/
|
|
|
|
int AlpDetectTest14(void) {
|
|
|
|
uint8_t l7data[] = "CONNECT www.ssllabs.com:443 HTTP/1.0\r\n";
|
|
|
|
uint8_t l7data_resp[] = "HTTP/1.1 405 Method Not Allowed\r\n";
|
|
|
|
int r = 1;
|
|
|
|
AlpProtoDetectCtx ctx;
|
|
|
|
AlpProtoDetectThreadCtx tctx;
|
|
|
|
|
|
|
|
AlpProtoInit(&ctx);
|
|
|
|
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "GET", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "PUT", 3, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "POST", 4, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "TRACE", 5, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "OPTIONS", 7, 0, STREAM_TOSERVER);
|
|
|
|
AlpProtoAdd(&ctx, "http", IPPROTO_UDP, ALPROTO_HTTP, "HTTP", 4, 0, STREAM_TOCLIENT);
|
|
|
|
|
|
|
|
if (ctx.toserver.id != 6) {
|
|
|
|
printf("ctx.toserver.id %u != 6: ", ctx.toserver.id);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.toserver.map[ctx.toserver.id - 1] != ALPROTO_HTTP) {
|
|
|
|
printf("ctx.toserver.id %u != %u: ", ctx.toserver.map[ctx.toserver.id - 1],ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoFinalizeGlobal(&ctx);
|
|
|
|
AlpProtoFinalizeThread(&ctx, &tctx);
|
|
|
|
|
|
|
|
uint8_t proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data, sizeof(l7data), STREAM_TOCLIENT, IPPROTO_UDP);
|
|
|
|
if (proto == ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " == %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
proto = AppLayerDetectGetProtoPMParser(&ctx, &tctx, l7data_resp, sizeof(l7data_resp), STREAM_TOSERVER, IPPROTO_UDP);
|
|
|
|
if (proto != ALPROTO_HTTP) {
|
|
|
|
printf("proto %" PRIu8 " != %" PRIu8 ": ", proto, ALPROTO_HTTP);
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AlpProtoTestDestroy(&ctx);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test test if the engine detect the proto and match with it */
|
|
|
|
static int AlpDetectTestSig1(void)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
Flow *f = NULL;
|
|
|
|
HtpState *http_state = NULL;
|
|
|
|
uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
|
|
|
|
"User-Agent: Mozilla/1.0\r\n"
|
|
|
|
"Cookie: hellocatch\r\n\r\n";
|
|
|
|
uint32_t http_buf1_len = sizeof(http_buf1) - 1;
|
|
|
|
TcpSession ssn;
|
|
|
|
Packet *p = NULL;
|
|
|
|
Signature *s = NULL;
|
|
|
|
ThreadVars tv;
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
|
|
|
|
|
|
memset(&tv, 0, sizeof(ThreadVars));
|
|
|
|
memset(&ssn, 0, sizeof(TcpSession));
|
|
|
|
|
|
|
|
p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
|
|
|
|
if (p == NULL) {
|
|
|
|
printf("packet setup failed: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
|
|
|
|
if (f == NULL) {
|
|
|
|
printf("flow setup failed: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
f->protoctx = &ssn;
|
|
|
|
p->flow = f;
|
|
|
|
|
|
|
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
|
|
|
p->flowflags |= FLOW_PKT_ESTABLISHED;
|
|
|
|
p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
|
|
|
|
|
|
|
|
f->alproto = ALPROTO_HTTP;
|
|
|
|
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
|
|
|
|
de_ctx = DetectEngineCtxInit();
|
|
|
|
if (de_ctx == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
de_ctx->mpm_matcher = MPM_B2G;
|
|
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
|
|
|
|
s = de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
|
|
|
|
"(msg:\"Test content option\"; "
|
|
|
|
"sid:1;)");
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SigGroupBuild(de_ctx);
|
|
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
|
|
|
|
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
|
|
|
|
if (r != 0) {
|
|
|
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
http_state = f->alstate;
|
|
|
|
if (http_state == NULL) {
|
|
|
|
printf("no http state: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do detect */
|
|
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
|
|
|
|
if (!PacketAlertCheck(p, 1)) {
|
|
|
|
printf("sig 1 didn't alert, but it should: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
result = 1;
|
|
|
|
end:
|
|
|
|
if (det_ctx != NULL)
|
|
|
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
SigGroupCleanup(de_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
|
|
|
|
StreamTcpFreeConfig(TRUE);
|
|
|
|
|
|
|
|
UTHFreePackets(&p, 1);
|
|
|
|
UTHFreeFlow(f);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test test if the engine detect the proto on a non standar port
|
|
|
|
* and match with it */
|
|
|
|
static int AlpDetectTestSig2(void)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
Flow *f = NULL;
|
|
|
|
HtpState *http_state = NULL;
|
|
|
|
uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
|
|
|
|
"User-Agent: Mozilla/1.0\r\n"
|
|
|
|
"Cookie: hellocatch\r\n\r\n";
|
|
|
|
uint32_t http_buf1_len = sizeof(http_buf1) - 1;
|
|
|
|
TcpSession ssn;
|
|
|
|
Packet *p = NULL;
|
|
|
|
Signature *s = NULL;
|
|
|
|
ThreadVars tv;
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
|
|
|
|
|
|
memset(&tv, 0, sizeof(ThreadVars));
|
|
|
|
memset(&ssn, 0, sizeof(TcpSession));
|
|
|
|
|
|
|
|
p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88);
|
|
|
|
|
|
|
|
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
|
|
|
|
if (f == NULL)
|
|
|
|
goto end;
|
|
|
|
f->protoctx = &ssn;
|
|
|
|
p->flow = f;
|
|
|
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
|
|
|
p->flowflags |= FLOW_PKT_ESTABLISHED;
|
|
|
|
p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
|
|
|
|
f->alproto = ALPROTO_HTTP;
|
|
|
|
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
|
|
|
|
de_ctx = DetectEngineCtxInit();
|
|
|
|
if (de_ctx == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
de_ctx->mpm_matcher = MPM_B2G;
|
|
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
|
|
|
|
s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any "
|
|
|
|
"(msg:\"http over non standar port\"; "
|
|
|
|
"sid:1;)");
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SigGroupBuild(de_ctx);
|
|
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
|
|
|
|
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
|
|
|
|
if (r != 0) {
|
|
|
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
http_state = f->alstate;
|
|
|
|
if (http_state == NULL) {
|
|
|
|
printf("no http state: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do detect */
|
|
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
|
|
|
|
if (!PacketAlertCheck(p, 1)) {
|
|
|
|
printf("sig 1 didn't alert, but it should: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = 1;
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (det_ctx != NULL)
|
|
|
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
SigGroupCleanup(de_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
|
|
|
|
StreamTcpFreeConfig(TRUE);
|
|
|
|
|
|
|
|
UTHFreePackets(&p, 1);
|
|
|
|
UTHFreeFlow(f);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test test if the engine detect the proto and doesn't match
|
|
|
|
* because the sig expects another proto (ex ftp)*/
|
|
|
|
static int AlpDetectTestSig3(void)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
Flow *f = NULL;
|
|
|
|
HtpState *http_state = NULL;
|
|
|
|
uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
|
|
|
|
"User-Agent: Mozilla/1.0\r\n"
|
|
|
|
"Cookie: hellocatch\r\n\r\n";
|
|
|
|
uint32_t http_buf1_len = sizeof(http_buf1) - 1;
|
|
|
|
TcpSession ssn;
|
|
|
|
Packet *p = NULL;
|
|
|
|
Signature *s = NULL;
|
|
|
|
ThreadVars tv;
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
|
|
|
|
|
|
memset(&tv, 0, sizeof(ThreadVars));
|
|
|
|
memset(&ssn, 0, sizeof(TcpSession));
|
|
|
|
|
|
|
|
p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
|
|
|
|
|
|
|
|
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
|
|
|
|
if (f == NULL)
|
|
|
|
goto end;
|
|
|
|
f->protoctx = &ssn;
|
|
|
|
p->flow = f;
|
|
|
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
|
|
|
p->flowflags |= FLOW_PKT_ESTABLISHED;
|
|
|
|
p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
|
|
|
|
f->alproto = ALPROTO_HTTP;
|
|
|
|
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
|
|
|
|
de_ctx = DetectEngineCtxInit();
|
|
|
|
if (de_ctx == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
de_ctx->mpm_matcher = MPM_B2G;
|
|
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
|
|
|
|
s = de_ctx->sig_list = SigInit(de_ctx, "alert ftp any any -> any any "
|
|
|
|
"(msg:\"Test content option\"; "
|
|
|
|
"sid:1;)");
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SigGroupBuild(de_ctx);
|
|
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
|
|
|
|
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
|
|
|
|
if (r != 0) {
|
|
|
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
http_state = f->alstate;
|
|
|
|
if (http_state == NULL) {
|
|
|
|
printf("no http state: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do detect */
|
|
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
|
|
|
|
if (PacketAlertCheck(p, 1)) {
|
|
|
|
printf("sig 1 alerted, but it should not (it's not ftp): ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = 1;
|
|
|
|
end:
|
|
|
|
if (det_ctx != NULL)
|
|
|
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
SigGroupCleanup(de_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
|
|
|
|
StreamTcpFreeConfig(TRUE);
|
|
|
|
|
|
|
|
UTHFreePackets(&p, 1);
|
|
|
|
UTHFreeFlow(f);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test test if the engine detect the proto and doesn't match
|
|
|
|
* because the packet has another proto (ex ftp) */
|
|
|
|
static int AlpDetectTestSig4(void)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
Flow *f = NULL;
|
|
|
|
uint8_t http_buf1[] = "MPUT one\r\n";
|
|
|
|
uint32_t http_buf1_len = sizeof(http_buf1) - 1;
|
|
|
|
TcpSession ssn;
|
|
|
|
Packet *p = NULL;
|
|
|
|
Signature *s = NULL;
|
|
|
|
ThreadVars tv;
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
|
|
|
|
|
|
memset(&tv, 0, sizeof(ThreadVars));
|
|
|
|
memset(&ssn, 0, sizeof(TcpSession));
|
|
|
|
|
|
|
|
p = UTHBuildPacketSrcDstPorts(http_buf1, http_buf1_len, IPPROTO_TCP, 12345, 88);
|
|
|
|
|
|
|
|
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
|
|
|
|
if (f == NULL)
|
|
|
|
goto end;
|
|
|
|
f->protoctx = &ssn;
|
|
|
|
p->flow = f;
|
|
|
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
|
|
|
p->flowflags |= FLOW_PKT_ESTABLISHED;
|
|
|
|
p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
|
|
|
|
f->alproto = ALPROTO_FTP;
|
|
|
|
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
|
|
|
|
de_ctx = DetectEngineCtxInit();
|
|
|
|
if (de_ctx == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
de_ctx->mpm_matcher = MPM_B2G;
|
|
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
|
|
|
|
s = de_ctx->sig_list = SigInit(de_ctx, "alert http any !80 -> any any "
|
|
|
|
"(msg:\"http over non standar port\"; "
|
|
|
|
"sid:1;)");
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SigGroupBuild(de_ctx);
|
|
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
|
|
|
|
int r = AppLayerParse(NULL, f, ALPROTO_FTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
|
|
|
|
if (r != 0) {
|
|
|
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do detect */
|
|
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
|
|
|
|
if (PacketAlertCheck(p, 1)) {
|
|
|
|
printf("sig 1 alerted, but it should not (it's ftp): ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = 1;
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (det_ctx != NULL)
|
|
|
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
SigGroupCleanup(de_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
|
|
|
|
StreamTcpFreeConfig(TRUE);
|
|
|
|
UTHFreePackets(&p, 1);
|
|
|
|
UTHFreeFlow(f);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \test test if the engine detect the proto and match with it
|
|
|
|
* and also against a content option */
|
|
|
|
static int AlpDetectTestSig5(void)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
Flow *f = NULL;
|
|
|
|
uint8_t http_buf1[] = "POST /one HTTP/1.0\r\n"
|
|
|
|
"User-Agent: Mozilla/1.0\r\n"
|
|
|
|
"Cookie: hellocatch\r\n\r\n";
|
|
|
|
uint32_t http_buf1_len = sizeof(http_buf1) - 1;
|
|
|
|
TcpSession ssn;
|
|
|
|
Packet *p = NULL;
|
|
|
|
Signature *s = NULL;
|
|
|
|
ThreadVars tv;
|
|
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
|
|
DetectEngineCtx *de_ctx = NULL;
|
|
|
|
|
|
|
|
memset(&tv, 0, sizeof(ThreadVars));
|
|
|
|
memset(&ssn, 0, sizeof(TcpSession));
|
|
|
|
|
|
|
|
p = UTHBuildPacket(http_buf1, http_buf1_len, IPPROTO_TCP);
|
|
|
|
|
|
|
|
f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1024, 80);
|
|
|
|
if (f == NULL)
|
|
|
|
goto end;
|
|
|
|
f->protoctx = &ssn;
|
|
|
|
p->flow = f;
|
|
|
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
|
|
|
p->flowflags |= FLOW_PKT_ESTABLISHED;
|
|
|
|
p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
|
|
|
|
f->alproto = ALPROTO_HTTP;
|
|
|
|
f->proto = IPPROTO_TCP;
|
|
|
|
p->flags |= PKT_STREAM_ADD;
|
|
|
|
p->flags |= PKT_STREAM_EOF;
|
|
|
|
|
|
|
|
de_ctx = DetectEngineCtxInit();
|
|
|
|
if (de_ctx == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
|
|
|
|
StreamMsg *stream_msg = StreamMsgGetFromPool();
|
|
|
|
if (stream_msg == NULL) {
|
|
|
|
printf("no stream_msg: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(stream_msg->data.data, http_buf1, http_buf1_len);
|
|
|
|
stream_msg->data.data_len = http_buf1_len;
|
|
|
|
|
|
|
|
ssn.toserver_smsg_head = stream_msg;
|
|
|
|
ssn.toserver_smsg_tail = stream_msg;
|
|
|
|
|
|
|
|
de_ctx->mpm_matcher = MPM_B2G;
|
|
|
|
de_ctx->flags |= DE_QUIET;
|
|
|
|
|
|
|
|
s = de_ctx->sig_list = SigInit(de_ctx, "alert http any any -> any any "
|
|
|
|
"(msg:\"Test content option\"; "
|
|
|
|
"content:\"one\"; sid:1;)");
|
|
|
|
if (s == NULL) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
SigGroupBuild(de_ctx);
|
|
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
|
|
|
|
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER, http_buf1, http_buf1_len);
|
|
|
|
if (r != 0) {
|
|
|
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do detect */
|
|
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
|
|
|
|
if (!PacketAlertCheck(p, 1)) {
|
|
|
|
printf("sig 1 didn't alert, but it should: ");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = 1;
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (det_ctx != NULL)
|
|
|
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
SigGroupCleanup(de_ctx);
|
|
|
|
if (de_ctx != NULL)
|
|
|
|
DetectEngineCtxFree(de_ctx);
|
|
|
|
|
|
|
|
StreamTcpFreeConfig(TRUE);
|
|
|
|
UTHFreePackets(&p, 1);
|
|
|
|
UTHFreeFlow(f);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* UNITTESTS */
|
|
|
|
|
|
|
|
void AlpDetectRegisterTests(void) {
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
UtRegisterTest("AlpDetectTest01", AlpDetectTest01, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest02", AlpDetectTest02, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest03", AlpDetectTest03, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest04", AlpDetectTest04, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest05", AlpDetectTest05, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest06", AlpDetectTest06, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest07", AlpDetectTest07, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest08", AlpDetectTest08, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest09", AlpDetectTest09, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest10", AlpDetectTest10, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest11", AlpDetectTest11, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest12", AlpDetectTest12, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest13", AlpDetectTest13, 1);
|
|
|
|
UtRegisterTest("AlpDetectTest14", AlpDetectTest14, 1);
|
|
|
|
UtRegisterTest("AlpDetectTestSig1", AlpDetectTestSig1, 1);
|
|
|
|
UtRegisterTest("AlpDetectTestSig2", AlpDetectTestSig2, 1);
|
|
|
|
UtRegisterTest("AlpDetectTestSig3", AlpDetectTestSig3, 1);
|
|
|
|
UtRegisterTest("AlpDetectTestSig4", AlpDetectTestSig4, 1);
|
|
|
|
UtRegisterTest("AlpDetectTestSig5", AlpDetectTestSig5, 1);
|
|
|
|
#endif /* UNITTESTS */
|
|
|
|
}
|