/* 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
 * 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>
 *
 * Stream engine TCP SACK handling.
 */

#include "suricata-common.h"
#include "stream-tcp.h"
#include "stream-tcp-private.h"
#include "stream-tcp-sack.h"
#include "util-unittest.h"

#ifdef DEBUG
void StreamTcpSackPrintList(TcpStream *stream)
{
    StreamTcpSackRecord *rec = stream->sack_head;
    for (; rec != NULL; rec = rec->next) {
        SCLogDebug("record %8u - %8u", rec->le, rec->re);
    }
}
#endif /* DEBUG */

static StreamTcpSackRecord *StreamTcpSackRecordAlloc(void)
{
    if (StreamTcpCheckMemcap((uint32_t)sizeof(StreamTcpSackRecord)) == 0)
        return NULL;

    StreamTcpSackRecord *rec = SCMalloc(sizeof(*rec));
    if (unlikely(rec == NULL))
        return NULL;

    StreamTcpIncrMemuse((uint64_t)sizeof(*rec));
    return rec;
}

static void StreamTcpSackRecordFree(StreamTcpSackRecord *rec)
{
    SCFree(rec);
    StreamTcpDecrMemuse((uint64_t)sizeof(*rec));
}

/**
 *  \brief insert a SACK range
 *
 *  \param le left edge in host order
 *  \param re right edge in host order
 *
 *  \retval 0 all is good
 *  \retval -1 error
 */
static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re)
{
    SCLogDebug("le %u, re %u", le, re);
#ifdef DEBUG
    StreamTcpSackPrintList(stream);
#endif

    /* if to the left of last_ack then ignore */
    if (SEQ_LT(re, stream->last_ack)) {
        SCLogDebug("too far left. discarding");
        goto end;
    }
    /* if to the right of the tcp window then ignore */
    if (SEQ_GT(le, (stream->last_ack + stream->window))) {
        SCLogDebug("too far right. discarding");
        goto end;
    }
    if (stream->sack_head != NULL) {
        StreamTcpSackRecord *rec;

        for (rec = stream->sack_head; rec != NULL; rec = rec->next) {
            SCLogDebug("rec %p, le %u, re %u", rec, rec->le, rec->re);

            if (SEQ_LT(le, rec->le)) {
                SCLogDebug("SEQ_LT(le, rec->le)");
                if (SEQ_LT(re, rec->le)) {
                    SCLogDebug("SEQ_LT(re, rec->le)");
                    // entirely before, prepend
                    StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
                    if (unlikely(stsr == NULL)) {
                        SCReturnInt(-1);
                    }
                    stsr->le = le;
                    stsr->re = re;

                    stsr->next = stream->sack_head;
                    stream->sack_head = stsr;
                    goto end;
                } else if (SEQ_EQ(re, rec->le)) {
                    SCLogDebug("SEQ_EQ(re, rec->le)");
                    // starts before, ends on rec->le, expand
                    rec->le = le;
                } else if (SEQ_GT(re, rec->le)) {
                    SCLogDebug("SEQ_GT(re, rec->le)");
                    // starts before, ends beyond rec->le
                    if (SEQ_LEQ(re, rec->re)) {
                        SCLogDebug("SEQ_LEQ(re, rec->re)");
                        // ends before rec->re, expand
                        rec->le = le;
                    } else { // implied if (re > rec->re)
                        SCLogDebug("implied if (re > rec->re), le set to %u", rec->re);
                        le = rec->re;
                        continue;
                    }
                }
            } else if (SEQ_EQ(le, rec->le)) {
                SCLogDebug("SEQ_EQ(le, rec->le)");
                if (SEQ_LEQ(re, rec->re)) {
                    SCLogDebug("SEQ_LEQ(re, rec->re)");
                    // new record fully overlapped
                    SCReturnInt(0);
                } else { // implied re > rec->re
                    SCLogDebug("implied re > rec->re");
                    if (rec->next != NULL) {
                        if (SEQ_LEQ(re, rec->next->le)) {
                            rec->re = re;
                            goto end;
                        } else {
                            rec->re = rec->next->le;
                            le = rec->next->le;
                            SCLogDebug("le is now %u", le);
                            continue;
                        }
                    } else {
                        rec->re = re;
                        goto end;
                    }
                }
            } else { // implied (le > rec->le)
                SCLogDebug("implied (le > rec->le)");
                if (SEQ_LT(le, rec->re)) {
                    SCLogDebug("SEQ_LT(le, rec->re))");
                    // new record fully overlapped
                    if (SEQ_GT(re, rec->re)) {
                        SCLogDebug("SEQ_GT(re, rec->re)");

                        if (rec->next != NULL) {
                            if (SEQ_LEQ(re, rec->next->le)) {
                                rec->re = re;
                                goto end;
                            } else {
                                rec->re = rec->next->le;
                                le = rec->next->le;
                                continue;
                            }
                        } else {
                            rec->re = re;
                            goto end;
                        }
                    }

                    SCLogDebug("new range fully overlapped");
                    SCReturnInt(0);
                } else if (SEQ_EQ(le, rec->re)) {
                    SCLogDebug("here");
                    // new record fully overlapped
                    //int r = StreamTcpSackInsertRange(stream, rec->re+1, re);
                    //SCReturnInt(r);
                    le = rec->re;
                    continue;
                } else { /* implied le > rec->re */
                    SCLogDebug("implied le > rec->re");
                    if (rec->next == NULL) {
                        SCLogDebug("rec->next == NULL");
                        StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
                        if (unlikely(stsr == NULL)) {
                            SCReturnInt(-1);
                        }
                        stsr->le = le;
                        stsr->re = re;
                        stsr->next = NULL;

                        stream->sack_tail->next = stsr;
                        stream->sack_tail = stsr;
                        goto end;
                    } else {
                        SCLogDebug("implied rec->next != NULL");
                        if (SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)) {
                            SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)");
                            StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
                            if (unlikely(stsr == NULL)) {
                                SCReturnInt(-1);
                            }
                            stsr->le = le;
                            stsr->re = re;
                            stsr->next = rec->next;
                            rec->next = stsr;

                        } else if (SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)) {
                            SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)");
                            StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
                            if (unlikely(stsr == NULL)) {
                                SCReturnInt(-1);
                            }
                            stsr->le = le;
                            stsr->re = rec->next->le;
                            stsr->next = rec->next;
                            rec->next = stsr;

                            le = rec->next->le;
                        }
                    }
                }
            }
        }
    } else {
        SCLogDebug("implied empty list");
        StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc();
        if (unlikely(stsr == NULL)) {
            SCReturnInt(-1);
        }
        stsr->le = le;
        stsr->re = re;
        stsr->next = NULL;

        stream->sack_head = stsr;
        stream->sack_tail = stsr;
    }

    StreamTcpSackPruneList(stream);
