/* Copyright (C) 2007-2017 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 Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
 *
 * This file provide a set of helper functions for reducing the complexity
 * when constructing unittests
 */

#include "suricata-common.h"

#include "decode.h"

#include "flow-private.h"
#include "flow-util.h"
#include "flow-spare-pool.h"

#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-alert.h"
#include "detect-engine-sigorder.h"
#include "detect-engine-build.h"

#include "stream-tcp.h"
#include "stream-tcp-private.h"

#include "util-debug.h"
#include "util-time.h"
#include "util-error.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"

#if defined(UNITTESTS) || defined(FUZZ)
Flow *TestHelperBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)
{
    struct in_addr in;

    Flow *f = SCMalloc(sizeof(Flow));
    if (unlikely(f == NULL)) {
        printf("FlowAlloc failed\n");
        ;
        return NULL;
    }
    memset(f, 0x00, sizeof(Flow));

    FLOW_INITIALIZE(f);

    if (family == AF_INET) {
        f->flags |= FLOW_IPV4;
    } else if (family == AF_INET6) {
        f->flags |= FLOW_IPV6;
    }

    if (src != NULL) {
        if (family == AF_INET) {
            if (inet_pton(AF_INET, src, &in) != 1) {
                printf("invalid address %s\n", src);
                SCFree(f);
                return NULL;
            }
            f->src.addr_data32[0] = in.s_addr;
        } else {
            BUG_ON(1);
        }
    }
    if (dst != NULL) {
        if (family == AF_INET) {
            if (inet_pton(AF_INET, dst, &in) != 1) {
                printf("invalid address %s\n", dst);
                SCFree(f);
                return NULL;
            }
            f->dst.addr_data32[0] = in.s_addr;
        } else {
            BUG_ON(1);
        }
    }

    f->sp = sp;
    f->dp = dp;

    return f;
}
/** \brief writes the contents of a buffer into a file */
int TestHelperBufferToFile(const char *name, const uint8_t *data, size_t size)
{
    if (remove(name) != 0) {
        if (errno != ENOENT) {
            printf("failed remove, errno=%d\n", errno);
            return -1;
        }
    }
    FILE *fd = fopen(name, "wb");
    if (fd == NULL) {
        printf("failed open, errno=%d\n", errno);
        return -2;
    }
    if (fwrite (data, 1, size, fd) != size) {
        fclose(fd);
        return -3;
    }
    fclose(fd);
    return 0;
}

#endif
#ifdef UNITTESTS
void UTHSetIPV4Hdr(Packet *p, IPV4Hdr *ip4h)
{
    PacketSetIPV4(p, (uint8_t *)ip4h);
}

void UTHSetIPV6Hdr(Packet *p, IPV6Hdr *ip6h)
{
    PacketSetIPV6(p, (uint8_t *)ip6h);
}

void UTHSetTCPHdr(Packet *p, TCPHdr *tcph)
{
    PacketSetTCP(p, (uint8_t *)tcph);
}

/**
 *  \brief return the uint32_t for a ipv4 address string
 *
 *  \param str Valid ipaddress in string form (e.g. 1.2.3.4)
 *
 *  \retval uint the uin32_t representation
 */
uint32_t UTHSetIPv4Address(const char *str)
{
    struct in_addr in;
    if (inet_pton(AF_INET, str, &in) != 1) {
        printf("invalid IPv6 address %s\n", str);
        exit(EXIT_FAILURE);
    }
    return (uint32_t)in.s_addr;
}