end:
    SCReturnInt(0);
}

/**
 *  \brief Update stream with SACK records from a TCP packet.
 *
 *  \param stream The stream to update.
 *  \param p packet to get the SACK records from
 *
 *  \retval -1 error
 *  \retval 0 ok
 */
int StreamTcpSackUpdatePacket(TcpStream *stream, Packet *p)
{
    int records = TCP_GET_SACK_CNT(p);
    int record = 0;

    TCPOptSackRecord *sack_rec = (TCPOptSackRecord *)(TCP_GET_SACK_PTR(p));

    for (record = 0; record < records; record++) {
        SCLogDebug("%p last_ack %u, left edge %u, right edge %u", sack_rec,
            stream->last_ack, ntohl(sack_rec->le), ntohl(sack_rec->re));

        if (SEQ_LEQ(ntohl(sack_rec->re), stream->last_ack)) {
            SCLogDebug("record before last_ack");
            goto next;
        }

        /** \todo need a metric to a check for a right edge limit */
/*
        if (SEQ_GT(ntohl(sack_rec->re), stream->next_seq)) {
            SCLogDebug("record beyond next_seq %u", stream->next_seq);
            goto next;
        }
*/
        if (SEQ_GEQ(ntohl(sack_rec->le), ntohl(sack_rec->re))) {
            SCLogDebug("invalid record: le >= re");
            goto next;
        }

        if (StreamTcpSackInsertRange(stream, ntohl(sack_rec->le),
                    ntohl(sack_rec->re)) == -1)
        {
            SCReturnInt(-1);
        }

    next:
        sack_rec++;
    }
#ifdef DEBUG
    StreamTcpSackPrintList(stream);
#endif
    SCReturnInt(0);
}

void StreamTcpSackPruneList(TcpStream *stream)
{
    SCEnter();

    StreamTcpSackRecord *rec = stream->sack_head;

    while (rec != NULL) {
        if (SEQ_LT(rec->re, stream->last_ack)) {
            SCLogDebug("removing le %u re %u", rec->le, rec->re);

            if (rec->next != NULL) {
                stream->sack_head = rec->next;
                StreamTcpSackRecordFree(rec);
                rec = stream->sack_head;
                continue;
            } else {
                stream->sack_head = NULL;
                stream->sack_tail = NULL;
                StreamTcpSackRecordFree(rec);
                break;
            }
        } else if (SEQ_LT(rec->le, stream->last_ack)) {
            SCLogDebug("adjusting record to le %u re %u", rec->le, rec->re);
            /* last ack inside this record, update */
            rec->le = stream->last_ack;
            break;
        } else {
            SCLogDebug("record beyond last_ack, nothing to do. Bailing out.");
            break;
        }
    }
#ifdef DEBUG
    StreamTcpSackPrintList(stream);
#endif
    SCReturn;
}

/**
 *  \brief Free SACK list from a stream
 *
 *  \param stream Stream to cleanup
 */
void StreamTcpSackFreeList(TcpStream *stream)
{
    SCEnter();

    StreamTcpSackRecord *rec = stream->sack_head;
    StreamTcpSackRecord *next = NULL;

    while (rec != NULL) {
        next = rec->next;
        StreamTcpSackRecordFree(rec);
        rec = next;
    }

    stream->sack_head = NULL;
    stream->sack_tail = NULL;
    SCReturn;
}