/**
 * \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
 * specifying ip and port sources and destinations (IPV6)
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 * \param src pointer to a string containing the ip source
 * \param dst pointer to a string containing the ip destination
 * \param sport pointer to a string containing the port source
 * \param dport pointer to a string containing the port destination
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacketIPV6Real(uint8_t *payload, uint16_t payload_len,
                           uint8_t ipproto, const char *src, const char *dst,
                           uint16_t sport, uint16_t dport)
{
    uint32_t in[4];
    TCPHdr *tcph = NULL;

    Packet *p = PacketGetFromAlloc();
    if (unlikely(p == NULL))
        return NULL;

    p->ts = TimeGet();

    p->src.family = AF_INET6;
    p->dst.family = AF_INET6;
    p->payload = payload;
    p->payload_len = payload_len;
    p->proto = ipproto;

    IPV6Hdr *ip6h = SCCalloc(1, sizeof(IPV6Hdr));
    if (ip6h == NULL)
        goto error;
    ip6h->s_ip6_nxt = ipproto;
    ip6h->s_ip6_plen = htons(payload_len + sizeof(TCPHdr));
    UTHSetIPV6Hdr(p, ip6h);

    if (inet_pton(AF_INET6, src, &in) != 1)
        goto error;
    p->src.addr_data32[0] = in[0];
    p->src.addr_data32[1] = in[1];
    p->src.addr_data32[2] = in[2];
    p->src.addr_data32[3] = in[3];
    p->sp = sport;
    ip6h->s_ip6_src[0] = in[0];
    ip6h->s_ip6_src[1] = in[1];
    ip6h->s_ip6_src[2] = in[2];
    ip6h->s_ip6_src[3] = in[3];

    if (inet_pton(AF_INET6, dst, &in) != 1)
        goto error;
    p->dst.addr_data32[0] = in[0];
    p->dst.addr_data32[1] = in[1];
    p->dst.addr_data32[2] = in[2];
    p->dst.addr_data32[3] = in[3];
    p->dp = dport;
    ip6h->s_ip6_dst[0] = in[0];
    ip6h->s_ip6_dst[1] = in[1];
    ip6h->s_ip6_dst[2] = in[2];
    ip6h->s_ip6_dst[3] = in[3];

    tcph = SCMalloc(sizeof(TCPHdr));
    if (tcph == NULL)
        goto error;
    memset(tcph, 0, sizeof(TCPHdr));
    tcph->th_sport = htons(sport);
    tcph->th_dport = htons(dport);
    UTHSetTCPHdr(p, tcph);

    SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(TCPHdr) + payload_len);
    return p;

error:
    if (p != NULL) {
        if (ip6h != NULL) {
            SCFree(ip6h);
        }
        if (tcph != NULL) {
            SCFree(tcph);
        }
        SCFree(p);
    }
    return NULL;
}

/**
 * \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
 * specifying ip and port sources and destinations
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 * \param src pointer to a string containing the ip source
 * \param dst pointer to a string containing the ip destination
 * \param sport pointer to a string containing the port source
 * \param dport pointer to a string containing the port destination
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len,
                           uint8_t ipproto, const char *src, const char *dst,
                           uint16_t sport, uint16_t dport)
{
    struct in_addr in;

    Packet *p = PacketGetFromAlloc();
    if (unlikely(p == NULL))
        return NULL;

    p->ts = TimeGet();

    p->src.family = AF_INET;
    p->dst.family = AF_INET;
    p->payload = payload;
    p->payload_len = payload_len;
    p->proto = ipproto;

    if (inet_pton(AF_INET, src, &in) != 1)
        goto error;
    p->src.addr_data32[0] = in.s_addr;
    if (ipproto == IPPROTO_TCP || ipproto == IPPROTO_UDP || ipproto == IPPROTO_SCTP)
        p->sp = sport;

    if (inet_pton(AF_INET, dst, &in) != 1)
        goto error;
    p->dst.addr_data32[0] = in.s_addr;
    if (ipproto == IPPROTO_TCP || ipproto == IPPROTO_UDP || ipproto == IPPROTO_SCTP)
        p->dp = dport;

    IPV4Hdr *ip4h = PacketSetIPV4(p, GET_PKT_DATA(p));
    if (ip4h == NULL)
        goto error;

    ip4h->s_ip_src.s_addr = p->src.addr_data32[0];
    ip4h->s_ip_dst.s_addr = p->dst.addr_data32[0];
    ip4h->ip_proto = ipproto;
    ip4h->ip_verhl = sizeof(IPV4Hdr);
    p->proto = ipproto;

    int hdr_offset = sizeof(IPV4Hdr);
    switch (ipproto) {
        case IPPROTO_UDP: {
            UDPHdr *udph = PacketSetUDP(p, (GET_PKT_DATA(p) + sizeof(IPV4Hdr)));
            if (udph == NULL)
                goto error;

            udph->uh_sport = sport;
            udph->uh_dport = dport;
            hdr_offset += sizeof(UDPHdr);
            break;
        }
        case IPPROTO_TCP: {
            TCPHdr *tcph = PacketSetTCP(p, GET_PKT_DATA(p) + sizeof(IPV4Hdr));
            if (tcph == NULL)
                goto error;

            tcph->th_sport = htons(sport);
            tcph->th_dport = htons(dport);
            hdr_offset += sizeof(TCPHdr);
            break;
        }
        case IPPROTO_ICMP: {
            ICMPV4Hdr *icmpv4h = PacketSetICMPv4(p, (GET_PKT_DATA(p) + sizeof(IPV4Hdr)));
            if (icmpv4h == NULL)
                goto error;

            hdr_offset += sizeof(ICMPV4Hdr);
            break;
        }
        default:
            break;
        /* TODO: Add more protocols */
    }

    if (payload && payload_len) {
        PacketCopyDataOffset(p, hdr_offset, payload, payload_len);
    }
    SET_PKT_LEN(p, hdr_offset + payload_len);
    p->payload = GET_PKT_DATA(p)+hdr_offset;
    p->app_update_direction = UPDATE_DIR_BOTH;

    return p;

error:
    SCFree(p);
    return NULL;
}

/**
 * \brief UTHBuildPacket is a wrapper that build packets with default ip
 * and port fields
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacket(uint8_t *payload, uint16_t payload_len,
                           uint8_t ipproto)
{
    return UTHBuildPacketReal(payload, payload_len, ipproto,
                              "192.168.1.5", "192.168.1.1",
                              41424, 80);
}

/**
 * \brief UTHBuildPacketFromEth is a wrapper that build a packet for the rawbytes
 *
 * \param raw_eth pointer to the rawbytes containing an ethernet packet
 *                    (and any other headers inside)
 * \param pktsize pointer to the length of the payload
 *
 * \retval Packet pointer to the built in packet; NULL if something fail
 */
Packet *UTHBuildPacketFromEth(uint8_t *raw_eth, uint16_t pktsize)
{
    DecodeThreadVars dtv;
    ThreadVars th_v;
    Packet *p = PacketGetFromAlloc();
    if (unlikely(p == NULL))
        return NULL;
    memset(&dtv, 0, sizeof(DecodeThreadVars));
    memset(&th_v, 0, sizeof(th_v));

    DecodeEthernet(&th_v, &dtv, p, raw_eth, pktsize);
    return p;
}

/**
 * \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
 * and defaulting ports
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacketSrcDst(uint8_t *payload, uint16_t payload_len,
                             uint8_t ipproto, const char *src, const char *dst)
{
    return UTHBuildPacketReal(payload, payload_len, ipproto,
                              src, dst,
                              41424, 80);
}

/**
 * \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
 * and defaulting ports (IPV6)
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacketIPV6SrcDst(uint8_t *payload, uint16_t payload_len,
                           uint8_t ipproto, const char *src, const char *dst)
{
    return UTHBuildPacketIPV6Real(payload, payload_len, ipproto,
                              src, dst,
                              41424, 80);
}

/**
 * \brief UTHBuildPacketSrcDstPorts is a wrapper that build packets specifying
 * src and dst ports and defaulting IPs
 *
 * \param payload pointer to the payload buffer
 * \param payload_len pointer to the length of the payload
 * \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
 *
 * \retval Packet pointer to the built in packet
 */
Packet *UTHBuildPacketSrcDstPorts(uint8_t *payload, uint16_t payload_len,
                           uint8_t ipproto, uint16_t sport, uint16_t dport)
{
    return UTHBuildPacketReal(payload, payload_len, ipproto,
                              "192.168.1.5", "192.168.1.1",
                              sport, dport);
}

/**
 * \brief UTHFreePackets: function to release the allocated data
 * from UTHBuildPacket and the packet itself
 *
 * \param p pointer to the Packet
 */
void UTHFreePackets(Packet **p, int numpkts)
{
    if (p == NULL)
        return;

    int i = 0;
    for (; i < numpkts; i++) {
        UTHFreePacket(p[i]);
    }
}

/**
 * \brief UTHFreePacket: function to release the allocated data
 * from UTHBuildPacket and the packet itself
 *
 * \param p pointer to the Packet
 */
void UTHFreePacket(Packet *p)
{
    if (p == NULL)
        return;
    PacketFree(p);
}

void UTHAssignFlow(Packet *p, Flow *f)
{
    if (p && f) {
        p->flow = f;
        p->flags |= PKT_HAS_FLOW;
    }
}

Flow *UTHBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)
{
    return TestHelperBuildFlow(family, src, dst, sp, dp);
}