#ifdef UNITTESTS

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest01 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 1, 10);
    StreamTcpSackInsertRange(&stream, 10, 20);
    StreamTcpSackInsertRange(&stream, 10, 20);
    StreamTcpSackInsertRange(&stream, 1, 20);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
        printf("list in weird state, head le %u, re %u: ",
                stream.sack_head->le, stream.sack_head->re);
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 19) {
        printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest02 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 10, 20);
    StreamTcpSackInsertRange(&stream, 1, 20);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 1 || stream.sack_head->re != 20) {
        printf("list in weird state, head le %u, re %u: ",
                stream.sack_head->le, stream.sack_head->re);
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 19) {
        printf("size should be 19, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest03 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 10, 20);
    StreamTcpSackInsertRange(&stream,  5, 15);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */
    StreamTcpSackInsertRange(&stream, 15, 25);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 5) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 20) {
        printf("size should be 20, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest04 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  20);
    StreamTcpSackInsertRange(&stream, 30, 50);
    StreamTcpSackInsertRange(&stream, 10, 25);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 45) {
        printf("size should be 45, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest05 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  20);
    StreamTcpSackInsertRange(&stream, 30, 50);
    StreamTcpSackInsertRange(&stream, 10, 35);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 50) {
        printf("size should be 50, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest06 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  9);
    StreamTcpSackInsertRange(&stream, 11, 19);
    StreamTcpSackInsertRange(&stream, 21, 29);
    StreamTcpSackInsertRange(&stream, 31, 39);
    StreamTcpSackInsertRange(&stream, 0, 40);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest07 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  9);
    StreamTcpSackInsertRange(&stream, 11, 19);
    StreamTcpSackInsertRange(&stream, 21, 29);
    StreamTcpSackInsertRange(&stream, 31, 39);
    StreamTcpSackInsertRange(&stream, 0, 40);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 10;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 30) {
        printf("size should be 30, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest08 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  9);
    StreamTcpSackInsertRange(&stream, 11, 19);
    StreamTcpSackInsertRange(&stream, 21, 29);
    StreamTcpSackInsertRange(&stream, 31, 39);
    StreamTcpSackInsertRange(&stream, 0, 40);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 41;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 0) {
        printf("size should be 0, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest09 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 100;

    StreamTcpSackInsertRange(&stream, 0,  9);
    StreamTcpSackInsertRange(&stream, 11, 19);
    StreamTcpSackInsertRange(&stream, 21, 29);
    StreamTcpSackInsertRange(&stream, 31, 39);
    StreamTcpSackInsertRange(&stream, 0, 40);

#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 0) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 39;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 1) {
        printf("size should be 1, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest10 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 1000;

    StreamTcpSackInsertRange(&stream, 100, 119);
    StreamTcpSackInsertRange(&stream, 111, 119);
    StreamTcpSackInsertRange(&stream, 121, 129);
    StreamTcpSackInsertRange(&stream, 131, 139);
    StreamTcpSackInsertRange(&stream, 100, 140);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 100) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 99;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest11 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 1000;

    StreamTcpSackInsertRange(&stream, 100, 119);
    StreamTcpSackInsertRange(&stream, 111, 119);
    StreamTcpSackInsertRange(&stream, 121, 129);
    StreamTcpSackInsertRange(&stream, 131, 139);
    StreamTcpSackInsertRange(&stream, 101, 140);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 100) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 99;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 40) {
        printf("size should be 40, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the pruning of SACK ranges.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest12 (void)
{
    TcpStream stream;
    int retval = 0;

    memset(&stream, 0, sizeof(stream));
    stream.window = 2000;

    StreamTcpSackInsertRange(&stream, 800, 1000);
    StreamTcpSackInsertRange(&stream, 700, 900);
    StreamTcpSackInsertRange(&stream, 600, 800);
    StreamTcpSackInsertRange(&stream, 500, 700);
    StreamTcpSackInsertRange(&stream, 100, 600);
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (stream.sack_head->le != 100) {
        goto end;
    }

    if (StreamTcpSackedSize(&stream) != 900) {
        printf("size should be 900, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    StreamTcpSackInsertRange(&stream, 0, 1000);

    if (StreamTcpSackedSize(&stream) != 1000) {
        printf("size should be 1000, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    stream.last_ack = 500;

    StreamTcpSackPruneList(&stream);

    if (StreamTcpSackedSize(&stream) != 500) {
        printf("size should be 500, is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion on out of window condition.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest13 (void) {
    TcpStream stream;
    int retval = 0;
    int i;

    memset(&stream, 0, sizeof(stream));
    stream.last_ack = 10000;
    stream.window = 2000;

    for (i = 0; i < 10; i++) {
        StreamTcpSackInsertRange(&stream, 100+(20*i), 110+(20*i));
    }
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (StreamTcpSackedSize(&stream) != 0) {
        printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

/**
 *  \test   Test the insertion of out of window condition.
 *
 *  \retval On success it returns 1 and on failure 0.
 */

static int StreamTcpSackTest14 (void) {
    TcpStream stream;
    int retval = 0;
    int i;

    memset(&stream, 0, sizeof(stream));
    stream.last_ack = 1000;
    stream.window = 2000;

    for (i = 0; i < 10; i++) {
        StreamTcpSackInsertRange(&stream, 4000+(20*i), 4010+(20*i));
    }
#ifdef DEBUG
    StreamTcpSackPrintList(&stream);
#endif /* DEBUG */

    if (StreamTcpSackedSize(&stream) != 0) {
        printf("Sacked size is %u: ", StreamTcpSackedSize(&stream));
        goto end;
    }

    retval = 1;
end:
    SCReturnInt(retval);
}

#endif /* UNITTESTS */

void StreamTcpSackRegisterTests (void)
{
#ifdef UNITTESTS
    UtRegisterTest("StreamTcpSackTest01 -- Insertion",
                    StreamTcpSackTest01, 1);
    UtRegisterTest("StreamTcpSackTest02 -- Insertion",
                    StreamTcpSackTest02, 1);
    UtRegisterTest("StreamTcpSackTest03 -- Insertion",
                    StreamTcpSackTest03, 1);
    UtRegisterTest("StreamTcpSackTest04 -- Insertion",
                    StreamTcpSackTest04, 1);
    UtRegisterTest("StreamTcpSackTest05 -- Insertion",
                    StreamTcpSackTest05, 1);
    UtRegisterTest("StreamTcpSackTest06 -- Insertion",
                    StreamTcpSackTest06, 1);
    UtRegisterTest("StreamTcpSackTest07 -- Pruning",
                    StreamTcpSackTest07, 1);
    UtRegisterTest("StreamTcpSackTest08 -- Pruning",
                    StreamTcpSackTest08, 1);
    UtRegisterTest("StreamTcpSackTest09 -- Pruning",
                    StreamTcpSackTest09, 1);
    UtRegisterTest("StreamTcpSackTest10 -- Pruning",
                    StreamTcpSackTest10, 1);
    UtRegisterTest("StreamTcpSackTest11 -- Insertion && Pruning",
                    StreamTcpSackTest11, 1);
    UtRegisterTest("StreamTcpSackTest12 -- Insertion && Pruning",
                    StreamTcpSackTest12, 1);
    UtRegisterTest("StreamTcpSackTest13 -- Insertion out of window",
                    StreamTcpSackTest13, 1);
    UtRegisterTest("StreamTcpSackTest14 -- Insertion out of window",
                    StreamTcpSackTest14, 1);
#endif
}