void UTHFreeFlow(Flow *flow)
{
    if (flow != NULL) {
        SCFree(flow);//FlowFree(flow);
    }
}

int UTHAddStreamToFlow(Flow *f, int direction,
    uint8_t *data, uint32_t data_len)
{
    FAIL_IF_NULL(f);
    FAIL_IF_NOT(f->proto == IPPROTO_TCP);
    FAIL_IF_NULL(f->protoctx);
    TcpSession *ssn = f->protoctx;

    StreamingBufferSegment seg;
    TcpStream *stream = direction == 0 ? &ssn->client : &ssn->server;
    int r = StreamingBufferAppend(&stream->sb, &stream_config.sbcnf, &seg, data, data_len);
    FAIL_IF_NOT(r == 0);
    stream->last_ack += data_len;
    return 1;
}

int UTHAddSessionToFlow(Flow *f,
    uint32_t ts_isn,
    uint32_t tc_isn)
{
    FAIL_IF_NULL(f);

    TcpSession *ssn = SCCalloc(1, sizeof(*ssn));
    FAIL_IF_NULL(ssn);

    StreamingBuffer x = STREAMING_BUFFER_INITIALIZER;
    ssn->client.sb = x;
    ssn->server.sb = x;

    ssn->client.isn = ts_isn;
    ssn->server.isn = tc_isn;

    f->protoctx = ssn;
    return 1;
}

int UTHRemoveSessionFromFlow(Flow *f)
{
    FAIL_IF_NULL(f);
    FAIL_IF_NOT(f->proto == IPPROTO_TCP);
    TcpSession *ssn = f->protoctx;
    FAIL_IF_NULL(ssn);
    StreamTcpSessionCleanup(ssn);
    SCFree(ssn);
    f->protoctx = NULL;
    return 1;
}

/**
 * \brief UTHGenericTest: function that perform a generic check taking care of
 *                      as maximum common unittest elements as possible.
 *                      It will create a detection engine, append an array
 *                      of signatures an check the expected results for each
 *                      of them, it check matches for an array of packets
 *
 * \param pkt pointer to the array of packets
 * \param numpkts number of packets to match
 * \param sigs array of char* pointing to signatures to load
 * \param numsigs number of signatures to load and check
 * \param results pointer to arrays of numbers, each of them foreach packet
 *                to check if sids matches that packet as expected with
 *                that number of times or not. The size of results should be
 *                numpkts * numsigs * sizeof(uint16_t *)
 *
 *                Example:
 *                result[1][3] would mean the number of times the pkt[1]
 *                match the sid[3]
 *
 * \retval int 1 if the match of all the sids is the specified has the
 *             specified results; 0 if not
 */
int UTHGenericTest(Packet **pkt, int numpkts, const char *sigs[], uint32_t sids[], uint32_t *results, int numsigs)
{

    int result = 0;
    if (pkt == NULL || sigs == NULL || numpkts == 0
        || sids == NULL || results == NULL || numsigs == 0) {
        SCLogError("Arguments invalid, that the pointer/arrays are not NULL, and the number of "
                   "signatures and packets is > 0");
        goto end;
    }
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL) {
        goto end;
    }
    de_ctx->flags |= DE_QUIET;

    if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
        goto cleanup;

    result = UTHMatchPacketsWithResults(de_ctx, pkt, numpkts, sids, results, numsigs);

cleanup:
    DetectEngineCtxFree(de_ctx);
end:
    return result;
}

/**
 * \brief UTHCheckPacketMatches: function to check if a packet match some sids
 *
 *
 * \param p pointer to the Packet
 * \param sigs array of char* pointing to signatures to load
 * \param numsigs number of signatures to load from the array
 * \param results pointer to an array of numbers to check if sids matches
 *                that number of times or not.
 *
 * \retval int 1 if the match of all the sids is the specified has the
 *             specified results; 0 if not
 */
int UTHCheckPacketMatchResults(Packet *p, uint32_t sids[], uint32_t results[], int numsigs)
{
    if (p == NULL || sids == NULL) {
        SCLogError("Arguments invalid, check if the "
                   "packet is NULL, and if the array contain sids is set");
        return 0;
    }

    int i = 0;
    int res = 1;
    for (; i < numsigs; i++) {
        uint32_t r = PacketAlertCheck(p, sids[i]);
        if (r != results[i]) {
            SCLogInfo("Sid %" PRIu32 " matched %" PRIu32 " times, and not %" PRIu32 " as expected",
                    sids[i], r, results[i]);
            res = 0;
        } else {
            SCLogInfo("Sid %" PRIu32 " matched %" PRIu32 " times, as expected", sids[i], r);
        }
    }
    return res;
}

/**
 * \brief UTHAppendSigs: Add sigs to the detection_engine checking for errors
 *
 * \param de_ctx pointer to the DetectEngineCtx used
 * \param sigs array of char* pointing to signatures to load
 * \param numsigs number of signatures to load from the array
 *                (size of the array)
 *
 * \retval int 0 if we have errors; 1 if all the signatures loaded successfully
 */
int UTHAppendSigs(DetectEngineCtx *de_ctx, const char *sigs[], int numsigs)
{
    BUG_ON(de_ctx == NULL);
    BUG_ON(numsigs <= 0);
    BUG_ON(sigs == NULL);

    for (int i = 0; i < numsigs; i++) {
        if (sigs[i] == NULL) {
            SCLogError("Check the signature"
                       " at position %d",
                    i);
            return 0;
        }
        Signature *s = DetectEngineAppendSig(de_ctx, sigs[i]);
        if (s == NULL) {
            SCLogError("Check the signature at"
                       " position %d (%s)",
                    i, sigs[i]);
            return 0;
        }
    }
    return 1;
}

/**
 * \test UTHMatchPacketsWithResults Match a packet or a array of packets against sigs
 * of a de_ctx, checking that each signature matches X times for certain packets
 *
 * \param de_ctx pointer with the signatures loaded
 * \param p pointer to the array of packets
 * \param num_packets number of packets in the array
 *
 * \retval return 1 if all goes well
 * \retval return 0 if something fail
 */
int UTHMatchPacketsWithResults(DetectEngineCtx *de_ctx, Packet **p, int num_packets, uint32_t sids[], uint32_t *results, int numsigs)
{
    BUG_ON(de_ctx == NULL);
    BUG_ON(p == NULL);

    int result = 0;
    DecodeThreadVars dtv;
    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;
    memset(&dtv, 0, sizeof(DecodeThreadVars));
    memset(&th_v, 0, sizeof(th_v));

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    for (int i = 0; i < num_packets; i++) {
        SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
        if (UTHCheckPacketMatchResults(p[i], sids, &results[(i * numsigs)], numsigs) == 0)
            goto cleanup;
    }

    result = 1;
cleanup:
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    return result;
}

/**
 * \test UTHMatchPackets Match a packet or a array of packets against sigs
 * of a de_ctx, but note that the return value doesn't mean that we have a
 * match, we have to check it later with PacketAlertCheck()
 *
 * \param de_ctx pointer with the signatures loaded
 * \param p pointer to the array of packets
 * \param num_packets number of packets in the array
 *
 * \retval return 1 if all goes well
 * \retval return 0 if something fail
 */
int UTHMatchPackets(DetectEngineCtx *de_ctx, Packet **p, int num_packets)
{
    BUG_ON(de_ctx == NULL);
    BUG_ON(p == NULL);
    int result = 1;
    DecodeThreadVars dtv;
    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;
    memset(&dtv, 0, sizeof(DecodeThreadVars));
    memset(&th_v, 0, sizeof(th_v));
    SCSigRegisterSignatureOrderingFuncs(de_ctx);
    SCSigOrderSignatures(de_ctx);
    SCSigSignatureOrderingModuleCleanup(de_ctx);
    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    for (int i = 0; i < num_packets; i++)
        SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);

    /* Here we don't check if the packet matched or not, because
     * the de_ctx can have multiple signatures, and some of them may match
     * and others may not. That check will be outside
     */
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    if (de_ctx != NULL) SigGroupCleanup(de_ctx);
    return result;
}

/**
 * \test Test if a packet match a signature given as string and a mpm_type
 * Hint: Useful for unittests with only one packet and one signature
 *
 * \param sig pointer to the string signature to test
 * \param sid sid number of the signature
 *
 * \retval return 1 if match
 * \retval return 0 if not
 */
int UTHPacketMatchSigMpm(Packet *p, char *sig, uint16_t mpm_type)
{
    SCEnter();

    int result = 0;

    DecodeThreadVars dtv;
    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;

    memset(&dtv, 0, sizeof(DecodeThreadVars));
    memset(&th_v, 0, sizeof(th_v));

    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL) {
        printf("de_ctx == NULL: ");
        goto end;
    }

    de_ctx->flags |= DE_QUIET;
    de_ctx->mpm_matcher = mpm_type;

    de_ctx->sig_list = SigInit(de_ctx, sig);
    if (de_ctx->sig_list == NULL) {
        printf("signature == NULL: ");
        goto end;
    }

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
    if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
        printf("signature didn't alert: ");
        goto end;
    }

    result = 1;
end:
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    DetectEngineCtxFree(de_ctx);
    SCReturnInt(result);
}

/**
 * \test Test if a packet match a signature given as string
 * Hint: Useful for unittests with only one packet and one signature
 *
 * \param sig pointer to the string signature to test
 * \param sid sid number of the signature
 *
 * \retval return 1 if match
 * \retval return 0 if not
 */
int UTHPacketMatchSig(Packet *p, const char *sig)
{
    int result = 1;

    DecodeThreadVars dtv;

    ThreadVars th_v;
    DetectEngineThreadCtx *det_ctx = NULL;

    memset(&dtv, 0, sizeof(DecodeThreadVars));
    memset(&th_v, 0, sizeof(th_v));

    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    if (de_ctx == NULL) {
        result=0;
        goto end;
    }

    de_ctx->flags |= DE_QUIET;

    de_ctx->sig_list = SigInit(de_ctx, sig);
    if (de_ctx->sig_list == NULL) {
        result = 0;
        goto end;
    }

    SigGroupBuild(de_ctx);
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);

    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
    if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
        result = 0;
        goto end;
    }

end:
    if (de_ctx) {
	SigGroupCleanup(de_ctx);
	SigCleanSignatures(de_ctx);
    }

    if (det_ctx != NULL)
        DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
    if (de_ctx != NULL)
        DetectEngineCtxFree(de_ctx);

    return result;
}

uint32_t UTHBuildPacketOfFlows(uint32_t start, uint32_t end, uint8_t dir)
{
    FlowLookupStruct fls;
    memset(&fls, 0, sizeof(fls));

    uint32_t i = start;
    uint8_t payload[] = "Payload";
    for (; i < end; i++) {
        Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
        if (dir == 0) {
            p->src.addr_data32[0] = i;
            p->dst.addr_data32[0] = i + 1;
        } else {
            p->src.addr_data32[0] = i + 1;
            p->dst.addr_data32[0] = i;
        }
        FlowHandlePacket(NULL, &fls, p);
        if (p->flow != NULL) {
            FLOWLOCK_UNLOCK(p->flow);
        }

        /* Now the queues should be updated */
        UTHFreePacket(p);
    }

    Flow *f;
    while ((f = FlowQueuePrivateGetFromTop(&fls.spare_queue))) {
        FlowFree(f);
    }
    while ((f = FlowQueuePrivateGetFromTop(&fls.work_queue))) {
        FlowFree(f);
    }

    return i;
}

/** \brief parser a sig and see if the expected result is correct */
int UTHParseSignature(const char *str, bool expect)
{
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
    FAIL_IF_NULL(de_ctx);
    de_ctx->flags |= DE_QUIET;

    Signature *s = DetectEngineAppendSig(de_ctx, str);
    if (expect)
        FAIL_IF_NULL(s);
    else
        FAIL_IF_NOT_NULL(s);

    DetectEngineCtxFree(de_ctx);
    PASS;
}

/*
 * unittests for the unittest helpers
 */

/**
 * \brief CheckUTHTestPacket wrapper to check packets for unittests
 */
static int CheckUTHTestPacket(Packet *p, uint8_t ipproto)
{
    uint16_t sport = 41424;
    uint16_t dport = 80;
    uint8_t payload[] = "Payload";

    uint8_t len = sizeof(payload);

    if (p == NULL)
        return 0;

    if (p->payload_len != len)
        return 0;

    if (strncmp((char *)payload, (char *)p->payload, len) != 0)
        return 0;

    if (p->src.family != AF_INET)
        return 0;
    if (p->dst.family != AF_INET)
        return 0;
    if (p->proto != ipproto)
        return 0;

    switch(ipproto) {
        case IPPROTO_UDP: {
            const UDPHdr *udph = PacketGetUDP(p);
            if (udph == NULL)
                return 0;
            if (udph->uh_sport != sport)
                return 0;
            if (udph->uh_dport != dport)
                return 0;
        break;
        }
        case IPPROTO_TCP: {
            const TCPHdr *tcph = PacketGetTCP(p);
            if (tcph == NULL)
                return 0;
            if (SCNtohs(tcph->th_sport) != sport)
                return 0;
            if (SCNtohs(tcph->th_dport) != dport)
                return 0;
            break;
        }
    }
    return 1;
}

#ifdef HAVE_MEMMEM
#include <string.h>
void * UTHmemsearch(const void *big, size_t big_len, const void *little, size_t little_len) {
    return memmem(big, big_len, little, little_len);
}
#else
#include "util-spm-bs.h"
void * UTHmemsearch(const void *big, size_t big_len, const void *little, size_t little_len) {
    return BasicSearch(big, big_len, little, little_len);
}
#endif //HAVE_MEMMEM

/**
 * \brief UTHBuildPacketRealTest01 wrapper to check packets for unittests
 */
static int UTHBuildPacketRealTest01(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_TCP,
                                   "192.168.1.5", "192.168.1.1", 41424, 80);

    int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketRealTest02 wrapper to check packets for unittests
 */
static int UTHBuildPacketRealTest02(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_UDP,
                                   "192.168.1.5", "192.168.1.1", 41424, 80);

    int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
    UTHFreePacket(p);
    return ret;
}

/**
 * \brief UTHBuildPacketTest01 wrapper to check packets for unittests
 */
static int UTHBuildPacketTest01(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);

    int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketTest02 wrapper to check packets for unittests
 */
static int UTHBuildPacketTest02(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_UDP);

    int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketOfFlowsTest01 wrapper to check packets for unittests
 */
static int UTHBuildPacketOfFlowsTest01(void)
{
    int result = 0;

    FlowInitConfig(FLOW_QUIET);
    uint32_t flow_spare_q_len = FlowSpareGetPoolSize();

    UTHBuildPacketOfFlows(0, 100, 0);

    if (FlowSpareGetPoolSize() != flow_spare_q_len - 100)
        result = 0;
    else
        result = 1;
    FlowShutdown();

    return result;
}


/**
 * \brief UTHBuildPacketSrcDstTest01 wrapper to check packets for unittests
 */
static int UTHBuildPacketSrcDstTest01(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_TCP,
                                     "192.168.1.5", "192.168.1.1");

    int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketSrcDstTest02 wrapper to check packets for unittests
 */
static int UTHBuildPacketSrcDstTest02(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_UDP,
                                     "192.168.1.5", "192.168.1.1");

    int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketSrcDstPortsTest01 wrapper to check packets for unittests
 */
static int UTHBuildPacketSrcDstPortsTest01(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_TCP,
                                          41424, 80);

    int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
    UTHFreePacket(p);

    return ret;
}

/**
 * \brief UTHBuildPacketSrcDstPortsTest02 wrapper to check packets for unittests
 */
static int UTHBuildPacketSrcDstPortsTest02(void)
{
    uint8_t payload[] = "Payload";

    Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_UDP,
                                          41424, 80);

    int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
    UTHFreePacket(p);

    return ret;
}

#endif /* UNITTESTS */

void UTHRegisterTests(void)
{
#ifdef UNITTESTS
    UtRegisterTest("UTHBuildPacketRealTest01", UTHBuildPacketRealTest01);
    UtRegisterTest("UTHBuildPacketRealTest02", UTHBuildPacketRealTest02);
    UtRegisterTest("UTHBuildPacketTest01", UTHBuildPacketTest01);
    UtRegisterTest("UTHBuildPacketTest02", UTHBuildPacketTest02);
    UtRegisterTest("UTHBuildPacketSrcDstTest01", UTHBuildPacketSrcDstTest01);
    UtRegisterTest("UTHBuildPacketSrcDstTest02", UTHBuildPacketSrcDstTest02);
    UtRegisterTest("UTHBuildPacketSrcDstPortsTest01",
                   UTHBuildPacketSrcDstPortsTest01);
    UtRegisterTest("UTHBuildPacketSrcDstPortsTest02",
                   UTHBuildPacketSrcDstPortsTest02);
    UtRegisterTest("UTHBuildPacketOfFlowsTest01", UTHBuildPacketOfFlowsTest01);

#endif /* UNITTESTS */
}

